Esempio n. 1
0
extern "C" HRESULT ExeEngineExecutePackage(
    __in BURN_EXECUTE_ACTION* pExecuteAction,
    __in BURN_VARIABLES* pVariables,
    __in BOOL fRollback,
    __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
    __in LPVOID pvContext,
    __out BOOTSTRAPPER_APPLY_RESTART* pRestart
    )
{
    HRESULT hr = S_OK;
    WCHAR wzCurrentDirectory[MAX_PATH] = { };
    BOOL fChangedCurrentDirectory = FALSE;
    int nResult = IDNOACTION;
    LPCWSTR wzArguments = NULL;
    LPWSTR sczArgumentsFormatted = NULL;
    LPWSTR sczArgumentsObfuscated = NULL;
    LPWSTR sczCachedDirectory = NULL;
    LPWSTR sczExecutablePath = NULL;
    LPWSTR sczCommand = NULL;
    LPWSTR sczCommandObfuscated = NULL;
    STARTUPINFOW si = { };
    PROCESS_INFORMATION pi = { };
    DWORD dwExitCode = 0;
    GENERIC_EXECUTE_MESSAGE message = { };

    // get cached executable path
    hr = CacheGetCompletedPath(pExecuteAction->exePackage.pPackage->fPerMachine, pExecuteAction->exePackage.pPackage->sczCacheId, &sczCachedDirectory);
    ExitOnFailure1(hr, "Failed to get cached path for package: %ls", pExecuteAction->exePackage.pPackage->sczId);

    // Best effort to set the execute package cache folder variable.
    VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE);

    hr = PathConcat(sczCachedDirectory, pExecuteAction->exePackage.pPackage->rgPayloads[0].pPayload->sczFilePath, &sczExecutablePath);
    ExitOnFailure(hr, "Failed to build executable path.");

    // pick arguments
    switch (pExecuteAction->exePackage.action)
    {
    case BOOTSTRAPPER_ACTION_STATE_INSTALL:
        wzArguments = pExecuteAction->exePackage.pPackage->Exe.sczInstallArguments;
        break;

    case BOOTSTRAPPER_ACTION_STATE_UNINSTALL:
        wzArguments = pExecuteAction->exePackage.pPackage->Exe.sczUninstallArguments;
        break;

    case BOOTSTRAPPER_ACTION_STATE_REPAIR:
        wzArguments = pExecuteAction->exePackage.pPackage->Exe.sczRepairArguments;
        break;

    default:
        hr = E_UNEXPECTED;
        ExitOnFailure(hr, "Failed to get action arguments for executable package.");
    }

    // build command
    if (wzArguments && *wzArguments)
    {
        hr = VariableFormatString(pVariables, wzArguments, &sczArgumentsFormatted, NULL);
        ExitOnFailure(hr, "Failed to format argument string.");

        hr = StrAllocFormattedSecure(&sczCommand, L"\"%ls\" %s", sczExecutablePath, sczArgumentsFormatted);
        ExitOnFailure(hr, "Failed to create executable command.");

        hr = VariableFormatStringObfuscated(pVariables, wzArguments, &sczArgumentsObfuscated, NULL);
        ExitOnFailure(hr, "Failed to format obfuscated argument string.");

        hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\" %s", sczExecutablePath, sczArgumentsObfuscated);
    }
    else
    {
        hr = StrAllocFormatted(&sczCommand, L"\"%ls\"", sczExecutablePath);
        ExitOnFailure(hr, "Failed to create executable command.");

        hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\"", sczExecutablePath);
    }
    ExitOnFailure(hr, "Failed to create obfuscated executable command.");

    if (BURN_EXE_PROTOCOL_TYPE_BURN == pExecuteAction->exePackage.pPackage->Exe.protocol)
    {
        // Add the list of dependencies to ignore, if any, to the burn command line.
        if (pExecuteAction->exePackage.sczIgnoreDependencies && BURN_EXE_PROTOCOL_TYPE_BURN == pExecuteAction->exePackage.pPackage->Exe.protocol)
        {
            hr = StrAllocFormattedSecure(&sczCommand, L"%ls -%ls=%ls", sczCommand, BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, pExecuteAction->exePackage.sczIgnoreDependencies);
            ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the command line.");

            hr = StrAllocFormatted(&sczCommandObfuscated, L"%ls -%ls=%ls", sczCommandObfuscated, BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, pExecuteAction->exePackage.sczIgnoreDependencies);
            ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the obfuscated command line.");
        }

        // Add the list of ancestors, if any, to the burn command line.
        if (pExecuteAction->exePackage.sczAncestors)
        {
            hr = StrAllocFormattedSecure(&sczCommand, L"%ls -%ls=%ls", sczCommand, BURN_COMMANDLINE_SWITCH_ANCESTORS, pExecuteAction->exePackage.sczAncestors);
            ExitOnFailure(hr, "Failed to append the list of ancestors to the command line.");

            hr = StrAllocFormatted(&sczCommandObfuscated, L"%ls -%ls=%ls", sczCommandObfuscated, BURN_COMMANDLINE_SWITCH_ANCESTORS, pExecuteAction->exePackage.sczAncestors);
            ExitOnFailure(hr, "Failed to append the list of ancestors to the obfuscated command line.");
        }
    }

    // Log before we add the secret pipe name and client token for embedded processes.
    LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pExecuteAction->exePackage.pPackage->sczId, LoggingActionStateToString(pExecuteAction->exePackage.action), sczExecutablePath, sczCommandObfuscated);

    if (!pExecuteAction->exePackage.fFireAndForget && BURN_EXE_PROTOCOL_TYPE_BURN == pExecuteAction->exePackage.pPackage->Exe.protocol)
    {
        hr = EmbeddedRunBundle(sczExecutablePath, sczCommand, pfnGenericMessageHandler, pvContext, &dwExitCode);
        ExitOnFailure1(hr, "Failed to run bundle as embedded from path: %ls", sczExecutablePath);
    }
    else if (!pExecuteAction->exePackage.fFireAndForget && BURN_EXE_PROTOCOL_TYPE_NETFX4 == pExecuteAction->exePackage.pPackage->Exe.protocol)
    {
        hr = NetFxRunChainer(sczExecutablePath, sczCommand, pfnGenericMessageHandler, pvContext, &dwExitCode);
        ExitOnFailure1(hr, "Failed to run netfx chainer: %ls", sczExecutablePath);
    }
    else // create and wait for the executable process while sending fake progress to allow cancel.
    {
        // Make the cache location of the executable the current directory to help those executables
        // that expect stuff to be relative to them.
        if (::GetCurrentDirectoryW(countof(wzCurrentDirectory), wzCurrentDirectory))
        {
            fChangedCurrentDirectory = ::SetCurrentDirectoryW(sczCachedDirectory);
        }

        si.cb = sizeof(si); // TODO: hookup the stdin/stdout/stderr pipes for logging purposes?
        if (!::CreateProcessW(sczExecutablePath, sczCommand, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
        {
            ExitWithLastError1(hr, "Failed to CreateProcess on path: %ls", sczExecutablePath);
        }

        if (pExecuteAction->exePackage.fFireAndForget)
        {
            ::WaitForInputIdle(pi.hProcess, 5000);
            ExitFunction();
        }

        do
        {
            message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS;
            message.dwAllowedResults = MB_OKCANCEL;
            message.progress.dwPercentage = 50;
            nResult = pfnGenericMessageHandler(&message, pvContext);
            hr = (IDOK == nResult || IDNOACTION == nResult) ? S_OK : IDCANCEL == nResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE);
            ExitOnRootFailure(hr, "Bootstrapper application aborted during EXE progress.");

            hr = ProcWaitForCompletion(pi.hProcess, 500, &dwExitCode);
            if (HRESULT_FROM_WIN32(WAIT_TIMEOUT) != hr)
            {
                ExitOnFailure1(hr, "Failed to wait for executable to complete: %ls", sczExecutablePath);
            }
        } while (HRESULT_FROM_WIN32(WAIT_TIMEOUT) == hr);
    }

    hr = HandleExitCode(pExecuteAction->exePackage.pPackage, dwExitCode, pRestart);
    ExitOnRootFailure1(hr, "Process returned error: 0x%x", dwExitCode);

LExit:
    if (fChangedCurrentDirectory)
    {
        ::SetCurrentDirectoryW(wzCurrentDirectory);
    }

    StrSecureZeroFreeString(sczArgumentsFormatted);
    ReleaseStr(sczArgumentsObfuscated);
    ReleaseStr(sczCachedDirectory);
    ReleaseStr(sczExecutablePath);
    StrSecureZeroFreeString(sczCommand);
    ReleaseStr(sczCommandObfuscated);

    ReleaseHandle(pi.hThread);
    ReleaseHandle(pi.hProcess);

    // Best effort to clear the execute package cache folder variable.
    VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE);

    return hr;
}
Esempio n. 2
0
extern "C" HRESULT MspEngineExecutePackage(
    __in_opt HWND hwndParent,
    __in BURN_EXECUTE_ACTION* pExecuteAction,
    __in BURN_VARIABLES* pVariables,
    __in BOOL fRollback,
    __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler,
    __in LPVOID pvContext,
    __out BOOTSTRAPPER_APPLY_RESTART* pRestart
    )
{
    HRESULT hr = S_OK;
    INSTALLUILEVEL uiLevel = pExecuteAction->mspTarget.pPackage->Msp.fDisplayInternalUI ? INSTALLUILEVEL_DEFAULT : static_cast<INSTALLUILEVEL>(INSTALLUILEVEL_NONE | INSTALLUILEVEL_SOURCERESONLY);
    WIU_MSI_EXECUTE_CONTEXT context = { };
    WIU_RESTART restart = WIU_RESTART_NONE;

    LPWSTR sczCachedDirectory = NULL;
    LPWSTR sczMspPath = NULL;
    LPWSTR sczPatches = NULL;
    LPWSTR sczProperties = NULL;
    LPWSTR sczObfuscatedProperties = NULL;

    // default to "verbose" logging
    DWORD dwLogMode = WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE;

    // get cached MSP paths
    for (DWORD i = 0; i < pExecuteAction->mspTarget.cOrderedPatches; ++i)
    {
        LPCWSTR wzAppend = NULL;
        BURN_PACKAGE* pMspPackage = pExecuteAction->mspTarget.rgOrderedPatches[i].pPackage;
        AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Invalid package type added to ordered patches.");

        if (BOOTSTRAPPER_ACTION_STATE_INSTALL == pExecuteAction->mspTarget.action)
        {
            hr = CacheGetCompletedPath(pMspPackage->fPerMachine, pMspPackage->sczCacheId, &sczCachedDirectory);
            ExitOnFailure(hr, "Failed to get cached path for MSP package: %ls", pMspPackage->sczId);

            // Best effort to set the execute package cache folder variable.
            VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE);

            hr = PathConcat(sczCachedDirectory, pMspPackage->rgPayloads[0].pPayload->sczFilePath, &sczMspPath);
            ExitOnFailure(hr, "Failed to build MSP path.");

            wzAppend = sczMspPath;
        }
        else // uninstall
        {
            wzAppend = pMspPackage->Msp.sczPatchCode;
        }

        if (NULL != sczPatches)
        {
            hr = StrAllocConcat(&sczPatches, L";", 0);
            ExitOnFailure(hr, "Failed to semi-colon delimit patches.");
        }

        hr = StrAllocConcat(&sczPatches, wzAppend, 0);
        ExitOnFailure(hr, "Failed to append patch.");
    }

    // Wire up the external UI handler and logging.
    hr = WiuInitializeExternalUI(pfnMessageHandler, uiLevel, hwndParent, pvContext, fRollback, &context);
    ExitOnFailure(hr, "Failed to initialize external UI handler.");

    //if (BURN_LOGGING_LEVEL_DEBUG == logLevel)
    //{
    //    dwLogMode | INSTALLLOGMODE_EXTRADEBUG;
    //}

    if (pExecuteAction->mspTarget.sczLogPath && *pExecuteAction->mspTarget.sczLogPath)
    {
        hr = WiuEnableLog(dwLogMode, pExecuteAction->mspTarget.sczLogPath, 0);
        ExitOnFailure(hr, "Failed to enable logging for package: %ls to: %ls", pExecuteAction->mspTarget.pPackage->sczId, pExecuteAction->mspTarget.sczLogPath);
    }

    // set up properties
    hr = MsiEngineConcatProperties(pExecuteAction->mspTarget.pPackage->Msp.rgProperties, pExecuteAction->mspTarget.pPackage->Msp.cProperties, pVariables, fRollback, &sczProperties, FALSE);
    ExitOnFailure(hr, "Failed to add properties to argument string.");

    hr = MsiEngineConcatProperties(pExecuteAction->mspTarget.pPackage->Msp.rgProperties, pExecuteAction->mspTarget.pPackage->Msp.cProperties, pVariables, fRollback, &sczObfuscatedProperties, TRUE);
    ExitOnFailure(hr, "Failed to add properties to obfuscated argument string.");

    LogId(REPORT_STANDARD, MSG_APPLYING_PATCH_PACKAGE, pExecuteAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pExecuteAction->mspTarget.action), sczPatches, sczObfuscatedProperties, pExecuteAction->mspTarget.sczTargetProductCode);

    //
    // Do the actual action.
    //
    switch (pExecuteAction->mspTarget.action)
    {
    case BOOTSTRAPPER_ACTION_STATE_INSTALL: __fallthrough;
    case BOOTSTRAPPER_ACTION_STATE_REPAIR:
        hr = StrAllocConcatSecure(&sczProperties, L" PATCH=\"", 0);
        ExitOnFailure(hr, "Failed to add PATCH property on install.");

        hr = StrAllocConcatSecure(&sczProperties, sczPatches, 0);
        ExitOnFailure(hr, "Failed to add patches to PATCH property on install.");

        hr = StrAllocConcatSecure(&sczProperties, L"\" REBOOT=ReallySuppress", 0);
        ExitOnFailure(hr, "Failed to add reboot suppression property on install.");

        hr = WiuConfigureProductEx(pExecuteAction->mspTarget.sczTargetProductCode, INSTALLLEVEL_DEFAULT, INSTALLSTATE_DEFAULT, sczProperties, &restart);
        ExitOnFailure(hr, "Failed to install MSP package.");
        break;

    case BOOTSTRAPPER_ACTION_STATE_UNINSTALL:
        hr = StrAllocConcatSecure(&sczProperties, L" REBOOT=ReallySuppress", 0);
        ExitOnFailure(hr, "Failed to add reboot suppression property on uninstall.");

        // Ignore all dependencies, since the Burn engine already performed the check.
        hr = StrAllocFormattedSecure(&sczProperties, L"%ls %ls=ALL", sczProperties, DEPENDENCY_IGNOREDEPENDENCIES);
        ExitOnFailure(hr, "Failed to add the list of dependencies to ignore to the properties.");

        hr = WiuRemovePatches(sczPatches, pExecuteAction->mspTarget.sczTargetProductCode, sczProperties, &restart);
        ExitOnFailure(hr, "Failed to uninstall MSP package.");
        break;
    }

