static HRESULT DoEnd( __in JSON_WRITER* pWriter, __in JSON_TOKEN tokenEnd, __in_z LPCWSTR wzEndString ) { HRESULT hr = S_OK; ::EnterCriticalSection(&pWriter->cs); if (!pWriter->rgTokenStack || 0 == pWriter->cTokens) { hr = E_UNEXPECTED; ExitOnRootFailure(hr, "Failure to pop token because the stack is empty."); } else { JSON_TOKEN token = pWriter->rgTokenStack[pWriter->cTokens - 1]; if ((JSON_TOKEN_ARRAY_END == tokenEnd && JSON_TOKEN_ARRAY_START != token && JSON_TOKEN_ARRAY_VALUE != token) || (JSON_TOKEN_OBJECT_END == tokenEnd && JSON_TOKEN_OBJECT_START != token && JSON_TOKEN_OBJECT_VALUE != token)) { hr = E_UNEXPECTED; ExitOnRootFailure1(hr, "Failure to pop token because the stack did not match the expected token: %d", tokenEnd); } } hr = StrAllocConcat(&pWriter->sczJson, wzEndString, 0); ExitOnFailure(hr, "Failed to end JSON array or object."); --pWriter->cTokens; LExit: ::LeaveCriticalSection(&pWriter->cs); return hr; }
static HRESULT ProcessEmbeddedMessages( __in BURN_PIPE_MESSAGE* pMsg, __in_opt LPVOID pvContext, __out DWORD* pdwResult ) { HRESULT hr = S_OK; BURN_EMBEDDED_CALLBACK_CONTEXT* pContext = static_cast<BURN_EMBEDDED_CALLBACK_CONTEXT*>(pvContext); DWORD dwResult = 0; // Process the message. switch (pMsg->dwMessage) { case BURN_EMBEDDED_MESSAGE_TYPE_ERROR: hr = OnEmbeddedErrorMessage(pContext->pfnGenericMessageHandler, pContext->pvContext, static_cast<BYTE*>(pMsg->pvData), pMsg->cbData, &dwResult); ExitOnFailure(hr, "Failed to process embedded error message."); break; case BURN_EMBEDDED_MESSAGE_TYPE_PROGRESS: hr = OnEmbeddedProgress(pContext->pfnGenericMessageHandler, pContext->pvContext, static_cast<BYTE*>(pMsg->pvData), pMsg->cbData, &dwResult); ExitOnFailure(hr, "Failed to process embedded progress message."); break; default: hr = E_INVALIDARG; ExitOnRootFailure1(hr, "Unexpected embedded message sent to child process, msg: %u", pMsg->dwMessage); } *pdwResult = dwResult; LExit: return hr; }
extern "C" HRESULT DependencyProcessDependentRegistration( __in const BURN_REGISTRATION* pRegistration, __in const BURN_DEPENDENT_REGISTRATION_ACTION* pAction ) { HRESULT hr = S_OK; switch (pAction->type) { case BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_REGISTER: hr = DepRegisterDependent(pRegistration->hkRoot, pRegistration->sczProviderKey, pAction->sczDependentProviderKey, NULL, NULL, 0); ExitOnFailure1(hr, "Failed to register dependent: %ls", pAction->sczDependentProviderKey); break; case BURN_DEPENDENT_REGISTRATION_ACTION_TYPE_UNREGISTER: hr = DepUnregisterDependent(pRegistration->hkRoot, pRegistration->sczProviderKey, pAction->sczDependentProviderKey); ExitOnFailure1(hr, "Failed to unregister dependent: %ls", pAction->sczDependentProviderKey); break; default: hr = E_INVALIDARG; ExitOnRootFailure1(hr, "Unrecognized registration action type: %d", pAction->type); } LExit: return hr; }
/******************************************************************* LoadSystemLibraryWithPath - Fully qualifies the path to a module in the Windows system directory and loads it and returns the path Returns E_MODNOTFOUND - The module could not be found. * - Another error occured. ********************************************************************/ extern "C" HRESULT DAPI LoadSystemLibraryWithPath( __in_z LPCWSTR wzModuleName, __out HMODULE *phModule, __deref_out_z_opt LPWSTR* psczPath ) { HRESULT hr = S_OK; DWORD cch = 0; WCHAR wzPath[MAX_PATH] = { }; cch = ::GetSystemDirectoryW(wzPath, MAX_PATH); ExitOnNullWithLastError(cch, hr, "Failed to get the Windows system directory."); if (L'\\' != wzPath[cch - 1]) { hr = ::StringCchCatNW(wzPath, MAX_PATH, L"\\", 1); ExitOnRootFailure(hr, "Failed to terminate the string with a backslash."); } hr = ::StringCchCatW(wzPath, MAX_PATH, wzModuleName); ExitOnRootFailure1(hr, "Failed to create the fully-qualified path to %ls.", wzModuleName); *phModule = ::LoadLibraryW(wzPath); ExitOnNullWithLastError1(*phModule, hr, "Failed to load the library %ls.", wzModuleName); if (psczPath) { hr = StrAllocString(psczPath, wzPath, MAX_PATH); ExitOnFailure(hr, "Failed to copy the path to library."); } LExit: return hr; }
/******************************************************************** ShelExecUnelevated() - executes a target unelevated. *******************************************************************/ extern "C" HRESULT DAPI ShelExecUnelevated( __in_z LPCWSTR wzTargetPath, __in_z_opt LPCWSTR wzParameters, __in_z_opt LPCWSTR wzVerb, __in_z_opt LPCWSTR wzWorkingDirectory, __in int nShowCmd ) { HRESULT hr = S_OK; BSTR bstrTargetPath = NULL; VARIANT vtParameters = { }; VARIANT vtVerb = { }; VARIANT vtWorkingDirectory = { }; VARIANT vtShow = { }; IShellView* psv = NULL; IShellDispatch2* psd = NULL; bstrTargetPath = ::SysAllocString(wzTargetPath); ExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate target path BSTR."); if (wzParameters && *wzParameters) { vtParameters.vt = VT_BSTR; vtParameters.bstrVal = ::SysAllocString(wzParameters); ExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate parameters BSTR."); } if (wzVerb && *wzVerb) { vtVerb.vt = VT_BSTR; vtVerb.bstrVal = ::SysAllocString(wzVerb); ExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate verb BSTR."); } if (wzWorkingDirectory && *wzWorkingDirectory) { vtWorkingDirectory.vt = VT_BSTR; vtWorkingDirectory.bstrVal = ::SysAllocString(wzWorkingDirectory); ExitOnNull(bstrTargetPath, hr, E_OUTOFMEMORY, "Failed to allocate working directory BSTR."); } vtShow.vt = VT_INT; vtShow.intVal = nShowCmd; hr = GetDesktopShellView(IID_PPV_ARGS(&psv)); ExitOnFailure(hr, "Failed to get desktop shell view."); hr = GetShellDispatchFromView(psv, IID_PPV_ARGS(&psd)); ExitOnFailure(hr, "Failed to get shell dispatch from view."); hr = psd->ShellExecute(bstrTargetPath, vtParameters, vtWorkingDirectory, vtVerb, vtShow); if (S_FALSE == hr) { hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); } ExitOnRootFailure1(hr, "Failed to launch unelevate executable: %ls", bstrTargetPath); LExit: ReleaseObject(psd); ReleaseObject(psv); ReleaseBSTR(vtWorkingDirectory.bstrVal); ReleaseBSTR(vtVerb.bstrVal); ReleaseBSTR(vtParameters.bstrVal); ReleaseBSTR(bstrTargetPath); return hr; }
/******************************************************************* PipeChildConnect - Called from the child process to connect back to the pipe provided by the parent process. *******************************************************************/ extern "C" HRESULT PipeChildConnect( __in BURN_PIPE_CONNECTION* pConnection, __in BOOL fConnectCachePipe ) { Assert(pConnection->sczName); Assert(pConnection->sczSecret); Assert(!pConnection->hProcess); Assert(INVALID_HANDLE_VALUE == pConnection->hPipe); Assert(INVALID_HANDLE_VALUE == pConnection->hCachePipe); HRESULT hr = S_OK; LPWSTR sczPipeName = NULL; // Try to connect to the parent. hr = StrAllocFormatted(&sczPipeName, PIPE_NAME_FORMAT_STRING, pConnection->sczName); ExitOnFailure(hr, "Failed to allocate name of parent pipe."); hr = E_UNEXPECTED; for (DWORD cRetry = 0; FAILED(hr) && cRetry < PIPE_RETRY_FOR_CONNECTION; ++cRetry) { pConnection->hPipe = ::CreateFileW(sczPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (INVALID_HANDLE_VALUE == pConnection->hPipe) { hr = HRESULT_FROM_WIN32(::GetLastError()); if (E_FILENOTFOUND == hr) // if the pipe isn't created, call it a timeout waiting on the parent. { hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT); } ::Sleep(PIPE_WAIT_FOR_CONNECTION); } else // we have a connection, go with it. { hr = S_OK; } } ExitOnRootFailure1(hr, "Failed to open parent pipe: %ls", sczPipeName) // Verify the parent and notify it that the child connected. hr = ChildPipeConnected(pConnection->hPipe, pConnection->sczSecret, &pConnection->dwProcessId); ExitOnFailure1(hr, "Failed to verify parent pipe: %ls", sczPipeName); if (fConnectCachePipe) { // Connect to the parent for the cache pipe. hr = StrAllocFormatted(&sczPipeName, CACHE_PIPE_NAME_FORMAT_STRING, pConnection->sczName); ExitOnFailure(hr, "Failed to allocate name of parent cache pipe."); pConnection->hCachePipe = ::CreateFileW(sczPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (INVALID_HANDLE_VALUE == pConnection->hCachePipe) { ExitWithLastError1(hr, "Failed to open parent pipe: %ls", sczPipeName) } // Verify the parent and notify it that the child connected. hr = ChildPipeConnected(pConnection->hCachePipe, pConnection->sczSecret, &pConnection->dwProcessId); ExitOnFailure1(hr, "Failed to verify parent pipe: %ls", sczPipeName); } pConnection->hProcess = ::OpenProcess(SYNCHRONIZE, FALSE, pConnection->dwProcessId); ExitOnNullWithLastError1(pConnection->hProcess, hr, "Failed to open companion process with PID: %u", pConnection->dwProcessId); LExit: ReleaseStr(sczPipeName); return hr; }
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; }
// // PlanCalculate - calculates the execute and rollback state for the requested package state. // extern "C" HRESULT ExeEnginePlanCalculatePackage( __in BURN_PACKAGE* pPackage ) { HRESULT hr = S_OK; //BOOL fCondition = FALSE; //BOOTSTRAPPER_PACKAGE_STATE expected = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN; BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE; BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE; //// evaluate rollback install condition //if (pPackage->sczRollbackInstallCondition) //{ // hr = ConditionEvaluate(pVariables, pPackage->sczRollbackInstallCondition, &fCondition); // ExitOnFailure(hr, "Failed to evaluate rollback install condition."); // expected = fCondition ? BOOTSTRAPPER_PACKAGE_STATE_PRESENT : BOOTSTRAPPER_PACKAGE_STATE_ABSENT; //} // execute action switch (pPackage->currentState) { case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: switch (pPackage->requested) { case BOOTSTRAPPER_REQUEST_STATE_PRESENT: execute = pPackage->Exe.fPseudoBundle ? BOOTSTRAPPER_ACTION_STATE_INSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; break; case BOOTSTRAPPER_REQUEST_STATE_REPAIR: execute = pPackage->Exe.fRepairable ? BOOTSTRAPPER_ACTION_STATE_REPAIR : BOOTSTRAPPER_ACTION_STATE_NONE; break; case BOOTSTRAPPER_REQUEST_STATE_ABSENT: execute = pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; break; case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL; break; default: execute = BOOTSTRAPPER_ACTION_STATE_NONE; } break; case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: switch (pPackage->requested) { case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; case BOOTSTRAPPER_REQUEST_STATE_REPAIR: execute = BOOTSTRAPPER_ACTION_STATE_INSTALL; break; case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough; case BOOTSTRAPPER_REQUEST_STATE_ABSENT: execute = BOOTSTRAPPER_ACTION_STATE_NONE; break; default: execute = BOOTSTRAPPER_ACTION_STATE_NONE; } break; default: hr = E_INVALIDARG; ExitOnRootFailure1(hr, "Invalid package current state: %d.", pPackage->currentState); } // Calculate the rollback action if there is an execute action. if (BOOTSTRAPPER_ACTION_STATE_NONE != execute) { switch (BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN != pPackage->expected ? pPackage->expected : pPackage->currentState) { case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: switch (pPackage->requested) { case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; case BOOTSTRAPPER_REQUEST_STATE_REPAIR: rollback = BOOTSTRAPPER_ACTION_STATE_NONE; break; case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough; case BOOTSTRAPPER_REQUEST_STATE_ABSENT: rollback = BOOTSTRAPPER_ACTION_STATE_INSTALL; break; default: rollback = BOOTSTRAPPER_ACTION_STATE_NONE; break; } break; case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: switch (pPackage->requested) { case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; case BOOTSTRAPPER_REQUEST_STATE_REPAIR: rollback = pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; break; case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough; case BOOTSTRAPPER_REQUEST_STATE_ABSENT: rollback = BOOTSTRAPPER_ACTION_STATE_NONE; break; default: rollback = BOOTSTRAPPER_ACTION_STATE_NONE; break; } break; default: hr = E_INVALIDARG; ExitOnRootFailure(hr, "Invalid package expected state."); } } // return values pPackage->execute = execute; pPackage->rollback = rollback; LExit: return hr; }
// Searches subdirectories of the given path for the highest version of ngen.exe available static HRESULT GetNgenVersion( __in LPWSTR pwzParentPath, __out LPWSTR* ppwzVersion ) { Assert(pwzParentPath); HRESULT hr = S_OK; DWORD dwError = 0; DWORD dwNgenFileFlags = 0; LPWSTR pwzVersionSearch = NULL; LPWSTR pwzNgen = NULL; LPWSTR pwzTemp = NULL; LPWSTR pwzTempVersion = NULL; DWORD dwMaxMajorVersion = 0; // This stores the highest major version we've seen so far DWORD dwMaxMinorVersion = 0; // This stores the minor version of the highest major version we've seen so far DWORD dwMajorVersion = 0; // This stores the major version of the directory we're currently considering DWORD dwMinorVersion = 0; // This stores the minor version of the directory we're currently considering BOOL fFound = TRUE; WIN32_FIND_DATAW wfdVersionDirectories; HANDLE hFind = INVALID_HANDLE_VALUE; hr = StrAllocFormatted(&pwzVersionSearch, L"%s*", pwzParentPath); ExitOnFailure1(hr, "failed to create outer directory search string from string %ls", pwzParentPath); hFind = FindFirstFileW(pwzVersionSearch, &wfdVersionDirectories); if (hFind == INVALID_HANDLE_VALUE) { ExitWithLastError1(hr, "failed to call FindFirstFileW with string %ls", pwzVersionSearch); } while (fFound) { pwzTempVersion = (LPWSTR)&(wfdVersionDirectories.cFileName); // Explicitly exclude v1.1.4322, which isn't backwards compatible and is not supported if (wfdVersionDirectories.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if (0 != lstrcmpW(L"v1.1.4322", pwzTempVersion)) { // A potential candidate directory was found to run ngen from - let's make sure ngen actually exists here hr = StrAllocFormatted(&pwzNgen, L"%s%s\\ngen.exe", pwzParentPath, pwzTempVersion); ExitOnFailure2(hr, "failed to create inner ngen search string with strings %ls and %ls", pwzParentPath, pwzTempVersion); // If Ngen.exe does exist as a file here, then let's check the file version if (FileExistsEx(pwzNgen, &dwNgenFileFlags) && (0 == (dwNgenFileFlags & FILE_ATTRIBUTE_DIRECTORY))) { hr = FileVersion(pwzNgen, &dwMajorVersion, &dwMinorVersion); if (FAILED(hr)) { WcaLog(LOGMSG_VERBOSE, "Failed to get version of %ls - continuing", pwzNgen); } else if (dwMajorVersion > dwMaxMajorVersion || (dwMajorVersion == dwMaxMajorVersion && dwMinorVersion > dwMaxMinorVersion)) { // If the version we found is the highest we've seen so far in this search, it will be our new best-so-far candidate hr = StrAllocString(ppwzVersion, pwzTempVersion, 0); ExitOnFailure1(hr, "failed to copy temp version string %ls to version string", pwzTempVersion); // Add one for the backslash after the directory name WcaLog(LOGMSG_VERBOSE, "Found highest-so-far version of ngen.exe (in directory %ls, version %u.%u.%u.%u)", *ppwzVersion, (DWORD)HIWORD(dwMajorVersion), (DWORD)LOWORD(dwMajorVersion), (DWORD)HIWORD(dwMinorVersion), (DWORD)LOWORD(dwMinorVersion)); dwMaxMajorVersion = dwMajorVersion; dwMaxMinorVersion = dwMinorVersion; } } else { WcaLog(LOGMSG_VERBOSE, "Ignoring %ls because it doesn't contain the file ngen.exe", pwzTempVersion); } } else { WcaLog(LOGMSG_VERBOSE, "Ignoring %ls because it is from .NET Framework v1.1, which is not backwards compatible with other versions of the Framework and thus is not supported by this custom action.", pwzTempVersion); } } else { WcaLog(LOGMSG_VERBOSE, "Ignoring %ls because it isn't a directory", pwzTempVersion); } fFound = FindNextFileW(hFind, &wfdVersionDirectories); if (!fFound) { dwError = ::GetLastError(); hr = (ERROR_NO_MORE_FILES == dwError) ? ERROR_SUCCESS : HRESULT_FROM_WIN32(dwError); ExitOnFailure1(hr, "Failed to call FindNextFileW() with query %ls", pwzVersionSearch); } } if (NULL == *ppwzVersion) { hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); ExitOnRootFailure1(hr, "Searched through all subdirectories of %ls, but failed to find any version of ngen.exe", pwzParentPath); } else { WcaLog(LOGMSG_VERBOSE, "Using highest version of ngen found, located in this subdirectory: %ls, version %u.%u.%u.%u", *ppwzVersion, (DWORD)HIWORD(dwMajorVersion), (DWORD)LOWORD(dwMajorVersion), (DWORD)HIWORD(dwMinorVersion), (DWORD)LOWORD(dwMinorVersion)); } LExit: if (hFind != INVALID_HANDLE_VALUE) { if (0 == FindClose(hFind)) { dwError = ::GetLastError(); hr = HRESULT_FROM_WIN32(dwError); WcaLog(LOGMSG_STANDARD, "Failed to close handle created by outer FindFirstFile with error %x - continuing", hr); } hFind = INVALID_HANDLE_VALUE; } ReleaseStr(pwzVersionSearch); ReleaseStr(pwzNgen); ReleaseStr(pwzTemp); // Purposely don't release pwzTempVersion, because it wasn't allocated in this function, it's just a pointer to a string inside wfdVersionDirectories return hr; }
extern "C" HRESULT DetectReportRelatedBundles( __in BURN_USER_EXPERIENCE* pUX, __in BURN_REGISTRATION* pRegistration, __in BOOTSTRAPPER_RELATION_TYPE relationType, __in BOOTSTRAPPER_ACTION action ) { HRESULT hr = S_OK; for (DWORD iRelatedBundle = 0; iRelatedBundle < pRegistration->relatedBundles.cRelatedBundles; ++iRelatedBundle) { const BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + iRelatedBundle; BOOTSTRAPPER_RELATED_OPERATION operation = BOOTSTRAPPER_RELATED_OPERATION_NONE; switch (pRelatedBundle->relationType) { case BOOTSTRAPPER_RELATION_UPGRADE: if (BOOTSTRAPPER_RELATION_UPGRADE != relationType && BOOTSTRAPPER_ACTION_UNINSTALL < action) { if (pRegistration->qwVersion > pRelatedBundle->qwVersion) { operation = BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE; } else if (pRegistration->qwVersion < pRelatedBundle->qwVersion) { operation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE; } } break; case BOOTSTRAPPER_RELATION_PATCH: __fallthrough; case BOOTSTRAPPER_RELATION_ADDON: if (BOOTSTRAPPER_ACTION_UNINSTALL == action) { operation = BOOTSTRAPPER_RELATED_OPERATION_REMOVE; } else if (BOOTSTRAPPER_ACTION_INSTALL == action || BOOTSTRAPPER_ACTION_MODIFY == action) { operation = BOOTSTRAPPER_RELATED_OPERATION_INSTALL; } else if (BOOTSTRAPPER_ACTION_REPAIR == action) { operation = BOOTSTRAPPER_RELATED_OPERATION_REPAIR; } break; case BOOTSTRAPPER_RELATION_DETECT: __fallthrough; case BOOTSTRAPPER_RELATION_DEPENDENT: break; default: hr = E_FAIL; ExitOnRootFailure1(hr, "Unexpected relation type encountered: %d", pRelatedBundle->relationType); break; } LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), LoggingVersionToString(pRelatedBundle->qwVersion), LoggingRelatedOperationToString(operation)); int nResult = pUX->pUserExperience->OnDetectRelatedBundle(pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->qwVersion, operation); hr = UserExperienceInterpretResult(pUX, MB_OKCANCEL, nResult); ExitOnRootFailure(hr, "BA aborted detect related bundle."); } LExit: return hr; }