static HRESULT GetUpdateInfoFileName( __in LPCWSTR wzApplicationId, __out LPWSTR* ppwzUpdateInfoPath ) { HRESULT hr = S_OK; WCHAR wzTempPath[MAX_PATH]; DWORD cchTempPath = countof(wzTempPath); cchTempPath = ::GetTempPathW(cchTempPath, wzTempPath); if (0 == cchTempPath) { ExitWithLastError(hr, "Failed to get temp path."); } else if (countof(wzTempPath) < cchTempPath) { hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); ExitOnFailure(hr, "Failed to get temp path."); } hr = StrAllocConcat(ppwzUpdateInfoPath, wzTempPath, 0); ExitOnFailure(hr, "Failed to allocate path to update info."); hr = StrAllocConcat(ppwzUpdateInfoPath, wzApplicationId, 0); ExitOnFailure(hr, "Failed to allocate path to update info."); LExit: return hr; }
// Gets the path to ngen.exe static HRESULT GetNgenPath( __out LPWSTR* ppwzNgenPath, __in BOOL f64BitFramework ) { Assert(ppwzNgenPath); HRESULT hr = S_OK; LPWSTR pwzVersion = NULL; LPWSTR pwzWindowsFolder = NULL; hr = WcaGetProperty(L"WindowsFolder", &pwzWindowsFolder); ExitOnFailure(hr, "failed to get WindowsFolder property"); hr = StrAllocString(ppwzNgenPath, pwzWindowsFolder, 0); ExitOnFailure1(hr, "failed to copy to NgenPath windows folder: %ls", pwzWindowsFolder); if (f64BitFramework) { WcaLog(LOGMSG_VERBOSE, "Searching for ngen under 64-bit framework path"); hr = StrAllocConcat(ppwzNgenPath, L"Microsoft.NET\\Framework64\\", 0); ExitOnFailure(hr, "failed to copy platform portion of ngen path"); } else { WcaLog(LOGMSG_VERBOSE, "Searching for ngen under 32-bit framework path"); hr = StrAllocConcat(ppwzNgenPath, L"Microsoft.NET\\Framework\\", 0); ExitOnFailure(hr, "failed to copy platform portion of ngen path"); } // We want to run the highest version of ngen possible, because they should be backwards compatible - so let's find the most appropriate directory now hr = GetNgenVersion(*ppwzNgenPath, &pwzVersion); ExitOnFailure1(hr, "failed to search for ngen under path %ls", *ppwzNgenPath); hr = StrAllocConcat(ppwzNgenPath, pwzVersion, 0); ExitOnFailure(hr, "failed to copy version portion of ngen path"); hr = StrAllocConcat(ppwzNgenPath, L"\\ngen.exe", 0); ExitOnFailure(hr, "failed to copy \"\\ngen.exe\" portion of ngen path"); LExit: ReleaseStr(pwzVersion); ReleaseStr(pwzWindowsFolder); return hr; }
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 DoKey( __in JSON_WRITER* pWriter, __in_z LPCWSTR wzKey ) { HRESULT hr = S_OK; JSON_TOKEN token = JSON_TOKEN_NONE; BOOL fNeedComma = FALSE; ::EnterCriticalSection(&pWriter->cs); hr = EnsureTokenStack(pWriter); ExitOnFailure(hr, "Failed to ensure token stack for key."); token = pWriter->rgTokenStack[pWriter->cTokens - 1]; switch (token) { case JSON_TOKEN_OBJECT_START: token = JSON_TOKEN_OBJECT_KEY; break; case JSON_TOKEN_OBJECT_VALUE: token = JSON_TOKEN_OBJECT_KEY; fNeedComma = TRUE; break; default: // everything else is not allowed. hr = E_UNEXPECTED; break; } ExitOnRootFailure(hr, "Cannot add key to JSON serializer now."); if (fNeedComma) { hr = StrAllocConcat(&pWriter->sczJson, L",", 0); ExitOnFailure(hr, "Failed to add comma for key to JSON."); } hr = StrAllocConcat(&pWriter->sczJson, wzKey, 0); ExitOnFailure(hr, "Failed to add key to JSON."); pWriter->rgTokenStack[pWriter->cTokens - 1] = token; LExit: ::LeaveCriticalSection(&pWriter->cs); return hr; }
static HRESULT OpenRequest( __in HINTERNET hConnect, __in_z_opt LPCWSTR wzMethod, __in INTERNET_SCHEME scheme, __in_z LPCWSTR wzResource, __in_z_opt LPCWSTR wzQueryString, __in_z_opt LPCWSTR wzHeader, __out HINTERNET* phUrl ) { HRESULT hr = S_OK; DWORD dwRequestFlags = INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_UI | INTERNET_FLAG_RELOAD; LPWSTR sczResource = NULL; HINTERNET hUrl = NULL; if (INTERNET_SCHEME_HTTPS == scheme) { dwRequestFlags |= INTERNET_FLAG_SECURE; } // Allocate the resource name. hr = StrAllocString(&sczResource, wzResource, 0); ExitOnFailure(hr, "Failed to allocate string for resource URI."); if (wzQueryString && *wzQueryString) { hr = StrAllocConcat(&sczResource, wzQueryString, 0); ExitOnFailure(hr, "Failed to append query strong to resource from URI."); } // Open the request and add the header if provided. hUrl = ::HttpOpenRequestW(hConnect, wzMethod, sczResource, NULL, NULL, BURN_DOWNLOAD_ENGINE_ACCEPT_TYPES, dwRequestFlags, NULL); ExitOnNullWithLastError(hUrl, hr, "Failed to open internet request."); if (wzHeader && *wzHeader) { if (!::HttpAddRequestHeadersW(hUrl, wzHeader, static_cast<DWORD>(-1), HTTP_ADDREQ_FLAG_COALESCE)) { ExitWithLastError(hr, "Failed to add header to HTTP request."); } } *phUrl = hUrl; hUrl = NULL; LExit: ReleaseInternet(hUrl); ReleaseStr(sczResource); return hr; }
HRESULT ProductGetLegacyManifestValueName( __in_z LPCWSTR wzProductName, __deref_out_z LPWSTR* psczManifestValueName ) { HRESULT hr = S_OK; hr = StrAllocString(psczManifestValueName, wzLegacyManifestValuePrefix, 0); ExitOnFailure(hr, "Failed to allocate legacy manifest value prefix"); hr = StrAllocConcat(psczManifestValueName, wzProductName, 0); ExitOnFailure(hr, "Failed to concat product name %ls to manifest value name", wzProductName); LExit: return hr; }
/****************************************************************** CaSchedSecureObjects - entry point for CaReadSecureObjects Custom Action called as Type 1 CustomAction (binary DLL) from Windows Installer in InstallExecuteSequence before CaSecureObjects ******************************************************************/ extern "C" UINT __stdcall SchedSecureObjects( __in MSIHANDLE hInstall ) { // AssertSz(FALSE, "debug SchedSecureObjects"); HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; LPWSTR pwzData = NULL; LPWSTR pwzTable = NULL; LPWSTR pwzTargetPath = NULL; LPWSTR pwzFormattedString = NULL; int iRoot = 0; int iAllUsers = 0; LPWSTR pwzKey = NULL; PMSIHANDLE hView = NULL; PMSIHANDLE hRec = NULL; MSIHANDLE hViewObject = NULL; // Don't free this since it's always a copy of either hViewService or hViewCreateFolder PMSIHANDLE hViewService = NULL; PMSIHANDLE hViewCreateFolder = NULL; PMSIHANDLE hViewFile = NULL; PMSIHANDLE hViewRegistry = NULL; PMSIHANDLE hRecObject = NULL; INSTALLSTATE isInstalled; INSTALLSTATE isAction; LPWSTR pwzCustomActionData = NULL; DWORD cchCustomActionData = 0; DWORD cObjects = 0; eOBJECTTYPE eType = OT_UNKNOWN; // // initialize // hr = WcaInitialize(hInstall, "SchedSecureObjects"); ExitOnFailure(hr, "failed to initialize"); // // loop through all the objects to be secured // hr = WcaOpenExecuteView(wzQUERY_SECUREOBJECTS, &hView); ExitOnFailure(hr, "failed to open view on SecureObjects table"); while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) { hViewObject = NULL; eType = OT_UNKNOWN; hr = WcaGetRecordString(hRec, QSO_TABLE, &pwzTable); ExitOnFailure(hr, "failed to get object table"); // ensure we're looking at a known table if (0 == lstrcmpW(L"ServiceInstall", pwzTable)) { eType = OT_SERVICE; } else if (0 == lstrcmpW(L"CreateFolder", pwzTable)) { eType = OT_FOLDER; } else if (0 == lstrcmpW(L"File", pwzTable)) { eType = OT_FILE; } else if (0 == lstrcmpW(L"Registry", pwzTable)) { eType = OT_REGISTRY; } else { ExitOnFailure1(hr = E_INVALIDARG, "unknown SecureObject.Table: %S", pwzTable); } // if we haven't opened a view on the ServiceInstall/CreateFolder table, do that now if (OT_SERVICE == eType) { if (!hViewService) { hr = WcaTableExists(pwzTable); if (S_FALSE == hr) hr = E_UNEXPECTED; ExitOnFailure1(hr, "failed to open %s table to secure object", pwzTable); hr = WcaOpenView(wzQUERY_SERVICECOMPONENT, &hViewService); ExitOnFailure(hr, "failed to open view on ServiceInstall table"); } hViewObject = hViewService; } else if (OT_FOLDER == eType) { if (!hViewCreateFolder) { hr = WcaTableExists(pwzTable); if (S_FALSE == hr) hr = E_UNEXPECTED; ExitOnFailure1(hr, "failed to open %s table to secure object", pwzTable); hr = WcaOpenView(wzQUERY_CREATEFOLDERCOMPONENT, &hViewCreateFolder); ExitOnFailure(hr, "failed to open view on CreateFolder table"); } hViewObject = hViewCreateFolder; } else if (OT_FILE== eType) { if (!hViewFile) { hr = WcaTableExists(pwzTable); if (S_FALSE == hr) hr = E_UNEXPECTED; ExitOnFailure1(hr, "failed to open %s table to secure object", pwzTable); hr = WcaOpenView(wzQUERY_FILECOMPONENT, &hViewFile); ExitOnFailure(hr, "failed to open view on CreateFolder table"); } hViewObject = hViewFile; } else if (OT_REGISTRY== eType) { if (!hViewRegistry) { hr = WcaTableExists(pwzTable); if (S_FALSE == hr) hr = E_UNEXPECTED; ExitOnFailure1(hr, "failed to open %s table to secure object", pwzTable); hr = WcaOpenView(wzQUERY_REGISTRYCOMPONENT, &hViewRegistry); ExitOnFailure(hr, "failed to open view on CreateFolder table"); } hViewObject = hViewRegistry; } Assert(hViewObject); // execute a view looking for the object's Component_ hr = WcaExecuteView(hViewObject, hRec); ExitOnFailure1(hr, "failed to execute view on %S table", pwzData); hr = WcaFetchSingleRecord(hViewObject, &hRecObject); ExitOnFailure(hr, "failed to fetch Component for secure object"); hr = WcaGetRecordString(hRecObject, QSOC_COMPONENT, &pwzData); ExitOnFailure(hr, "failed to get Component name for secure object"); // // if we are installing this Component // er = ::MsiGetComponentStateW(hInstall, pwzData, &isInstalled, &isAction); ExitOnFailure1(hr = HRESULT_FROM_WIN32(er), "failed to get install state for Component: %S", pwzData); if (WcaIsInstalling(isInstalled, isAction)) { // add the data to the CustomActionData hr = WcaGetRecordString(hRecObject, QSOC_OBJECTNAME, &pwzData); ExitOnFailure(hr, "failed to get name of object"); if (OT_SERVICE == eType) { hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); } else if (OT_FOLDER == eType) { hr = WcaGetTargetPath(pwzData, &pwzTargetPath); ExitOnFailure1(hr, "failed to get target path for directory id: %S", pwzData); hr = WcaWriteStringToCaData(pwzTargetPath, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); } else if (OT_FILE == eType) { hr = StrAllocFormatted(&pwzFormattedString, L"[#%s]", pwzData); ExitOnFailure1(hr, "failed to create formatted string for securing file object: %S", pwzData); hr = WcaGetFormattedString(pwzFormattedString, &pwzTargetPath); ExitOnFailure2(hr, "failed to get file path from formatted string: %S for secure object: %S", pwzFormattedString, pwzData); hr = WcaWriteStringToCaData(pwzTargetPath, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); } else if (OT_REGISTRY == eType) { hr = WcaGetRecordInteger(hRecObject, QSOC_REGROOT, &iRoot); ExitOnFailure1(hr, "Failed to get reg key root for secure object: %S", pwzData); hr = WcaGetRecordFormattedString(hRecObject, QSOC_REGKEY, &pwzKey); ExitOnFailure1(hr, "Failed to get reg key for secure object: %S", pwzData); // Decode the root value if (-1 == iRoot) { // They didn't specify a root so that means it's either HKCU or HKLM depending on ALLUSERS property hr = WcaGetIntProperty(L"ALLUSERS", &iAllUsers); ExitOnFailure(hr, "failed to get value of ALLUSERS property"); if (1 == iAllUsers) { hr = StrAllocString(&pwzTargetPath, L"MACHINE\\", 0); ExitOnFailure(hr, "failed to allocate target registry string with HKLM root"); } else { hr = StrAllocString(&pwzTargetPath, L"CURRENT_USER\\", 0); ExitOnFailure(hr, "failed to allocate target registry string with HKCU root"); } } else if (/*msidbRegistryRootClassesRoot*/ 0 == iRoot) { hr = StrAllocString(&pwzTargetPath, L"CLASSES_ROOT\\", 0); ExitOnFailure(hr, "failed to allocate target registry string with HKCR root"); } else if (/*msidbRegistryRootCurrentUser*/ 1 == iRoot) { hr = StrAllocString(&pwzTargetPath, L"CURRENT_USER\\", 0); ExitOnFailure(hr, "failed to allocate target registry string with HKCU root"); } else if (/*msidbRegistryRootLocalMachine*/ 2 == iRoot) { hr = StrAllocString(&pwzTargetPath, L"MACHINE\\", 0); ExitOnFailure(hr, "failed to allocate target registry string with HKLM root"); } else if (/*msidbRegistryRootUsers*/ 3 == iRoot) { hr = StrAllocString(&pwzTargetPath, L"USERS\\", 0); ExitOnFailure(hr, "failed to allocate target registry string with HKU root"); } else { ExitOnFailure2(hr = E_UNEXPECTED, "Unknown registry key root specified for secure object: '%S' root: %d", pwzData, iRoot); } hr = StrAllocConcat(&pwzTargetPath, pwzKey, 0); ExitOnFailure2(hr, "Failed to concat key: %S for secure object: %S", pwzKey, pwzData); hr = WcaWriteStringToCaData(pwzTargetPath, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); } else { AssertSz(FALSE, "How did you get here?"); } hr = WcaWriteStringToCaData(pwzTable, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaGetRecordFormattedString(hRec, QSO_DOMAIN, &pwzData); ExitOnFailure(hr, "failed to get domain for user to configure object"); hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaGetRecordFormattedString(hRec, QSO_USER, &pwzData); ExitOnFailure(hr, "failed to get user to configure object"); hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaGetRecordString(hRec, QSO_PERMISSION, &pwzData); ExitOnFailure(hr, "failed to get domain for user to configure object"); hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData); ExitOnFailure(hr, "failed to add data to CustomActionData"); cObjects++; } } // if we looped through all records all is well if (E_NOMOREITEMS == hr) hr = S_OK; ExitOnFailure(hr, "failed while looping through all objects to secure"); // // schedule the custom action and add to progress bar // if (pwzCustomActionData && *pwzCustomActionData) { Assert(0 < cObjects); hr = WcaDoDeferredAction(L"ExecSecureObjects", pwzCustomActionData, cObjects * COST_SECUREOBJECT); ExitOnFailure(hr, "failed to schedule ExecSecureObjects action"); } LExit: ReleaseStr(pwzCustomActionData); ReleaseStr(pwzData); ReleaseStr(pwzTable); ReleaseStr(pwzTargetPath); ReleaseStr(pwzFormattedString); ReleaseStr(pwzKey); if (FAILED(hr)) er = ERROR_INSTALL_FAILURE; return WcaFinalize(er); }
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; }
HRESULT ScaWriteWebError(IMSAdminBase* piMetabase, int iParentType, LPCWSTR wzRoot, SCA_WEB_ERROR* psweList) { // AssertSz(0, "Debug ScaWriteWebError here"); Assert(*wzRoot && psweList); HRESULT hr = S_OK; DWORD cchData = 0; LPWSTR pwzSearchKey = NULL; LPWSTR pwz = NULL; LPWSTR pwzErrors = NULL; LPWSTR pwzCodeSubCode = NULL; LPWSTR pwzAcceptableCodeSubCode = NULL; LPCWSTR wzFoundCodeSubCode = NULL; DWORD_PTR dwFoundCodeSubCodeIndex = 0xFFFFFFFF; BOOL fOldValueFound = FALSE; LPWSTR pwzAcceptableErrors = NULL; LPWSTR pwzNewError = NULL; METADATA_RECORD mr; ::ZeroMemory(&mr, sizeof(mr)); ExitOnNull(piMetabase, hr, E_INVALIDARG, "Failed to write web error, because no metabase was provided"); ExitOnNull(wzRoot, hr, E_INVALIDARG, "Failed to write web error, because no root was provided"); // get the set of all valid custom errors from the metabase mr.dwMDIdentifier = MD_CUSTOM_ERROR_DESC; mr.dwMDAttributes = METADATA_INHERIT; mr.dwMDUserType = IIS_MD_UT_SERVER; mr.dwMDDataType = ALL_METADATA; mr.dwMDDataLen = cchData = 0; mr.pbMDData = NULL; hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, L"/LM/W3SVC/Info", &mr); ExitOnFailure(hr, "Unable to get set of acceptable error codes for this server."); pwzAcceptableErrors = reinterpret_cast<LPWSTR>(mr.pbMDData); // Check if web errors already exist here mr.dwMDIdentifier = MD_CUSTOM_ERROR; mr.dwMDAttributes = METADATA_INHERIT; mr.dwMDUserType = IIS_MD_UT_SERVER; mr.dwMDDataType = ALL_METADATA; mr.dwMDDataLen = cchData = 0; mr.pbMDData = NULL; hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, wzRoot, &mr); if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr || MD_ERROR_DATA_NOT_FOUND == hr) { // // If we don't have one already, find an appropriate one to start with // // we can walk up key by key and look for custom errors to inherit hr = StrAllocConcat(&pwzSearchKey, wzRoot, 0); ExitOnFailure1(hr, "Failed to copy root string: %ls", wzRoot); pwz = pwzSearchKey + lstrlenW(pwzSearchKey); while (NULL == pwzErrors) { // find the last slash while (*pwz != '/' && pwz != pwzSearchKey) pwz --; if (pwz == pwzSearchKey) break; *pwz = L'\0'; // Try here. If it's not found, keep walking up the path hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, pwzSearchKey, &mr); if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr || MD_ERROR_DATA_NOT_FOUND == hr) hr = S_FALSE; ExitOnFailure1(hr, "failed to discover default error values to start with for web root: %ls while walking up the tree", wzRoot); if (S_OK == hr) { pwzErrors = reinterpret_cast<LPWSTR>(mr.pbMDData); break; } // Don't keep going if we're at the root if (0 == lstrcmpW(pwz + 1, L"W3SVC")) break; } } else { pwzErrors = reinterpret_cast<LPWSTR>(mr.pbMDData); } ExitOnFailure1(hr, "failed to discover default error values to start with for web root: %ls", wzRoot); // The above code should have come up with some value to start pwzErrors off with. Make sure it did. if (NULL == pwzErrors) { ExitOnFailure1(hr = E_UNEXPECTED, "failed to discover default error values to start with for web root: %ls", wzRoot); } // Loop through the web errors for (SCA_WEB_ERROR* pswe = psweList; pswe; pswe = pswe->psweNext) { // Assume that we will have to replace fOldValueFound = TRUE; // If the subcode is 0, that means "*" in MD_CUSTOM_ERROR (thus the special formatting logic) if (0 == pswe->iSubCode) { hr = StrAllocFormatted(&pwzCodeSubCode, L"%d,*", pswe->iErrorCode); ExitOnFailure(hr, "failed to create error code string while installing web error"); } else { hr = StrAllocFormatted(&pwzCodeSubCode, L"%d,%d", pswe->iErrorCode, pswe->iSubCode); ExitOnFailure(hr, "failed to create error code,subcode string while installing web error"); } hr = MultiSzFindSubstring(pwzErrors, pwzCodeSubCode, &dwFoundCodeSubCodeIndex, &wzFoundCodeSubCode); ExitOnFailure1(hr, "failed to find existing error code,subcode: %ls", pwzCodeSubCode); // If we didn't find this error code/sub code pair in the list already, make sure it's acceptable to add if (S_FALSE == hr) { // // Make sure this error code/sub code pair is in the "acceptable" list // // If the subcode is 0, that means "0" in MD_CUSTOM_ERROR_DESC (no special formatting logic needed) hr = StrAllocFormatted(&pwzAcceptableCodeSubCode, L"%d,%d", pswe->iErrorCode, pswe->iSubCode); ExitOnFailure(hr, "failed to create error code,subcode string while installing web error"); // We don't care where it is, just whether it's there or not hr = MultiSzFindSubstring(pwzAcceptableErrors, pwzAcceptableCodeSubCode, NULL, NULL); ExitOnFailure1(hr, "failed to find whether or not error code, subcode: %ls is supported", pwzCodeSubCode); if (S_FALSE == hr) { WcaLog(LOGMSG_VERBOSE, "Skipping error code, subcode: %ls because it is not supported by the server.", pwzCodeSubCode); continue; } // If we didn't find it (and its an acceptable error) then we have nothing to replace fOldValueFound = FALSE; } // Set up the new error string if needed if (*(pswe->wzFile)) { hr = StrAllocFormatted(&pwzNewError, L"%s,FILE,%s", pwzCodeSubCode, pswe->wzFile); ExitOnFailure2(hr, "failed to create new error code string with code,subcode: %ls, file: %ls", pwzCodeSubCode, pswe->wzFile); } else if (*(pswe->wzURL)) { hr = StrAllocFormatted(&pwzNewError, L"%s,URL,%s", pwzCodeSubCode, pswe->wzURL); ExitOnFailure2(hr, "failed to create new error code string with code,subcode: %ls, file: %ls", pwzCodeSubCode, pswe->wzFile); } else if (fOldValueFound) { // If no File or URL was specified, they want a default error so remove the old value from the MULTISZ and move on hr = MultiSzRemoveString(&pwzErrors, dwFoundCodeSubCodeIndex); ExitOnFailure1(hr, "failed to remove string for error code sub code: %ls in order to make it 'default'", pwzCodeSubCode); continue; } // If we have something to replace, replace it, otherwise, put it at the beginning (order shouldn't matter) if (fOldValueFound) { hr = MultiSzReplaceString(&pwzErrors, dwFoundCodeSubCodeIndex, pwzNewError); ExitOnFailure1(hr, "failed to replace old error string with new error string for error code,subcode: %ls", pwzCodeSubCode); } else { hr = MultiSzPrepend(&pwzErrors, NULL, pwzNewError); ExitOnFailure1(hr, "failed to prepend new error string for error code,subcode: %ls", pwzCodeSubCode); } } // now write the CustomErrors to the metabase if (weptWeb == iParentType) { hr = ScaWriteMetabaseValue(piMetabase, wzRoot, L"/Root", MD_CUSTOM_ERROR, METADATA_INHERIT, IIS_MD_UT_FILE, MULTISZ_METADATA, pwzErrors); ExitOnFailure(hr, "Failed to write Web Error to /Root"); } else { hr = ScaWriteMetabaseValue(piMetabase, wzRoot, NULL, MD_CUSTOM_ERROR, METADATA_INHERIT, IIS_MD_UT_FILE, MULTISZ_METADATA, pwzErrors); ExitOnFailure(hr, "Failed to write Web Error"); } LExit: ReleaseStr(pwzErrors); ReleaseStr(pwzSearchKey); ReleaseStr(pwzCodeSubCode); ReleaseStr(pwzAcceptableCodeSubCode); ReleaseStr(pwzAcceptableErrors); return hr; }
extern "C" HRESULT MsuEngineExecutePackage( __in BURN_EXECUTE_ACTION* pExecuteAction, __in BOOL fRollback, __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, __in LPVOID pvContext, __out BOOTSTRAPPER_APPLY_RESTART* pRestart ) { HRESULT hr = S_OK; int nResult = IDNOACTION; LPWSTR sczCachedDirectory = NULL; LPWSTR sczMsuPath = NULL; LPWSTR sczSystemPath = NULL; LPWSTR sczWusaPath = NULL; LPWSTR sczCommand = NULL; SC_HANDLE schWu = NULL; BOOL fWuWasDisabled = FALSE; STARTUPINFOW si = { }; PROCESS_INFORMATION pi = { }; GENERIC_EXECUTE_MESSAGE message = { }; DWORD dwExitCode = 0; BOOL fDoDependency = FALSE; // get wusa.exe path hr = PathGetKnownFolder(CSIDL_SYSTEM, &sczSystemPath); ExitOnFailure(hr, "Failed to find System32 directory."); hr = PathConcat(sczSystemPath, L"wusa.exe", &sczWusaPath); ExitOnFailure(hr, "Failed to allocate WUSA.exe path."); // build command switch (pExecuteAction->msuPackage.action) { case BOOTSTRAPPER_ACTION_STATE_INSTALL: // get cached executable path hr = CacheGetCompletedPath(TRUE, pExecuteAction->msuPackage.pPackage->sczCacheId, &sczCachedDirectory); ExitOnFailure1(hr, "Failed to get cached path for package: %ls", pExecuteAction->msuPackage.pPackage->sczId); hr = PathConcat(sczCachedDirectory, pExecuteAction->msuPackage.pPackage->rgPayloads[0].pPayload->sczFilePath, &sczMsuPath); ExitOnFailure(hr, "Failed to build executable path."); // format command hr = StrAllocFormatted(&sczCommand, L"\"%ls\" \"%ls\" /quiet /norestart", sczWusaPath, sczMsuPath); ExitOnFailure(hr, "Failed to format MSU install command."); break; case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: // format command hr = StrAllocFormatted(&sczCommand, L"\"%ls\" /uninstall /kb:%ls /quiet /norestart", sczWusaPath, pExecuteAction->msuPackage.pPackage->Msu.sczKB); ExitOnFailure(hr, "Failed to format MSU uninstall command."); break; default: hr = E_UNEXPECTED; ExitOnFailure(hr, "Failed to get action arguments for MSU package."); } if (pExecuteAction->msuPackage.sczLogPath && *pExecuteAction->msuPackage.sczLogPath) { hr = StrAllocConcat(&sczCommand, L" /log:", 0); ExitOnFailure(hr, "Failed to append log switch to MSU command-line."); hr = StrAllocConcat(&sczCommand, pExecuteAction->msuPackage.sczLogPath, 0); ExitOnFailure(hr, "Failed to append log path to MSU command-line."); } LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pExecuteAction->msuPackage.pPackage->sczId, LoggingActionStateToString(pExecuteAction->msuPackage.action), sczMsuPath ? sczMsuPath : pExecuteAction->msuPackage.pPackage->Msu.sczKB, sczCommand); hr = EnsureWUServiceEnabled(&schWu, &fWuWasDisabled); ExitOnFailure(hr, "Failed to ensure WU service was enabled to install MSU package."); // create process si.cb = sizeof(si); if (!::CreateProcessW(sczWusaPath, sczCommand, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) { ExitWithLastError1(hr, "Failed to CreateProcess on path: %ls", sczWusaPath); } 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 MSU progress."); // wait for process to terminate hr = ProcWaitForCompletion(pi.hProcess, 500, &dwExitCode); if (HRESULT_FROM_WIN32(WAIT_TIMEOUT) != hr) { ExitOnFailure1(hr, "Failed to wait for executable to complete: %ls", sczWusaPath); } } while (HRESULT_FROM_WIN32(WAIT_TIMEOUT) == hr); // get process exit code if (!::GetExitCodeProcess(pi.hProcess, &dwExitCode)) { ExitWithLastError(hr, "Failed to get process exit code."); } // We'll normalize the restart required error code from wusa.exe just in case. Most likely // that on reboot we'll actually get WU_S_REBOOT_REQUIRED. if (HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED) == static_cast<HRESULT>(dwExitCode)) { dwExitCode = ERROR_SUCCESS_REBOOT_REQUIRED; } // handle exit code switch (dwExitCode) { case S_OK: __fallthrough; case WU_S_ALREADY_INSTALLED: fDoDependency = TRUE; __fallthrough; case S_FALSE: __fallthrough; case WU_E_NOT_APPLICABLE: *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE; hr = S_OK; break; case ERROR_SUCCESS_REBOOT_REQUIRED: __fallthrough; case WU_S_REBOOT_REQUIRED: fDoDependency = TRUE; *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED; hr = S_OK; break; default: hr = E_UNEXPECTED; break; } if (fDoDependency) { if (BOOTSTRAPPER_ACTION_STATE_INSTALL == pExecuteAction->msuPackage.action) { hr = DependencyRegisterPackage(pExecuteAction->msuPackage.pPackage); ExitOnFailure(hr, "Failed to register the package dependency providers."); } else if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pExecuteAction->msuPackage.action) { hr = DependencyUnregisterPackage(pExecuteAction->msuPackage.pPackage); ExitOnFailure(hr, "Failed to unregister the package dependency providers."); } } LExit: ReleaseStr(sczCachedDirectory); ReleaseStr(sczMsuPath); ReleaseStr(sczSystemPath); ReleaseStr(sczWusaPath); ReleaseStr(sczCommand); ReleaseHandle(pi.hProcess); ReleaseHandle(pi.hThread); if (fWuWasDisabled) { SetServiceStartType(schWu, SERVICE_DISABLED); } return hr; }
int __cdecl wmain( __in int argc, __in WCHAR * argv[] ) { HRESULT hr = S_OK; LPWSTR pwzCommandLine = NULL; LPWSTR wzAppId = NULL; GUID guidApp; DWORD64 dw64Version; LPWSTR pwzFeedUri = NULL; LPWSTR pwzApplicationPath = NULL; LPWSTR pwzApplicationDirectory = NULL; DWORD64 dw64NextUpdateTime = 0; BOOL fUpdateReady = FALSE; DWORD64 dw64UpdateVersion = 0; LPWSTR pwzFeedPath = NULL; LPWSTR pwzSetupPath = NULL; DWORD dwTimeToLive = 0; LPWSTR pwzApplicationId = NULL; LPWSTR pwzApplicationSource = NULL; BOOL bDeleteUpdateInfoPath = FALSE; BOOL bDeleteUpdateBinaryPath = FALSE; HANDLE hProcess = INVALID_HANDLE_VALUE; HANDLE hUpdateMutex = INVALID_HANDLE_VALUE; // // Process command-line arguments. // for (int i=1; i<argc; i++) { if (argv[i][0] == L'-' || argv[i][0] == L'/') { if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"ac", -1)) { if (wzAppId) { ExitOnFailure(hr = E_INVALIDARG, "May only specify one -ac switch."); } wzAppId = argv[++i]; hr = ::CLSIDFromString(wzAppId, &guidApp); ExitOnFailure(hr, "Failed to parse the -ac argument."); } } else { ExitOnFailure1(hr = E_INVALIDARG, "Bad commandline argument: %S", argv[i]); } } ExitOnNull(wzAppId, hr, E_INVALIDARG, "Must specify a -ac switch."); hr = GetUpdateMutex(&guidApp, &hUpdateMutex); if (FAILED(hr)) { TraceError(hr, "Failed to query the update mutex. Proceeding as if this process didn't acquire the mutex."); } hr = RssUpdateGetAppInfo(wzAppId, &dw64Version, &pwzFeedUri, &pwzApplicationPath); ExitOnFailure(hr, "Failed to get app info."); // If we acquired the update lock and there is already an update downloaded, install that now. if (INVALID_HANDLE_VALUE != hUpdateMutex) { Trace(REPORT_DEBUG, "Got the update mutex. Will check for updates on local machine before launching app."); // If an update is available and higher version that the application currently on the local // machine, launch the install and bail. hr = RssUpdateTryLaunchUpdate(wzAppId, dw64Version, &hProcess, &dw64NextUpdateTime); if (SUCCEEDED(hr)) { if (hProcess) { ::CloseHandle(hProcess); ExitFunction(); // bail since we're doing an update } } } else { Trace(REPORT_DEBUG, "Didn't get the update mutex. Won't check for updates."); } hr = PathExpand(&pwzCommandLine, pwzApplicationPath, PATH_EXPAND_FULLPATH); ExitOnFailure(hr, "Failed to expand application path."); if (pwzCommandLine && L'\"' != pwzCommandLine[0]) { // Get the working directory. hr = PathGetDirectory(pwzCommandLine, &pwzApplicationDirectory); ExitOnFailure(hr, "Failed to get application directory from command-line."); // Put quotes around the command line. hr = StrAllocPrefix(&pwzCommandLine, L"\"", 0); ExitOnFailure(hr, "Failed to prefix command-line with quote."); hr = StrAllocConcat(&pwzCommandLine, L"\"", 0); ExitOnFailure(hr, "Failed to concat command-line with quote."); } Trace1(REPORT_DEBUG, "Launching the target app with commandline: %ls.", pwzCommandLine); hr = LaunchTarget(pwzCommandLine, pwzApplicationDirectory, &hProcess); ExitOnFailure1(hr, "Failed to launch %ls", pwzCommandLine); // If we acquired the update lock then check to see if enough time has passed such that we look for more updates. if (INVALID_HANDLE_VALUE != hUpdateMutex) { hr = RssUpdateCheckFeed(wzAppId, dw64Version, pwzFeedUri, dw64NextUpdateTime); hr = S_OK; } LExit: if (INVALID_HANDLE_VALUE != hUpdateMutex) { ::CloseHandle(hUpdateMutex); } if (INVALID_HANDLE_VALUE != hProcess) { ::CloseHandle(hProcess); } if (bDeleteUpdateInfoPath) { ::DeleteFileW(pwzFeedPath); } if (bDeleteUpdateBinaryPath) { ::DeleteFileW(pwzSetupPath); } ReleaseStr(pwzApplicationSource); ReleaseStr(pwzApplicationId); ReleaseStr(pwzSetupPath); ReleaseStr(pwzFeedPath); ReleaseStr(pwzApplicationPath); ReleaseStr(pwzFeedUri); ReleaseStr(pwzCommandLine); ReleaseStr(pwzApplicationDirectory); return SCODE_CODE(hr); }
HRESULT RssUpdateGetUpdateInfo( __in LPCWSTR wzApplicationId, __out_opt DWORD64* pdw64NextUpdate, __out_opt BOOL* pfUpdateReady, __out_opt DWORD64* pdw64UpdateVersion, __out_opt LPWSTR* ppwzLocalFeedPath, __out_opt LPWSTR* ppwzLocalSetupPath ) { HRESULT hr = S_OK; LPWSTR pwzUpdateInfoPath = NULL; HANDLE hFile = INVALID_HANDLE_VALUE; DWORD dwRead = 0; DWORD dwExpected = 0; DWORD64 dw64NextUpdate = 0; DWORD64 dw64UpdateVersion = 0; BOOL fUpdateReady = TRUE; WCHAR wzBuffer[1024 * 64]; LPWSTR pwzLocalFeedPath = NULL; LPWSTR pwzLocalSetupPath = NULL; hr = GetUpdateInfoFileName(wzApplicationId, &pwzUpdateInfoPath); ExitOnFailure(hr, "Failed to allocate path to update info."); hFile = ::CreateFileW(pwzUpdateInfoPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hFile) { ExitWithLastError(hr, "Failed to open update info."); } dwExpected = sizeof(dw64NextUpdate); if (!::ReadFile(hFile, &dw64NextUpdate, dwExpected, &dwRead, NULL)) { ExitWithLastError(hr, "Failed to read next update time from update info."); } else if (dwExpected != dwRead) { hr = E_UNEXPECTED; ExitOnFailure(hr, "Failed to find next update time at beginning of update info."); } dwExpected = sizeof(dw64UpdateVersion); if (!::ReadFile(hFile, &dw64UpdateVersion, dwExpected, &dwRead, NULL)) { ExitWithLastError(hr, "Failed to read update version from update info."); } else if (0 == dwRead) // no other update information { fUpdateReady = FALSE; } else if (dwExpected != dwRead) { hr = E_UNEXPECTED; ExitOnFailure(hr, "Failed to find update version in update info."); } if (fUpdateReady) { dwExpected = sizeof(WCHAR) * 2; if (!::ReadFile(hFile, wzBuffer, dwExpected, &dwRead, NULL)) { ExitWithLastError(hr, "Failed to read newline after next update time in update info."); } else if (dwExpected != dwRead || wzBuffer[0] != L'\r' || wzBuffer[1] != L'\n') { hr = E_UNEXPECTED; ExitOnFailure(hr, "Failed to find newline after next update time in update info."); } LPWSTR pwcCrLn = NULL; do { dwExpected = sizeof(wzBuffer) - sizeof(WCHAR); // leave space to null terminate the buffer if (!::ReadFile(hFile, wzBuffer, dwExpected, &dwRead, NULL)) { ExitWithLastError(hr, "Failed to read buffer in update info."); } else if (0 == dwRead) { break; } Assert(dwRead / sizeof(WCHAR) < countof(wzBuffer)); wzBuffer[dwRead / sizeof(WCHAR)] = L'\0'; pwcCrLn = wcsstr(wzBuffer, L"\r\n"); if (pwcCrLn) { *pwcCrLn = L'\0'; hr = StrAllocConcat(&pwzLocalFeedPath, wzBuffer, 0); ExitOnFailure(hr, "Failed to copy buffer into feed path."); pwcCrLn += 2; if (*pwcCrLn) { hr = StrAllocString(&pwzLocalSetupPath, pwcCrLn, 0); ExitOnFailure(hr, "Failed to copy remaining buffer into setup path."); } } else { hr = StrAllocConcat(&pwzLocalFeedPath, wzBuffer, 0); ExitOnFailure(hr, "Failed to copy buffer into feed path."); } } while (!pwcCrLn); do { dwExpected = sizeof(wzBuffer) - sizeof(WCHAR); // leave space to null terminate the buffer if (!::ReadFile(hFile, wzBuffer, dwExpected, &dwRead, NULL)) { ExitWithLastError(hr, "Failed to read buffer in update info."); } else if (0 == dwRead) { break; } Assert(dwRead / sizeof(WCHAR) < countof(wzBuffer)); wzBuffer[dwRead / sizeof(WCHAR)] = L'\0'; hr = StrAllocConcat(&pwzLocalSetupPath, wzBuffer, 0); ExitOnFailure(hr, "Failed to copy buffer into setup path."); } while (0 < dwRead); if (!pwzLocalSetupPath) { hr = E_UNEXPECTED; ExitOnFailure(hr, "Failed to parse update info."); } } if (pfUpdateReady) { *pfUpdateReady = fUpdateReady; } if (pdw64NextUpdate) { *pdw64NextUpdate = dw64NextUpdate; } if (fUpdateReady && pdw64UpdateVersion) { *pdw64UpdateVersion = dw64UpdateVersion; } if (fUpdateReady && ppwzLocalFeedPath) { hr = StrAllocString(ppwzLocalFeedPath, pwzLocalFeedPath, 0); ExitOnFailure(hr, "Failed to allocate local feed path."); } if (fUpdateReady && ppwzLocalSetupPath) { hr = StrAllocString(ppwzLocalSetupPath, pwzLocalSetupPath, 0); ExitOnFailure(hr, "Failed to allocate local setup path."); } LExit: ReleaseStr(pwzLocalSetupPath); ReleaseStr(pwzLocalFeedPath); ReleaseFile(hFile); ReleaseStr(pwzUpdateInfoPath); return hr; }
static HRESULT DoStart( __in JSON_WRITER* pWriter, __in JSON_TOKEN tokenStart, __in_z LPCWSTR wzStartString ) { Assert(JSON_TOKEN_ARRAY_START == tokenStart || JSON_TOKEN_OBJECT_START == tokenStart); HRESULT hr = S_OK; JSON_TOKEN token = JSON_TOKEN_NONE; BOOL fNeedComma = FALSE; BOOL fPushToken = TRUE; ::EnterCriticalSection(&pWriter->cs); hr = EnsureTokenStack(pWriter); ExitOnFailure(hr, "Failed to ensure token stack for start."); token = pWriter->rgTokenStack[pWriter->cTokens - 1]; switch (token) { case JSON_TOKEN_NONE: token = tokenStart; fPushToken = FALSE; break; case JSON_TOKEN_ARRAY_START: // array start changes to array value. token = JSON_TOKEN_ARRAY_VALUE; break; case JSON_TOKEN_ARRAY_VALUE: case JSON_TOKEN_ARRAY_END: case JSON_TOKEN_OBJECT_END: fNeedComma = TRUE; break; default: // everything else is not allowed. hr = E_UNEXPECTED; break; } ExitOnRootFailure(hr, "Cannot start array or object to JSON serializer now."); if (fNeedComma) { hr = StrAllocConcat(&pWriter->sczJson, L",", 0); ExitOnFailure(hr, "Failed to add comma for start array or object to JSON."); } hr = StrAllocConcat(&pWriter->sczJson, wzStartString, 0); ExitOnFailure(hr, "Failed to start JSON array or object."); pWriter->rgTokenStack[pWriter->cTokens - 1] = token; if (fPushToken) { pWriter->rgTokenStack[pWriter->cTokens] = tokenStart; ++pWriter->cTokens; } LExit: ::LeaveCriticalSection(&pWriter->cs); return hr; }
static HRESULT GetTargetPath( __in eOBJECTTYPE eType, __in LPCWSTR pwzSecureObject, __out LPWSTR* ppwzTargetPath ) { HRESULT hr = S_OK; PMSIHANDLE hView = NULL; PMSIHANDLE hRecObject = NULL; PMSIHANDLE hRec = NULL; int iRoot = 0; int iAllUsers = 0; LPWSTR pwzKey = NULL; LPWSTR pwzFormattedString = NULL; if (OT_SERVICE == eType) { hr = WcaTableExists(L"ServiceInstall"); if (S_FALSE == hr) { hr = E_UNEXPECTED; } ExitOnFailure(hr, "failed to open ServiceInstall table to secure object"); hr = WcaOpenView(wzQUERY_SERVICEINSTALL, &hView); ExitOnFailure(hr, "failed to open view on ServiceInstall table"); // create a record that stores the object to secure hRec = MsiCreateRecord(1); MsiRecordSetStringW(hRec, 1, pwzSecureObject); // execute a view looking for the object's ServiceInstall.ServiceInstall row. hr = WcaExecuteView(hView, hRec); ExitOnFailure(hr, "failed to execute view on ServiceInstall table"); hr = WcaFetchSingleRecord(hView, &hRecObject); ExitOnFailure(hr, "failed to fetch ServiceInstall row for secure object"); hr = WcaGetRecordFormattedString(hRecObject, QSSI_NAME, ppwzTargetPath); ExitOnFailure1(hr, "failed to get service name for secure object: %ls", pwzSecureObject); } else if (OT_FOLDER == eType) { hr = WcaGetTargetPath(pwzSecureObject, ppwzTargetPath); ExitOnFailure1(hr, "failed to get target path for directory id: %ls", pwzSecureObject); } else if (OT_FILE == eType) { hr = StrAllocFormatted(&pwzFormattedString, L"[#%s]", pwzSecureObject); ExitOnFailure1(hr, "failed to create formatted string for securing file object: %ls", pwzSecureObject); hr = WcaGetFormattedString(pwzFormattedString, ppwzTargetPath); ExitOnFailure2(hr, "failed to get file path from formatted string: %ls for secure object: %ls", pwzFormattedString, pwzSecureObject); } else if (OT_REGISTRY == eType) { hr = WcaTableExists(L"Registry"); if (S_FALSE == hr) { hr = E_UNEXPECTED; } ExitOnFailure(hr, "failed to open Registry table to secure object"); hr = WcaOpenView(wzQUERY_REGISTRY, &hView); ExitOnFailure(hr, "failed to open view on Registry table"); // create a record that stores the object to secure hRec = MsiCreateRecord(1); MsiRecordSetStringW(hRec, 1, pwzSecureObject); // execute a view looking for the object's Registry row hr = WcaExecuteView(hView, hRec); ExitOnFailure(hr, "failed to execute view on Registry table"); hr = WcaFetchSingleRecord(hView, &hRecObject); ExitOnFailure(hr, "failed to fetch Registry row for secure object"); hr = WcaGetRecordInteger(hRecObject, QSOC_REGROOT, &iRoot); ExitOnFailure1(hr, "Failed to get reg key root for secure object: %ls", pwzSecureObject); hr = WcaGetRecordFormattedString(hRecObject, QSOC_REGKEY, &pwzKey); ExitOnFailure1(hr, "Failed to get reg key for secure object: %ls", pwzSecureObject); // Decode the root value if (-1 == iRoot) { // They didn't specify a root so that means it's either HKCU or HKLM depending on ALLUSERS property hr = WcaGetIntProperty(L"ALLUSERS", &iAllUsers); ExitOnFailure(hr, "failed to get value of ALLUSERS property"); if (1 == iAllUsers) { hr = StrAllocString(ppwzTargetPath, L"MACHINE\\", 0); ExitOnFailure(hr, "failed to allocate target registry string with HKLM root"); } else { hr = StrAllocString(ppwzTargetPath, L"CURRENT_USER\\", 0); ExitOnFailure(hr, "failed to allocate target registry string with HKCU root"); } } else if (msidbRegistryRootClassesRoot == iRoot) { hr = StrAllocString(ppwzTargetPath, L"CLASSES_ROOT\\", 0); ExitOnFailure(hr, "failed to allocate target registry string with HKCR root"); } else if (msidbRegistryRootCurrentUser == iRoot) { hr = StrAllocString(ppwzTargetPath, L"CURRENT_USER\\", 0); ExitOnFailure(hr, "failed to allocate target registry string with HKCU root"); } else if (msidbRegistryRootLocalMachine == iRoot) { hr = StrAllocString(ppwzTargetPath, L"MACHINE\\", 0); ExitOnFailure(hr, "failed to allocate target registry string with HKLM root"); } else if (msidbRegistryRootUsers == iRoot) { hr = StrAllocString(ppwzTargetPath, L"USERS\\", 0); ExitOnFailure(hr, "failed to allocate target registry string with HKU root"); } else { ExitOnFailure2(hr = E_UNEXPECTED, "Unknown registry key root specified for secure object: '%ls' root: %d", pwzSecureObject, iRoot); } hr = StrAllocConcat(ppwzTargetPath, pwzKey, 0); ExitOnFailure2(hr, "Failed to concat key: %ls for secure object: %ls", pwzKey, pwzSecureObject); } else { AssertSz(FALSE, "How did you get here?"); ExitOnFailure1(hr = E_UNEXPECTED, "Unknown secure object type: %d", eType); } LExit: ReleaseStr(pwzFormattedString); ReleaseStr(pwzKey); return hr; }
static HRESULT CreateInstallCommand( __out LPWSTR* ppwzCommandLine, __in LPCWSTR pwzNgenPath, __in LPCWSTR pwzFile, __in int iPriority, __in int iAttributes, __in LPCWSTR pwzFileApp, __in LPCWSTR pwzDirAppBase ) { Assert(ppwzCommandLine && pwzNgenPath && *pwzNgenPath && pwzFile && *pwzFile&& pwzFileApp && pwzDirAppBase); HRESULT hr = S_OK; LPWSTR pwzQueueString = NULL; hr = StrAllocFormatted(ppwzCommandLine, L"%s install %s", pwzNgenPath, pwzFile); ExitOnFailure(hr, "failed to assemble install command line"); if (iPriority > 0) { hr = StrAllocFormatted(&pwzQueueString, L" /queue:%d", iPriority); ExitOnFailure(hr, "failed to format queue string"); hr = StrAllocConcat(ppwzCommandLine, pwzQueueString, 0); ExitOnFailure(hr, "failed to add queue string to NGEN command line"); } if (NGEN_DEBUG & iAttributes) { hr = StrAllocConcat(ppwzCommandLine, L" /Debug", 0); ExitOnFailure(hr, "failed to add debug to NGEN command line"); } if (NGEN_PROFILE & iAttributes) { hr = StrAllocConcat(ppwzCommandLine, L" /Profile", 0); ExitOnFailure(hr, "failed to add profile to NGEN command line"); } if (NGEN_NODEP & iAttributes) { hr = StrAllocConcat(ppwzCommandLine, L" /NoDependencies", 0); ExitOnFailure(hr, "failed to add no dependencies to NGEN command line"); } // If it's more than just two quotes around an empty string if (EMPTY_FORMATTED_LENGTH_QUOTED_FILE < lstrlenW(pwzFileApp)) { hr = StrAllocConcat(ppwzCommandLine, L" /ExeConfig:", 0); ExitOnFailure(hr, "failed to add exe config to NGEN command line"); hr = StrAllocConcat(ppwzCommandLine, pwzFileApp, 0); ExitOnFailure(hr, "failed to add file app to NGEN command line"); } // If it's more than just two quotes around a backslash if (EMPTY_FORMATTED_LENGTH_QUOTED_DIRECTORY < lstrlenW(pwzDirAppBase)) { hr = StrAllocConcat(ppwzCommandLine, L" /AppBase:", 0); ExitOnFailure(hr, "failed to add app base to NGEN command line"); hr = StrAllocConcat(ppwzCommandLine, pwzDirAppBase, 0); ExitOnFailure(hr, "failed to add dir app base to NGEN command line"); } LExit: return hr; }
DAPI_(HRESULT) PathCommandLineAppend( __deref_out_z LPWSTR* psczCommandLine, __in_z LPCWSTR wzArgument ) { HRESULT hr = S_OK; LPWSTR sczQuotedArg = NULL; BOOL fRequiresQuoting = FALSE; DWORD dwMaxEscapedSize = 0; // Loop through the argugment determining if it needs to be quoted and what the maximum // size would be if there are escape characters required. for (LPCWSTR pwz = wzArgument; *pwz; ++pwz) { // Arguments with whitespace need quoting. if (L' ' == *pwz || L'\t' == *pwz || L'\n' == *pwz || L'\v' == *pwz) { fRequiresQuoting = TRUE; } else if (L'"' == *pwz) // quotes need quoting and sometimes escaping. { fRequiresQuoting = TRUE; ++dwMaxEscapedSize; } else if (L'\\' == *pwz) // some backslashes need escaping, so we'll count them all to make sure there is room. { ++dwMaxEscapedSize; } ++dwMaxEscapedSize; } // If we found anything in the argument that requires our argument to be quoted if (fRequiresQuoting) { hr = StrAlloc(&sczQuotedArg, dwMaxEscapedSize + 3); // plus three for the start and end quote plus null terminator. ExitOnFailure(hr, "Failed to allocate argument to be quoted."); LPCWSTR pwz = wzArgument; LPWSTR pwzQuoted = sczQuotedArg; *pwzQuoted = L'"'; ++pwzQuoted; while (*pwz) { DWORD dwBackslashes = 0; while (L'\\' == *pwz) { ++dwBackslashes; ++pwz; } // Escape all backslashes at the end of the string. if (!*pwz) { dwBackslashes *= 2; } else if (L'"' == *pwz) // escape all backslashes before the quote and escape the quote itself. { dwBackslashes = dwBackslashes * 2 + 1; } // the backslashes don't have to be escaped. // Add the appropriate number of backslashes for (DWORD i = 0; i < dwBackslashes; ++i) { *pwzQuoted = L'\\'; ++pwzQuoted; } // If there is a character, add it after all the escaped backslashes if (*pwz) { *pwzQuoted = *pwz; ++pwz; ++pwzQuoted; } } *pwzQuoted = L'"'; ++pwzQuoted; *pwzQuoted = L'\0'; // ensure the arg is null terminated. } // If there is already data in the command line, append a space before appending the // argument. if (*psczCommandLine && **psczCommandLine) { hr = StrAllocConcat(psczCommandLine, L" ", 0); ExitOnFailure(hr, "Failed to append space to command line with existing data."); } hr = StrAllocConcat(psczCommandLine, sczQuotedArg ? sczQuotedArg : wzArgument, 0); ExitOnFailure(hr, "Failed to copy command line argument."); LExit: ReleaseStr(sczQuotedArg); return hr; }
extern "C" HRESULT PseudoBundleInitialize( __in DWORD64 qwEngineVersion, __in BURN_PACKAGE* pPackage, __in BOOL fPerMachine, __in_z LPCWSTR wzId, __in BOOTSTRAPPER_RELATION_TYPE relationType, __in BOOTSTRAPPER_PACKAGE_STATE state, __in_z LPCWSTR wzFilePath, __in_z LPCWSTR wzLocalSource, __in_z_opt LPCWSTR wzDownloadSource, __in DWORD64 qwSize, __in BOOL fVital, __in_z_opt LPCWSTR wzInstallArguments, __in_z_opt LPCWSTR wzRepairArguments, __in_z_opt LPCWSTR wzUninstallArguments, __in_opt BURN_DEPENDENCY_PROVIDER* pDependencyProvider, __in_opt BYTE* pbHash, __in DWORD cbHash ) { HRESULT hr = S_OK; LPWSTR sczRelationTypeCommandLineSwitch = NULL; LPCWSTR wzRelationTypeCommandLine = CoreRelationTypeToCommandLineString(relationType); if (wzRelationTypeCommandLine) { hr = StrAllocFormatted(&sczRelationTypeCommandLineSwitch, L" -%ls", wzRelationTypeCommandLine); } // Initialize the single payload, and fill out all the necessary fields pPackage->rgPayloads = (BURN_PACKAGE_PAYLOAD *)MemAlloc(sizeof(BURN_PACKAGE_PAYLOAD), TRUE); ExitOnNull(pPackage->rgPayloads, hr, E_OUTOFMEMORY, "Failed to allocate space for burn package payload inside of related bundle struct"); pPackage->cPayloads = 1; pPackage->rgPayloads->pPayload = (BURN_PAYLOAD *)MemAlloc(sizeof(BURN_PAYLOAD), TRUE); ExitOnNull(pPackage->rgPayloads, hr, E_OUTOFMEMORY, "Failed to allocate space for burn payload inside of related bundle struct"); pPackage->rgPayloads->pPayload->packaging = BURN_PAYLOAD_PACKAGING_EXTERNAL; pPackage->rgPayloads->pPayload->qwFileSize = qwSize; hr = StrAllocString(&pPackage->rgPayloads->pPayload->sczKey, wzId, 0); ExitOnFailure(hr, "Failed to copy key for pseudo bundle payload."); hr = StrAllocString(&pPackage->rgPayloads->pPayload->sczFilePath, wzFilePath, 0); ExitOnFailure(hr, "Failed to copy filename for pseudo bundle."); hr = StrAllocString(&pPackage->rgPayloads->pPayload->sczSourcePath, wzLocalSource, 0); ExitOnFailure(hr, "Failed to copy local source path for pseudo bundle."); if (wzDownloadSource && *wzDownloadSource) { hr = StrAllocString(&pPackage->rgPayloads->pPayload->downloadSource.sczUrl, wzDownloadSource, 0); ExitOnFailure(hr, "Failed to copy download source for pseudo bundle."); } if (pbHash) { pPackage->rgPayloads->pPayload->pbHash = static_cast<BYTE*>(MemAlloc(cbHash, FALSE)); ExitOnNull(pPackage->rgPayloads->pPayload->pbHash, hr, E_OUTOFMEMORY, "Failed to allocate memory for pseudo bundle payload hash."); pPackage->rgPayloads->pPayload->cbHash = cbHash; memcpy_s(pPackage->rgPayloads->pPayload->pbHash, pPackage->rgPayloads->pPayload->cbHash, pbHash, cbHash); } pPackage->rgPayloads->fCached = (BOOTSTRAPPER_PACKAGE_STATE_PRESENT == state || BOOTSTRAPPER_PACKAGE_STATE_CACHED == state); pPackage->Exe.fPseudoBundle = TRUE; pPackage->type = BURN_PACKAGE_TYPE_EXE; pPackage->fPerMachine = fPerMachine; pPackage->currentState = state; pPackage->cache = (BOOTSTRAPPER_PACKAGE_STATE_PRESENT == state || BOOTSTRAPPER_PACKAGE_STATE_CACHED == state) ? BURN_CACHE_STATE_COMPLETE : BURN_CACHE_STATE_NONE; pPackage->qwInstallSize = qwSize; pPackage->qwSize = qwSize; pPackage->fVital = fVital; hr = StrAllocString(&pPackage->sczId, wzId, 0); ExitOnFailure(hr, "Failed to copy key for pseudo bundle."); hr = StrAllocString(&pPackage->sczCacheId, wzId, 0); ExitOnFailure(hr, "Failed to copy cache id for pseudo bundle."); // If we are a self updating bundle, we don't have to have Install arguments. if (wzInstallArguments) { hr = StrAllocString(&pPackage->Exe.sczInstallArguments, wzInstallArguments, 0); ExitOnFailure(hr, "Failed to copy install arguments for related bundle package"); } if (sczRelationTypeCommandLineSwitch) { hr = StrAllocConcat(&pPackage->Exe.sczInstallArguments, sczRelationTypeCommandLineSwitch, 0); ExitOnFailure(hr, "Failed to append relation type to install arguments for related bundle package"); } if (wzRepairArguments) { hr = StrAllocString(&pPackage->Exe.sczRepairArguments, wzRepairArguments, 0); ExitOnFailure(hr, "Failed to copy repair arguments for related bundle package"); if (sczRelationTypeCommandLineSwitch) { hr = StrAllocConcat(&pPackage->Exe.sczRepairArguments, sczRelationTypeCommandLineSwitch, 0); ExitOnFailure(hr, "Failed to append relation type to repair arguments for related bundle package"); } pPackage->Exe.fRepairable = TRUE; } if (wzUninstallArguments) { hr = StrAllocString(&pPackage->Exe.sczUninstallArguments, wzUninstallArguments, 0); ExitOnFailure(hr, "Failed to copy uninstall arguments for related bundle package"); if (sczRelationTypeCommandLineSwitch) { hr = StrAllocConcat(&pPackage->Exe.sczUninstallArguments, sczRelationTypeCommandLineSwitch, 0); ExitOnFailure(hr, "Failed to append relation type to uninstall arguments for related bundle package"); } pPackage->fUninstallable = TRUE; } // Only support progress from engines that are compatible (aka: version greater than or equal to last protocol breaking change *and* versions that are older or the same as this engine). pPackage->Exe.protocol = (FILEMAKEVERSION(3, 6, 2221, 0) <= qwEngineVersion && qwEngineVersion <= FILEMAKEVERSION(rmj, rmm, rup, 0)) ? BURN_EXE_PROTOCOL_TYPE_BURN : BURN_EXE_PROTOCOL_TYPE_NONE; if (pDependencyProvider) { pPackage->rgDependencyProviders = (BURN_DEPENDENCY_PROVIDER*)MemAlloc(sizeof(BURN_DEPENDENCY_PROVIDER), TRUE); ExitOnNull(pPackage->rgDependencyProviders, hr, E_OUTOFMEMORY, "Failed to allocate memory for dependency providers."); pPackage->cDependencyProviders = 1; pPackage->rgDependencyProviders[0].fImported = pDependencyProvider->fImported; hr = StrAllocString(&pPackage->rgDependencyProviders[0].sczKey, pDependencyProvider->sczKey, 0); ExitOnFailure(hr, "Failed to copy key for pseudo bundle."); hr = StrAllocString(&pPackage->rgDependencyProviders[0].sczVersion, pDependencyProvider->sczVersion, 0); ExitOnFailure(hr, "Failed to copy version for pseudo bundle."); hr = StrAllocString(&pPackage->rgDependencyProviders[0].sczDisplayName, pDependencyProvider->sczDisplayName, 0); ExitOnFailure(hr, "Failed to copy display name for pseudo bundle."); } LExit: ReleaseStr(sczRelationTypeCommandLineSwitch); return hr; }
/******************************************************************** ConfigureIIs - CUSTOM ACTION ENTRY POINT for installing IIs settings ********************************************************************/ extern "C" UINT __stdcall ConfigureIIs( __in MSIHANDLE hInstall ) { //AssertSz(FALSE, "debug ConfigureIIs here"); HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; LPWSTR pwzScriptKey = NULL; LPWSTR pwzBackupId = NULL; LPWSTR pwzCustomActionData = NULL; // CustomActionData for ConfigureIIs custom action // initialize hr = WcaInitialize(hInstall, "ConfigureIIs"); ExitOnFailure(hr, "Failed to initialize"); // check for the prerequsite tables if (S_OK != WcaTableExists(L"IIsWebSite") && S_OK != WcaTableExists(L"IIsFilter") && S_OK != WcaTableExists(L"IIsProperty") && S_OK != WcaTableExists(L"IIsWebServiceExtension") && S_OK != WcaTableExists(L"IIsAppPool")) { WcaLog(LOGMSG_VERBOSE, "skipping IIs CustomAction, no IIsWebSite table, no IIsFilter table, no IIsProperty table, no IIsWebServiceExtension, and no IIsAppPool table"); ExitFunction1(hr = S_FALSE); } // Get a CaScript key hr = WcaCaScriptCreateKey(&pwzScriptKey); ExitOnFailure(hr, "Failed to get encoding key."); // Generate a unique string to be used for this product's transaction // This prevents a name collision when doing a major upgrade hr = WcaGetProperty(L"ProductCode", &pwzBackupId); ExitOnFailure(hr, "failed to get ProductCode"); hr = StrAllocConcat(&pwzBackupId, L"ScaConfigureIIs", 0); ExitOnFailure(hr, "failed to concat ScaConfigureIIs"); // make sure the operations below are wrapped in a "transaction" // use IIS7 transaction logic even if using Iis6 compat because Backup/Restore don't work with metabase compatibility if (MSICONDITION_TRUE == ::MsiEvaluateConditionW(hInstall, IIS7CONDITION)) { hr = ScaIIS7ConfigTransaction(pwzBackupId); MessageExitOnFailure(hr, msierrIISFailedSchedTransaction, "failed to start IIS7 transaction"); } else { hr = ScaMetabaseTransaction(pwzBackupId); MessageExitOnFailure(hr, msierrIISFailedSchedTransaction, "failed to start IIS transaction"); } // Write the CaScript key to the ConfigureIIS custom action data hr = WcaWriteStringToCaData(pwzScriptKey, &pwzCustomActionData); ExitOnFailure(hr, "Failed to add encoding key to CustomActionData."); // Wrap vcsUserDeferredQuery to send to deferred CA if (S_OK == WcaTableExists(L"User")) { hr = WcaWrapQuery(vcsUserDeferredQuery, &pwzCustomActionData, efmcColumn3 | efmcColumn4 | efmcColumn5, 0xFFFFFFFF, 0xFFFFFFFF); ExitOnFailure(hr, "Failed to wrap User query"); } else { hr = WcaWrapEmptyQuery(&pwzCustomActionData); ExitOnFailure(hr, "Failed to wrap User empty query"); } // Wrap vcsWebSvcExtQuery to send to deferred CA if (S_OK == WcaTableExists(L"IIsWebServiceExtension")) { hr = WcaWrapQuery(vcsWebSvcExtQuery, &pwzCustomActionData, efmcColumn2 | efmcColumn3 | efmcColumn4, 1, 0xFFFFFFFF); ExitOnFailure(hr, "Failed to wrap IIsWebServiceExtension query"); } else { hr = WcaWrapEmptyQuery(&pwzCustomActionData); ExitOnFailure(hr, "Failed to wrap IIsWebServiceExtension empty query"); } // Wrap vcsAppPoolQuery to send to deferred CA if (S_OK == WcaTableExists(L"IIsAppPool")) { hr = WcaWrapQuery(vcsAppPoolQuery, &pwzCustomActionData, efmcColumn2 | efmcColumn15 | efmcColumn16, 3, 0xFFFFFFFF); ExitOnFailure(hr, "Failed to wrap IIsAppPool query"); hr = WcaWrapQuery(vcsComponentAttrQuery, &pwzCustomActionData, 0, 0xFFFFFFFF, 0xFFFFFFFF); ExitOnFailure(hr, "Failed to wrap Component query"); } else { hr = WcaWrapEmptyQuery(&pwzCustomActionData); ExitOnFailure(hr, "Failed to wrap IIsAppPool empty query"); } // Wrap vcsMimeMapQuery to send to deferred CA if (S_OK == WcaTableExists(L"IIsMimeMap")) { hr = WcaWrapQuery(vcsMimeMapQuery, &pwzCustomActionData, efmcColumn4 | efmcColumn5, 0xFFFFFFFF, 0xFFFFFFFF); ExitOnFailure(hr, "Failed to wrap IIsMimeMap query"); } else { hr = WcaWrapEmptyQuery(&pwzCustomActionData); ExitOnFailure(hr, "Failed to wrap IIsMimeMap empty query"); } // Wrap vcsHttpHeaderQuery to send to deferred CA if (S_OK == WcaTableExists(L"IIsHttpHeader")) { hr = WcaWrapQuery(vcsHttpHeaderQuery, &pwzCustomActionData, efmcColumn1 | efmcColumn4, 0xFFFFFFFF, 0xFFFFFFFF); ExitOnFailure(hr, "Failed to wrap IIsHttpHeader query"); } else { hr = WcaWrapEmptyQuery(&pwzCustomActionData); ExitOnFailure(hr, "Failed to wrap IIsHttpHeader empty query"); } // Wrap vcsWebErrorQuery to send to deferred CA if (S_OK == WcaTableExists(L"IIsWebError")) { hr = WcaWrapQuery(vcsWebErrorQuery, &pwzCustomActionData, efmcColumn5 | efmcColumn6, 0xFFFFFFFF, 0xFFFFFFFF); ExitOnFailure(hr, "Failed to wrap IIsWebError query"); } else { hr = WcaWrapEmptyQuery(&pwzCustomActionData); ExitOnFailure(hr, "Failed to wrap IIsWebError empty query"); } // Wrap vcsWebDirPropertiesQuery to send to deferred CA if (S_OK == WcaTableExists(L"IIsWebDirProperties")) { hr = WcaWrapQuery(vcsWebDirPropertiesQuery, &pwzCustomActionData, efmcColumn8 | efmcColumn10 | efmcColumn12 | efmcColumn15, 0xFFFFFFFF, 0xFFFFFFFF); ExitOnFailure(hr, "Failed to wrap IIsWebDirProperties query"); } else { hr = WcaWrapEmptyQuery(&pwzCustomActionData); ExitOnFailure(hr, "Failed to wrap IIsWebDirProperties empty query"); } // Wrap vcsSslCertificateQuery to send to deferred CA if (S_OK == WcaTableExists(L"Certificate") && S_OK == WcaTableExists(L"CertificateHash") && S_OK == WcaTableExists(L"IIsWebSiteCertificates")) { hr = WcaWrapQuery(vcsSslCertificateQuery, &pwzCustomActionData, 0, 0xFFFFFFFF, 0xFFFFFFFF); ExitOnFailure(hr, "Failed to wrap SslCertificate query"); } else { hr = WcaWrapEmptyQuery(&pwzCustomActionData); ExitOnFailure(hr, "Failed to wrap SslCertificate empty query"); } // Wrap vcsWebLogQuery to send to deferred CA if (S_OK == WcaTableExists(L"IIsWebLog")) { hr = WcaWrapQuery(vcsWebLogQuery, &pwzCustomActionData, efmcColumn2, 0xFFFFFFFF, 0xFFFFFFFF); ExitOnFailure(hr, "Failed to wrap IIsWebLog query"); } else { hr = WcaWrapEmptyQuery(&pwzCustomActionData); ExitOnFailure(hr, "Failed to wrap IIsWebLog empty query"); } // Wrap vcsWebApplicationQuery to send to deferred CA if (S_OK == WcaTableExists(L"IIsWebApplication")) { hr = WcaWrapQuery(vcsWebApplicationQuery, &pwzCustomActionData, efmcColumn1, 0xFFFFFFFF, 0xFFFFFFFF); ExitOnFailure(hr, "Failed to wrap IIsWebApplication query"); } else { hr = WcaWrapEmptyQuery(&pwzCustomActionData); ExitOnFailure(hr, "Failed to wrap IIsWebApplication empty query"); } // Wrap vcsWebAppExtensionQuery to send to deferred CA if (S_OK == WcaTableExists(L"IIsWebApplicationExtension")) { hr = WcaWrapQuery(vcsWebAppExtensionQuery, &pwzCustomActionData, efmcColumn2 | efmcColumn3, 0xFFFFFFFF, 0xFFFFFFFF); ExitOnFailure(hr, "Failed to wrap IIsWebApplicationExtension query"); } else { hr = WcaWrapEmptyQuery(&pwzCustomActionData); ExitOnFailure(hr, "Failed to wrap IIsWebApplicationExtension empty query"); } // Wrap vcsWebQuery, vcsWebAddressQuery, and vcsWebBaseQuery to send to deferred CA if (S_OK == WcaTableExists(L"IIsWebAddress") && S_OK == WcaTableExists(L"IIsWebSite")) { hr = WcaWrapQuery(vcsWebQuery, &pwzCustomActionData, efmcColumn3 | efmcColumn4 | efmcColumn12 | efmcColumn13 | efmcColumn14, 2, 6); ExitOnFailure(hr, "Failed to wrap IIsWebSite query"); hr = WcaWrapQuery(vcsWebAddressQuery, &pwzCustomActionData, efmcColumn3 | efmcColumn4 | efmcColumn5, 0xFFFFFFFF, 0xFFFFFFFF); ExitOnFailure(hr, "Failed to wrap IIsWebAddress query"); hr = WcaWrapQuery(vcsWebBaseQuery, &pwzCustomActionData, efmcColumn2 | efmcColumn3 | efmcColumn4 | efmcColumn5 | efmcColumn7, 0xFFFFFFFF, 0xFFFFFFFF); ExitOnFailure(hr, "Failed to wrap IIsWebBase query"); } else { hr = WcaWrapEmptyQuery(&pwzCustomActionData); ExitOnFailure(hr, "Failed to wrap IIsWebSite empty query"); hr = WcaWrapEmptyQuery(&pwzCustomActionData); ExitOnFailure(hr, "Failed to wrap IIsWebAddress empty query"); hr = WcaWrapEmptyQuery(&pwzCustomActionData); ExitOnFailure(hr, "Failed to wrap IIsWebBase empty query"); } // Wrap vcsWebDirQuery to send to deferred CA if (S_OK == WcaTableExists(L"IIsWebDir")) { hr = WcaWrapQuery(vcsWebDirQuery, &pwzCustomActionData, efmcColumn4, 3, 0xFFFFFFFF); ExitOnFailure(hr, "Failed to wrap IIsWebDir query"); } else { hr = WcaWrapEmptyQuery(&pwzCustomActionData); ExitOnFailure(hr, "Failed to wrap IIsWebDir empty query"); } // Wrap vcsVDirQuery to send to deferred CA if (S_OK == WcaTableExists(L"IIsWebVirtualDir")) { hr = WcaWrapQuery(vcsVDirQuery, &pwzCustomActionData, efmcColumn4, 3, 5); ExitOnFailure(hr, "Failed to wrap IIsWebVirtualDir query"); } else { hr = WcaWrapEmptyQuery(&pwzCustomActionData); ExitOnFailure(hr, "Failed to wrap IIsWebVirtualDir empty query"); } // Wrap vcsFilterQuery to send to deferred CA if (S_OK == WcaTableExists(L"IIsFilter")) { hr = WcaWrapQuery(vcsFilterQuery, &pwzCustomActionData, efmcColumn4 | efmcColumn5, 3, 0xFFFFFFFF); ExitOnFailure(hr, "Failed to wrap IIsFilter query"); } else { hr = WcaWrapEmptyQuery(&pwzCustomActionData); ExitOnFailure(hr, "Failed to wrap IIsFilter empty query"); } // Wrap vcsPropertyQuery to send to deferred CA if (S_OK == WcaTableExists(L"IIsProperty")) { hr = WcaWrapQuery(vcsPropertyQuery, &pwzCustomActionData, efmcColumn4, 2, 0xFFFFFFFF); ExitOnFailure(hr, "Failed to wrap IIsProperty query"); } else { hr = WcaWrapEmptyQuery(&pwzCustomActionData); ExitOnFailure(hr, "Failed to wrap IIsProperty empty query"); } if (MSICONDITION_TRUE == ::MsiEvaluateConditionW(hInstall, USEIIS7CONDITION)) { // This must remain trace only, CA data may contain password WcaLog(LOGMSG_TRACEONLY, "Custom Action Data for ConfigureIIS7Exec will be: %ls", pwzCustomActionData); hr = WcaDoDeferredAction(L"ConfigureIIs7Exec", pwzCustomActionData, ConfigureIIsCost); ExitOnFailure(hr, "Failed to schedule ConfigureIIs7Exec custom action"); ReleaseNullStr(pwzCustomActionData); // Write the CaScript key to the ConfigureIIS custom action data hr = WcaWriteStringToCaData(pwzScriptKey, &pwzCustomActionData); ExitOnFailure(hr, "Failed to add script key to CustomActionData."); hr = WcaDoDeferredAction(L"WriteIIS7ConfigChanges", pwzCustomActionData, WriteIIS7ConfigChangesCost); ExitOnFailure(hr, "Failed to schedule WriteMetabaseChanges custom action"); } else { // This must remain trace only, CA data may contain password WcaLog(LOGMSG_TRACEONLY, "Custom Action Data for ConfigureIISExec will be: %ls", pwzCustomActionData); hr = WcaDoDeferredAction(L"ConfigureIIsExec", pwzCustomActionData, ConfigureIIsCost); ExitOnFailure(hr, "Failed to schedule ConfigureIISExec custom action"); ReleaseNullStr(pwzCustomActionData); // Write the CaScript key to the ConfigureIIS custom action data hr = WcaWriteStringToCaData(pwzScriptKey, &pwzCustomActionData); ExitOnFailure(hr, "Failed to add script key to CustomActionData."); hr = WcaDoDeferredAction(L"WriteMetabaseChanges", pwzCustomActionData, WriteMetabaseChangesCost); ExitOnFailure(hr, "Failed to schedule WriteMetabaseChanges custom action"); } LExit: ReleaseStr(pwzScriptKey); ReleaseStr(pwzBackupId); ReleaseStr(pwzCustomActionData); er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; return WcaFinalize(er); }
static HRESULT ReadRegKeyWriteLegacyDb( __in CFGDB_STRUCT *pcdb, __in LEGACY_SYNC_PRODUCT_SESSION *pSyncProductSession, __in LEGACY_REGISTRY_KEY *pRegKey, __in_z LPCWSTR wzSubKey ) { HRESULT hr = S_OK; HKEY hkKey = NULL; DWORD dwType = 0; DWORD dwIndex = 0; LPWSTR sczValueName = NULL; LPWSTR sczSubkeyName = NULL; LPWSTR sczSubkeyPath = NULL; hr = RegOpen(ManifestConvertToRootKey(pRegKey->dwRoot), wzSubKey, KEY_READ, &hkKey); if (E_FILENOTFOUND == hr) { ExitFunction1(hr = S_OK); } ExitOnFailure(hr, "Failed to open regkey: %ls", wzSubKey); dwIndex = 0; while (E_NOMOREITEMS != (hr = RegValueEnum(hkKey, dwIndex, &sczValueName, &dwType))) { ExitOnFailure(hr, "Failed to enumerate value %u", dwIndex); hr = ReadRegValueWriteLegacyDb(pcdb, pSyncProductSession, pRegKey, hkKey, wzSubKey, sczValueName, dwType); ExitOnFailure(hr, "Failed to write registry value setting: %ls", sczValueName); ++dwIndex; } // Recurse and handle subkeys as well dwIndex = 0; while (E_NOMOREITEMS != (hr = RegKeyEnum(hkKey, dwIndex, &sczSubkeyName))) { ExitOnFailure(hr, "Failed to enumerate key %u", dwIndex); hr = StrAllocString(&sczSubkeyPath, wzSubKey, 0); ExitOnFailure(hr, "Failed to allocate copy of subkey name"); hr = PathBackslashTerminate(&sczSubkeyPath); ExitOnFailure(hr, "Failed to ensure path is terminated with a backslash"); hr = StrAllocConcat(&sczSubkeyPath, sczSubkeyName, 0); ExitOnFailure(hr, "Failed to concatenate subkey name to subkey path"); hr = ReadRegKeyWriteLegacyDb(pcdb, pSyncProductSession, pRegKey, sczSubkeyPath); ExitOnFailure(hr, "Failed to read regkey and write settings for root: %u, subkey: %ls", pRegKey->dwRoot, sczSubkeyPath); ++dwIndex; } if (E_NOMOREITEMS == hr) { hr = S_OK; } LExit: ReleaseRegKey(hkKey); ReleaseStr(sczValueName); ReleaseStr(sczSubkeyName); ReleaseStr(sczSubkeyPath); return hr; }
static HRESULT DeleteEmptyRegistryKeyChildren( __in DWORD dwRoot, __in_z LPCWSTR wzSubKey ) { HRESULT hr = S_OK; HKEY hkKey = NULL; DWORD dwIndex = 0; BOOL fNoValues = FALSE; LPWSTR sczValueName = NULL; LPWSTR sczSubkeyName = NULL; LPWSTR sczSubkeyPath = NULL; DWORD dwSubKeyPathLen = 0; hr = RegOpen(ManifestConvertToRootKey(dwRoot), wzSubKey, KEY_READ, &hkKey); if (E_FILENOTFOUND == hr) { ExitFunction1(hr = S_OK); } ExitOnFailure(hr, "Failed to open regkey: %ls", wzSubKey); if (E_NOMOREITEMS == RegValueEnum(hkKey, dwIndex, &sczValueName, NULL)) { fNoValues = TRUE; } // Recurse and handle subkeys as well dwIndex = 0; while (E_NOMOREITEMS != (hr = RegKeyEnum(hkKey, dwIndex, &sczSubkeyName))) { ExitOnFailure(hr, "Failed to enumerate key %u", dwIndex); hr = StrAllocString(&sczSubkeyPath, wzSubKey, 0); ExitOnFailure(hr, "Failed to allocate copy of subkey name"); dwSubKeyPathLen = lstrlenW(sczSubkeyPath); if (0 == dwSubKeyPathLen) { hr = E_INVALIDARG; ExitOnFailure(hr, "Encountered empty keyname while enumerating subkeys under key: %ls", wzSubKey); } else if (L'\\' != sczSubkeyPath[dwSubKeyPathLen - 1]) { hr = StrAllocConcat(&sczSubkeyPath, L"\\", 1); ExitOnFailure(hr, "Failed to concatenate backslash to copy of regkey name"); } hr = StrAllocConcat(&sczSubkeyPath, sczSubkeyName, 0); ExitOnFailure(hr, "Failed to concatenate subkey name to subkey path"); hr = DeleteEmptyRegistryKeyChildren(dwRoot, sczSubkeyPath); // Increment and ignore the error if we didn't delete the subkey if (HRESULT_FROM_WIN32(ERROR_DIR_NOT_EMPTY) == hr) { ++dwIndex; hr = S_OK; } ExitOnFailure(hr, "Failed to read regkey and write settings for root: %u, subkey: %ls", dwRoot, sczSubkeyPath); } // If there are no keys and no values under it, delete it if (fNoValues && 0 == dwIndex) { hr = RegDelete(ManifestConvertToRootKey(dwRoot), wzSubKey, REG_KEY_DEFAULT, FALSE); ExitOnFailure(hr, "Failed to delete registry key at root: %u, subkey: %ls", dwRoot, wzSubKey); ExitFunction1(hr = S_OK); } else { ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_DIR_NOT_EMPTY)); } LExit: ReleaseRegKey(hkKey); ReleaseStr(sczValueName); ReleaseStr(sczSubkeyName); ReleaseStr(sczSubkeyPath); return hr; }
/****************************************************************** WixSchedInternetShortcuts - entry point ********************************************************************/ extern "C" UINT __stdcall WixSchedInternetShortcuts( __in MSIHANDLE hInstall ) { HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; UINT uiCost = 0; PMSIHANDLE hView = NULL; PMSIHANDLE hRec = NULL; MSIHANDLE hCreateFolderTable = NULL; MSIHANDLE hCreateFolderColumns = NULL; LPWSTR pwzCustomActionData = NULL; LPWSTR pwzComponent = NULL; LPWSTR pwzDirectory = NULL; LPWSTR pwzFilename = NULL; LPWSTR pwzTarget = NULL; LPWSTR pwzShortcutPath = NULL; int iAttr = 0; LPWSTR pwzIconFile = NULL; int iIconIndex = 0; IUniformResourceLocatorW* piURL = NULL; IShellLinkW* piShellLink = NULL; BOOL fInitializedCom = FALSE; hr = WcaInitialize(hInstall, "WixSchedInternetShortcuts"); ExitOnFailure(hr, "failed to initialize WixSchedInternetShortcuts."); // anything to do? if (S_OK != WcaTableExists(L"WixInternetShortcut")) { WcaLog(LOGMSG_STANDARD, "WixInternetShortcut table doesn't exist, so there are no Internet shortcuts to process"); goto LExit; } // check to see if we can create a shortcut - Server Core and others may not have a shell registered. hr = ::CoInitialize(NULL); ExitOnFailure(hr, "failed to initialize COM"); fInitializedCom = TRUE; hr = ::CoCreateInstance(CLSID_InternetShortcut, NULL, CLSCTX_ALL, IID_IUniformResourceLocatorW, (void**)&piURL); if (S_OK != hr) { WcaLog(LOGMSG_STANDARD, "failed to create an instance of IUniformResourceLocatorW, skipping shortcut creation"); ExitFunction1(hr = S_OK); } hr = ::CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_ALL, IID_IShellLinkW, (void**)&piShellLink); if (S_OK != hr) { WcaLog(LOGMSG_STANDARD, "failed to create an instance of IShellLinkW, skipping shortcut creation"); ExitFunction1(hr = S_OK); } // query and loop through all the shortcuts hr = WcaOpenExecuteView(vcsShortcutsQuery, &hView); ExitOnFailure(hr, "failed to open view on WixInternetShortcut table"); while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) { // read column values hr = WcaGetRecordString(hRec, esqComponent, &pwzComponent); ExitOnFailure(hr, "failed to get shortcut component"); hr = WcaGetRecordString(hRec, esqDirectory, &pwzDirectory); ExitOnFailure(hr, "failed to get shortcut directory"); hr = WcaGetRecordString(hRec, esqFilename, &pwzFilename); ExitOnFailure(hr, "failed to get shortcut filename"); hr = WcaGetRecordFormattedString(hRec, esqTarget, &pwzTarget); ExitOnFailure(hr, "failed to get shortcut target"); hr = WcaGetRecordInteger(hRec, esqAttributes, &iAttr); ExitOnFailure(hr, "failed to get shortcut attributes"); hr = WcaGetRecordFormattedString(hRec, esqIconFile, &pwzIconFile); ExitOnFailure(hr, "failed to get shortcut icon file"); hr = WcaGetRecordInteger(hRec, esqIconIndex, &iIconIndex); ExitOnFailure(hr, "failed to get shortcut icon index"); // skip processing this WixInternetShortcut row if the component isn't being configured WCA_TODO todo = WcaGetComponentToDo(pwzComponent); if (WCA_TODO_UNKNOWN == todo) { WcaLog(LOGMSG_VERBOSE, "Skipping shortcut for null-action component '%ls'", pwzComponent); continue; } // we need to create the directory where the shortcut is supposed to live; rather // than doing so in our deferred custom action, use the CreateFolder table to have MSI // make (and remove) them on our behalf (including the correct cleanup of parent directories). MSIDBERROR dbError = MSIDBERROR_NOERROR; WcaLog(LOGMSG_STANDARD, "Adding folder '%ls', component '%ls' to the CreateFolder table", pwzDirectory, pwzComponent); hr = WcaAddTempRecord(&hCreateFolderTable, &hCreateFolderColumns, L"CreateFolder", &dbError, 0, 2, pwzDirectory, pwzComponent); if (MSIDBERROR_DUPLICATEKEY == dbError) { WcaLog(LOGMSG_STANDARD, "Folder '%ls' already exists in the CreateFolder table; the above error is harmless", pwzDirectory); hr = S_OK; } ExitOnFailure(hr, "Couldn't add temporary CreateFolder row"); // only if we're installing/reinstalling do we need to schedule the deferred CA // (uninstallation is handled via permanent RemoveFile rows and temporary CreateFolder rows) if (WCA_TODO_INSTALL == todo || WCA_TODO_REINSTALL == todo) { // turn the Directory_ id into a path hr = WcaGetTargetPath(pwzDirectory, &pwzShortcutPath); ExitOnFailure(hr, "failed to allocate string for shortcut directory"); // append the shortcut filename hr = StrAllocConcat(&pwzShortcutPath, pwzFilename, 0); ExitOnFailure(hr, "failed to allocate string for shortcut filename"); // write the shortcut path and target to custom action data for deferred CAs hr = WcaWriteStringToCaData(pwzShortcutPath, &pwzCustomActionData); ExitOnFailure(hr, "failed to write shortcut path to custom action data"); hr = WcaWriteStringToCaData(pwzTarget, &pwzCustomActionData); ExitOnFailure(hr, "failed to write shortcut target to custom action data"); hr = WcaWriteIntegerToCaData(iAttr, &pwzCustomActionData); ExitOnFailure(hr, "failed to write shortcut attributes to custom action data"); hr = WcaWriteStringToCaData(pwzIconFile, &pwzCustomActionData); ExitOnFailure(hr, "failed to write icon file to custom action data"); hr = WcaWriteIntegerToCaData(iIconIndex, &pwzCustomActionData); ExitOnFailure(hr, "failed to write icon index to custom action data"); uiCost += COST_INTERNETSHORTCUT; } } if (E_NOMOREITEMS == hr) { hr = S_OK; } ExitOnFailure(hr, "Failure occured while processing WixInternetShortcut table"); // if we have any shortcuts to install if (pwzCustomActionData && *pwzCustomActionData) { // add cost to progress bar hr = WcaProgressMessage(uiCost, TRUE); ExitOnFailure(hr, "failed to extend progress bar for InternetShortcuts"); // provide custom action data to deferred and rollback CAs hr = WcaSetProperty(PLATFORM_DECORATION(L"WixRollbackInternetShortcuts"), pwzCustomActionData); ExitOnFailure(hr, "failed to set WixRollbackInternetShortcuts rollback custom action data"); hr = WcaSetProperty(PLATFORM_DECORATION(L"WixCreateInternetShortcuts"), pwzCustomActionData); ExitOnFailure(hr, "failed to set WixCreateInternetShortcuts custom action data"); } LExit: if (hCreateFolderTable) { ::MsiCloseHandle(hCreateFolderTable); } if (hCreateFolderColumns) { ::MsiCloseHandle(hCreateFolderColumns); } ReleaseStr(pwzCustomActionData); ReleaseStr(pwzComponent); ReleaseStr(pwzDirectory); ReleaseStr(pwzFilename); ReleaseStr(pwzTarget); ReleaseStr(pwzShortcutPath); ReleaseObject(piShellLink); ReleaseObject(piURL); if (fInitializedCom) { ::CoUninitialize(); } er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; return WcaFinalize(er); }
static HRESULT HandleOutput( __in BOOL fLogOutput, __in HANDLE hRead ) { BYTE *pBuffer = NULL; LPWSTR szLog = NULL; LPWSTR szTemp = NULL; LPWSTR pEnd = NULL; LPWSTR pNext = NULL; LPWSTR sczEscaped = NULL; LPSTR szWrite = NULL; DWORD dwBytes = OUTPUT_BUFFER; BOOL bFirst = TRUE; BOOL bUnicode = TRUE; HRESULT hr = S_OK; // Get buffer for output pBuffer = static_cast<BYTE *>(MemAlloc(OUTPUT_BUFFER, FALSE)); ExitOnNull(pBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer for output."); while (0 != dwBytes) { ::ZeroMemory(pBuffer, OUTPUT_BUFFER); if (!::ReadFile(hRead, pBuffer, OUTPUT_BUFFER - 1, &dwBytes, NULL) && GetLastError() != ERROR_BROKEN_PIPE) { ExitOnLastError(hr, "Failed to read from handle."); } if (fLogOutput) { // Check for UNICODE or ANSI output if (bFirst) { if ((isgraph(pBuffer[0]) && isgraph(pBuffer[1])) || (isgraph(pBuffer[0]) && isspace(pBuffer[1])) || (isspace(pBuffer[0]) && isgraph(pBuffer[1])) || (isspace(pBuffer[0]) && isspace(pBuffer[1]))) { bUnicode = FALSE; } bFirst = FALSE; } // Keep track of output if (bUnicode) { hr = StrAllocConcat(&szLog, (LPCWSTR)pBuffer, 0); ExitOnFailure(hr, "Failed to concatenate output strings"); } else { hr = StrAllocStringAnsi(&szTemp, (LPCSTR)pBuffer, 0, CP_OEMCP); ExitOnFailure(hr, "Failed to allocate output string"); hr = StrAllocConcat(&szLog, szTemp, 0); ExitOnFailure(hr, "Failed to concatenate output strings"); } // Log each line of the output pNext = szLog; pEnd = wcschr(szLog, L'\r'); if (NULL == pEnd) { pEnd = wcschr(szLog, L'\n'); } while (pEnd && *pEnd) { // Find beginning of next line pEnd[0] = 0; ++pEnd; if ((pEnd[0] == L'\r') || (pEnd[0] == L'\n')) { ++pEnd; } // Log output hr = StrAllocString(&sczEscaped, pNext, 0); ExitOnFailure(hr, "Failed to allocate copy of string"); hr = StrReplaceStringAll(&sczEscaped, L"%", L"%%"); ExitOnFailure(hr, "Failed to escape percent signs in string"); hr = StrAnsiAllocString(&szWrite, sczEscaped, 0, CP_OEMCP); ExitOnFailure(hr, "Failed to convert output to ANSI"); WcaLog(LOGMSG_STANDARD, szWrite); // Next line pNext = pEnd; pEnd = wcschr(pNext, L'\r'); if (NULL == pEnd) { pEnd = wcschr(pNext, L'\n'); } } hr = StrAllocString(&szTemp, pNext, 0); ExitOnFailure(hr, "Failed to allocate string"); hr = StrAllocString(&szLog, szTemp, 0); ExitOnFailure(hr, "Failed to allocate string"); } } // Print any text that didn't end with a new line if (szLog && *szLog) { hr = StrReplaceStringAll(&szLog, L"%", L"%%"); ExitOnFailure(hr, "Failed to escape percent signs in string"); hr = StrAnsiAllocString(&szWrite, szLog, 0, CP_OEMCP); ExitOnFailure(hr, "Failed to convert output to ANSI"); WcaLog(LOGMSG_VERBOSE, szWrite); } LExit: ReleaseMem(pBuffer); ReleaseStr(szLog); ReleaseStr(szTemp); ReleaseStr(szWrite); ReleaseStr(sczEscaped); return hr; }