LExit:
    WiuUninitializeExternalUI(&context);

    ReleaseStr(sczCachedDirectory);
    ReleaseStr(sczMspPath);
    StrSecureZeroFreeString(sczProperties);
    ReleaseStr(sczObfuscatedProperties);
    ReleaseStr(sczPatches);

    switch (restart)
    {
        case WIU_RESTART_NONE:
            *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;
            break;

        case WIU_RESTART_REQUIRED:
            *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED;
            break;

        case WIU_RESTART_INITIATED:
            *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED;
            break;
    }

    // Best effort to clear the execute package cache folder variable.
    VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE);

    return hr;
}
Esempio n. 3
0
extern "C" HRESULT NetFxRunChainer(
    __in LPCWSTR wzExecutablePath,
    __in LPCWSTR wzArguments,
    __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler,
    __in LPVOID pvContext,
    __out DWORD* pdwExitCode
    )
{
    HRESULT hr = S_OK;
    DWORD er = 0;
    UUID guid = { };
    WCHAR wzGuid[39];
    RPC_STATUS rs = RPC_S_OK;
    LPWSTR sczEventName = NULL;
    LPWSTR sczSectionName = NULL;
    LPWSTR sczCommand = NULL;
    NetFxChainer* pNetfxChainer = NULL;
    STARTUPINFOW si = { };
    PROCESS_INFORMATION pi = { };
    HRESULT hrInternalError = 0;

    // Create the unique name suffix.
    rs = ::UuidCreate(&guid);
    hr = HRESULT_FROM_RPC(rs);
    ExitOnFailure(hr, "Failed to create netfx chainer guid.");

    if (!::StringFromGUID2(guid, wzGuid, countof(wzGuid)))
    {
        hr = E_OUTOFMEMORY;
        ExitOnRootFailure(hr, "Failed to convert netfx chainer guid into string.");
    }

    hr = StrAllocFormatted(&sczSectionName, L"NetFxSection.%ls", wzGuid);
    ExitOnFailure(hr, "Failed to allocate section name.");

    hr = StrAllocFormatted(&sczEventName, L"NetFxEvent.%ls", wzGuid);
    ExitOnFailure(hr, "Failed to allocate event name.");

    hr = CreateNetFxChainer(sczSectionName, sczEventName, &pNetfxChainer);
    ExitOnFailure(hr, "Failed to create netfx chainer.");

    hr = StrAllocFormattedSecure(&sczCommand, L"%ls /pipe %ls", wzArguments, sczSectionName);
    ExitOnFailure(hr, "Failed to allocate netfx chainer arguments.");

    si.cb = sizeof(si);
    if (!::CreateProcessW(wzExecutablePath, sczCommand, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
    {
        ExitWithLastError1(hr, "Failed to CreateProcess on path: %ls", wzExecutablePath);
    }

    HANDLE handles[2] = { pi.hProcess, pNetfxChainer->hEventChaineeSend };

    for (;;)
    {
        er = ::WaitForMultipleObjects(2, handles, FALSE, 100);
        if (WAIT_OBJECT_0 == er)
        {
            // Process has exited
            *pdwExitCode = NetFxGetResult(pNetfxChainer, &hrInternalError);
            if (E_PENDING == *pdwExitCode)
            {
                if (!::GetExitCodeProcess(pi.hProcess, pdwExitCode))
                {
                    ExitWithLastError(hr, "Failed to get netfx return code.");
                }
            }
            else if (FAILED(hrInternalError))
            {
                // push internal error message
                OnNetFxError(pNetfxChainer, hrInternalError, pfnGenericMessageHandler, pvContext);
                ExitOnFailure(hr, "Failed to send internal error message from netfx chainer.");
            }           

            break;
        }
        else if (WAIT_OBJECT_0 + 1 == er)
        {
            // Chainee has notified us of a change.
            hr = ProcessNetFxMessage(pNetfxChainer, pfnGenericMessageHandler, pvContext);
            ExitOnFailure(hr, "Failed to process netfx chainer message.");
        }
        else if (WAIT_FAILED == er)
        {
            ExitWithLastError(hr, "Failed to wait for netfx chainer process to complete");
        }
    }

LExit:
    ReleaseStr(sczSectionName);
    ReleaseStr(sczEventName);
    StrSecureZeroFreeString(sczCommand);
    DestroyNetFxChainer(pNetfxChainer);
    ReleaseHandle(pi.hThread);
    ReleaseHandle(pi.hProcess);

    return hr;
}
Esempio n. 4
0
extern "C" HRESULT ApprovedExesLaunch(
    __in BURN_VARIABLES* pVariables,
    __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe,
    __out DWORD* pdwProcessId
    )
{
    HRESULT hr = S_OK;
    LPWSTR sczArgumentsFormatted = NULL;
    LPWSTR sczArgumentsObfuscated = NULL;
    LPWSTR sczCommand = NULL;
    LPWSTR sczCommandObfuscated = NULL;
    LPWSTR sczExecutableDirectory = NULL;
    STARTUPINFOW si = { };
    PROCESS_INFORMATION pi = { };

    // build command
    if (pLaunchApprovedExe->sczArguments && *pLaunchApprovedExe->sczArguments)
    {
        hr = VariableFormatString(pVariables, pLaunchApprovedExe->sczArguments, &sczArgumentsFormatted, NULL);
        ExitOnFailure(hr, "Failed to format argument string.");

        hr = StrAllocFormattedSecure(&sczCommand, L"\"%ls\" %s", pLaunchApprovedExe->sczExecutablePath, sczArgumentsFormatted);
        ExitOnFailure(hr, "Failed to create executable command.");

        hr = VariableFormatStringObfuscated(pVariables, pLaunchApprovedExe->sczArguments, &sczArgumentsObfuscated, NULL);
        ExitOnFailure(hr, "Failed to format obfuscated argument string.");

        hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\" %s", pLaunchApprovedExe->sczExecutablePath, sczArgumentsObfuscated);
    }
    else
    {
        hr = StrAllocFormatted(&sczCommand, L"\"%ls\"", pLaunchApprovedExe->sczExecutablePath);
        ExitOnFailure(hr, "Failed to create executable command.");

        hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\"", pLaunchApprovedExe->sczExecutablePath);
    }
    ExitOnFailure(hr, "Failed to create obfuscated executable command.");

    // Try to get the directory of the executable so we can set the current directory of the process to help those executables
    // that expect stuff to be relative to them.  Best effort only.
    hr = PathGetDirectory(pLaunchApprovedExe->sczExecutablePath, &sczExecutableDirectory);
    if (FAILED(hr))
    {
        ReleaseNullStr(sczExecutableDirectory);
    }

    LogId(REPORT_STANDARD, MSG_LAUNCHING_APPROVED_EXE, pLaunchApprovedExe->sczExecutablePath, sczCommandObfuscated);

    si.cb = sizeof(si);
    if (!::CreateProcessW(pLaunchApprovedExe->sczExecutablePath, sczCommand, NULL, NULL, FALSE, CREATE_NEW_PROCESS_GROUP, NULL, sczExecutableDirectory, &si, &pi))
    {
        ExitWithLastError1(hr, "Failed to CreateProcess on path: %ls", pLaunchApprovedExe->sczExecutablePath);
    }

    *pdwProcessId = pi.dwProcessId;

    if (pLaunchApprovedExe->dwWaitForInputIdleTimeout)
    {
        ::WaitForInputIdle(pi.hProcess, pLaunchApprovedExe->dwWaitForInputIdleTimeout);
    }

LExit:
    StrSecureZeroFreeString(sczArgumentsFormatted);
    ReleaseStr(sczArgumentsObfuscated);
    StrSecureZeroFreeString(sczCommand);
    ReleaseStr(sczCommandObfuscated);
    ReleaseStr(sczExecutableDirectory);

    ReleaseHandle(pi.hThread);
    ReleaseHandle(pi.hProcess);

    return hr;
}