/******************************************************************** RmuJoinSession - Joins an existing Restart Manager session. ********************************************************************/ extern "C" HRESULT DAPI RmuJoinSession( __out PRMU_SESSION *ppSession, __in_z LPCWSTR wzSessionKey ) { HRESULT hr = S_OK; DWORD er = ERROR_SUCCESS; PRMU_SESSION pSession = NULL; HMODULE hModule = NULL; PFNRMJOINSESSION pfnRmJoinSession = NULL; *ppSession = NULL; pSession = static_cast<PRMU_SESSION>(MemAlloc(sizeof(RMU_SESSION), TRUE)); ExitOnNull(pSession, hr, E_OUTOFMEMORY, "Failed to allocate the RMU_SESSION structure."); hr = RmuInitialize(); ExitOnFailure(hr, "Failed to initialize Restart Manager."); er = vpfnRmJoinSession(&pSession->dwSessionHandle, wzSessionKey); ExitOnWin32Error1(er, hr, "Failed to join Restart Manager session %ls.", wzSessionKey); ::InitializeCriticalSection(&pSession->cs); pSession->fInitialized = TRUE; *ppSession = pSession; LExit: if (FAILED(hr)) { ReleaseNullMem(pSession); } return hr; }
void LegacySyncUninitializeSession( __in CFGDB_STRUCT *pcdb, __inout LEGACY_SYNC_SESSION *pSyncSession ) { LEGACY_SYNC_PRODUCT_SESSION *pSyncProductSession = &pSyncSession->syncProductSession; if (pSyncSession->fInSceTransaction) { SceRollbackTransaction(pcdb->psceDb); pSyncSession->fInSceTransaction = FALSE; } for (DWORD i = 0; i < pSyncProductSession->cIniFiles; ++i) { IniFree(pSyncProductSession->rgIniFiles + i); } ReleaseNullMem(pSyncProductSession->rgIniFiles); ReleaseDict(pSyncProductSession->shDictValuesSeen); ReleaseDict(pSyncProductSession->shIniFilesByNamespace); DetectFreeArpProducts(&pSyncSession->arpProducts); DetectFreeExeProducts(&pSyncSession->exeProducts); ManifestFreeProductStruct(&pSyncProductSession->product); ZeroMemory(&pSyncProductSession->product, sizeof(pSyncProductSession->product)); }
/******************************************************************** RmuEndSession - Ends the session. If the session was joined by RmuJoinSession, any remaining resources are registered before the session is ended (left). ********************************************************************/ extern "C" HRESULT DAPI RmuEndSession( __in PRMU_SESSION pSession ) { HRESULT hr = S_OK; DWORD er = ERROR_SUCCESS; AssertSz(vcRmuInitialized, "Restart Manager was not properly initialized."); // Make sure all resources are registered if we joined the session. if (!pSession->fStartedSessionHandle) { hr = RmuRegisterResources(pSession); ExitOnFailure(hr, "Failed to register remaining resources."); } er = vpfnRmEndSession(pSession->dwSessionHandle); ExitOnWin32Error(er, hr, "Failed to end the Restart Manager session."); LExit: if (pSession->fInitialized) { ::DeleteCriticalSection(&pSession->cs); } ReleaseNullStrArray(pSession->rgsczFilenames, pSession->cFilenames); ReleaseNullApplicationArray(pSession->rgApplications, pSession->cApplications); ReleaseNullStrArray(pSession->rgsczServiceNames, pSession->cServiceNames); ReleaseNullMem(pSession); RmuUninitialize(); return hr; }
/****************************************************************** PromptToContinue - displays the prompt if the application is still running. ******************************************************************/ static HRESULT PromptToContinue( __in_z LPCWSTR wzApplication, __in_z LPCWSTR wzPrompt ) { HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; PMSIHANDLE hRecMessage = NULL; DWORD *prgProcessIds = NULL; DWORD cProcessIds = 0; hRecMessage = ::MsiCreateRecord(1); ExitOnNull(hRecMessage, hr, E_OUTOFMEMORY, "Failed to create record for prompt."); er = ::MsiRecordSetStringW(hRecMessage, 0, wzPrompt); ExitOnWin32Error(er, hr, "Failed to set prompt record field string"); do { hr = ProcFindAllIdsFromExeName(wzApplication, &prgProcessIds, &cProcessIds); if (SUCCEEDED(hr) && 0 < cProcessIds) { er = WcaProcessMessage(static_cast<INSTALLMESSAGE>(INSTALLMESSAGE_WARNING | MB_ABORTRETRYIGNORE | MB_DEFBUTTON3 | MB_ICONWARNING), hRecMessage); if (IDABORT == er) { hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); } else if (IDRETRY == er) { hr = S_FALSE; } else if (IDIGNORE == er) { hr = S_OK; } else { ExitOnWin32Error(er, hr, "Unexpected return value from prompt to continue."); } } ReleaseNullMem(prgProcessIds); cProcessIds = 0; } while (S_FALSE == hr); LExit: ReleaseMem(prgProcessIds); return hr; }
extern "C" void DetectReset( __in BURN_REGISTRATION* pRegistration, __in BURN_PACKAGES* pPackages, __in BURN_UPDATE* /*pUpdate*/ ) { RelatedBundlesUninitialize(&pRegistration->relatedBundles); ReleaseNullStr(pRegistration->sczDetectedProviderKeyBundleId); pRegistration->fEnabledForwardCompatibleBundle = FALSE; PackageUninitialize(&pRegistration->forwardCompatibleBundle); for (DWORD iPackage = 0; iPackage < pPackages->cPackages; ++iPackage) { BURN_PACKAGE* pPackage = pPackages->rgPackages + iPackage; pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN; pPackage->cache = BURN_CACHE_STATE_NONE; for (DWORD iPayload = 0; iPayload < pPackage->cPayloads; ++iPayload) { BURN_PACKAGE_PAYLOAD* pPayload = pPackage->rgPayloads + iPayload; pPayload->fCached = FALSE; } if (BURN_PACKAGE_TYPE_MSI == pPackage->type) { for (DWORD iFeature = 0; iFeature < pPackage->Msi.cFeatures; ++iFeature) { BURN_MSIFEATURE* pFeature = pPackage->Msi.rgFeatures + iFeature; pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN; } } else if (BURN_PACKAGE_TYPE_MSP == pPackage->type) { ReleaseNullMem(pPackage->Msp.rgTargetProducts); pPackage->Msp.cTargetProductCodes = 0; } } for (DWORD iPatchInfo = 0; iPatchInfo < pPackages->cPatchInfo; ++iPatchInfo) { MSIPATCHSEQUENCEINFOW* pPatchInfo = pPackages->rgPatchInfo + iPatchInfo; pPatchInfo->dwOrder = 0; pPatchInfo->uStatus = 0; } }
extern "C" void DetectReset( __in BURN_PACKAGES* pPackages ) { for (DWORD iPackage = 0; iPackage < pPackages->cPackages; ++iPackage) { BURN_PACKAGE* pPackage = pPackages->rgPackages + iPackage; pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN; pPackage->cache = BURN_CACHE_STATE_NONE; for (DWORD iPayload = 0; iPayload < pPackage->cPayloads; ++iPayload) { BURN_PACKAGE_PAYLOAD* pPayload = pPackage->rgPayloads + iPayload; pPayload->fCached = FALSE; } if (BURN_PACKAGE_TYPE_MSI == pPackage->type) { for (DWORD iFeature = 0; iFeature < pPackage->Msi.cFeatures; ++iFeature) { BURN_MSIFEATURE* pFeature = pPackage->Msi.rgFeatures + iFeature; pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN; } } else if (BURN_PACKAGE_TYPE_MSP == pPackage->type) { ReleaseNullMem(pPackage->Msp.rgTargetProducts); pPackage->Msp.cTargetProductCodes = 0; } } for (DWORD iPatchInfo = 0; iPatchInfo < pPackages->cPatchInfo; ++iPatchInfo) { MSIPATCHSEQUENCEINFOW* pPatchInfo = pPackages->rgPatchInfo + iPatchInfo; pPatchInfo->dwOrder = 0; pPatchInfo->uStatus = 0; } }
static HRESULT UninstallCertificatePackage( __in HCERTSTORE hStore, __in BOOL fUserCertificateStore, __in LPCWSTR wzName ) { HRESULT hr = S_OK; DWORD er = ERROR_SUCCESS; PCCERT_CONTEXT pCertContext = NULL; CRYPT_KEY_PROV_INFO* pPrivateKeyInfo = NULL; DWORD cbPrivateKeyInfo = 0; LPWSTR pwzUniquePrefix = NULL; int ccUniquePrefix = 0; hr = StrAllocFormatted(&pwzUniquePrefix, L"%s_wixCert_", wzName); ExitOnFailure(hr, "Failed to format unique name"); ccUniquePrefix = ::lstrlenW(pwzUniquePrefix); WcaLog(LOGMSG_STANDARD, "Deleting certificate that begin with friendly name: %ls", pwzUniquePrefix); // Loop through all certificates in the store, deleting the ones that begin with our prefix. while (pCertContext = ::CertFindCertificateInStore(hStore, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, pCertContext)) { WCHAR wzFriendlyName[256] = { 0 }; DWORD cbFriendlyName = sizeof(wzFriendlyName); if (::CertGetCertificateContextProperty(pCertContext, CERT_FRIENDLY_NAME_PROP_ID, reinterpret_cast<BYTE*>(wzFriendlyName), &cbFriendlyName) && lstrlenW(wzFriendlyName) >= ccUniquePrefix && CSTR_EQUAL == ::CompareStringW(LOCALE_SYSTEM_DEFAULT, 0, pwzUniquePrefix, ccUniquePrefix, wzFriendlyName, ccUniquePrefix)) { PCCERT_CONTEXT pCertContextDelete = ::CertDuplicateCertificateContext(pCertContext); // duplicate the context so we can delete it with out disrupting the looping if(pCertContextDelete) { // Delete the certificate and if successful delete the matching private key as well. if (::CertDeleteCertificateFromStore(pCertContextDelete)) { // If we found private key info, delete it. hr = CertReadProperty(pCertContextDelete, CERT_KEY_PROV_INFO_PROP_ID, &pPrivateKeyInfo, NULL); if (SUCCEEDED(hr)) { HCRYPTPROV hProvIgnored = NULL; // ignored on deletes. DWORD dwKeyset = fUserCertificateStore ? CRYPT_USER_KEYSET : CRYPT_MACHINE_KEYSET; if (!::CryptAcquireContextW(&hProvIgnored, pPrivateKeyInfo->pwszContainerName, pPrivateKeyInfo->pwszProvName, pPrivateKeyInfo->dwProvType, dwKeyset | CRYPT_DELETEKEYSET | CRYPT_SILENT)) { er = ::GetLastError(); hr = HRESULT_FROM_WIN32(er); } ReleaseNullMem(pPrivateKeyInfo); } else // don't worry about failures to delete private keys. { hr = S_OK; } } else { er = ::GetLastError(); hr = HRESULT_FROM_WIN32(er); } if (FAILED(hr)) { WcaLog(LOGMSG_STANDARD, "Failed to delete certificate with friendly name: %ls, continuing anyway. Error: 0x%x", wzFriendlyName, hr); } pCertContextDelete = NULL; } } } hr = WcaProgressMessage(COST_CERT_DELETE, FALSE); ExitOnFailure(hr, "Failed to send uninstall progress message."); LExit: ReleaseStr(pwzUniquePrefix); ReleaseMem(pPrivateKeyInfo); if(pCertContext) { ::CertFreeCertificateContext(pCertContext); } return hr; }
HRESULT LegacySyncSetProduct( __in CFGDB_STRUCT *pcdb, __inout LEGACY_SYNC_SESSION *pSyncSession, __in LPCWSTR wzName ) { HRESULT hr = S_OK; LEGACY_SYNC_PRODUCT_SESSION *pSyncProductSession = &pSyncSession->syncProductSession; LEGACY_FILE *pFile = NULL; LEGACY_FILE_SPECIAL *pFileSpecial = NULL; LEGACY_INI_FILE *pIniFile = NULL; CONFIG_VALUE cvManifestContents = { }; CONFIG_VALUE cvManifestConvertedToBlob = { }; SCE_ROW_HANDLE sceManifestValueRow = NULL; LPWSTR sczManifestValueName = NULL; BOOL fWasRegistered = FALSE; BYTE *pbManifestBuffer = NULL; SIZE_T iManifestBuffer = 0; LPWSTR sczBlobManifestAsString = NULL; if (pSyncSession->fInSceTransaction) { SceRollbackTransaction(pcdb->psceDb); pSyncSession->fInSceTransaction = FALSE; } hr = ProductGetLegacyManifestValueName(wzName, &sczManifestValueName); ExitOnFailure(hr, "Failed to get legacy manifest value name"); ManifestFreeProductStruct(&pSyncProductSession->product); ZeroMemory(&pSyncProductSession->product, sizeof(pSyncProductSession->product)); for (DWORD i = 0; i < pSyncProductSession->cIniFiles; ++i) { IniFree(pSyncProductSession->rgIniFiles + i); } ReleaseNullMem(pSyncProductSession->rgIniFiles); pSyncProductSession->cIniFiles = 0; ReleaseNullDict(pSyncProductSession->shDictValuesSeen); ReleaseNullDict(pSyncProductSession->shIniFilesByNamespace); hr = DictCreateStringList(&pSyncProductSession->shDictValuesSeen, 0, DICT_FLAG_CASEINSENSITIVE); ExitOnFailure(hr, "Failed to create dictionary of values seen"); hr = DictCreateWithEmbeddedKey(&pSyncProductSession->shIniFilesByNamespace, 0, reinterpret_cast<void **>(&pSyncProductSession->rgIniFiles), offsetof(LEGACY_INI_FILE, sczNamespace), DICT_FLAG_CASEINSENSITIVE); ExitOnFailure(hr, "Failed to create ini file dictionary"); hr = DictCreateWithEmbeddedKey(&pSyncProductSession->product.detect.shCachedDetectionPropertyValues, offsetof(LEGACY_CACHED_DETECTION_RESULT, sczPropertyName), reinterpret_cast<void **>(&pSyncProductSession->product.detect.rgCachedDetectionProperties), 0, DICT_FLAG_CASEINSENSITIVE); ExitOnFailure(hr, "Failed to create cached detection property values dictionary"); hr = ValueFindRow(pcdb, pcdb->dwCfgAppID, sczManifestValueName, &sceManifestValueRow); ExitOnFailure(hr, "Failed to find config value for legacy manifest (AppID: %u, Config Value named: %ls)", pcdb->dwCfgAppID, sczManifestValueName); hr = ValueRead(pcdb, sceManifestValueRow, &cvManifestContents); ExitOnFailure(hr, "Failed to read manifest contents"); // TODO: someday remove this temporary conversion code when we feel confident nobody has old databases with old manifests laying around if (VALUE_STRING == cvManifestContents.cvType) { LogStringLine(REPORT_STANDARD, "Converting manifest value named %ls from string to blob value", sczManifestValueName); hr = ValueSetBlob(reinterpret_cast<BYTE *>(cvManifestContents.string.sczValue), lstrlenW(cvManifestContents.string.sczValue) * sizeof(WCHAR), FALSE, NULL, pcdb->sczGuid, &cvManifestConvertedToBlob); ExitOnFailure(hr, "Failed to set converted manifest value in memory"); hr = ValueWrite(pcdb, pcdb->dwCfgAppID, sczManifestValueName, &cvManifestConvertedToBlob, TRUE, NULL); ExitOnFailure(hr, "Failed to set converted manifest blob: %ls", sczManifestValueName); ReleaseNullSceRow(sceManifestValueRow); ReleaseNullCfgValue(cvManifestContents); hr = ValueFindRow(pcdb, pcdb->dwCfgAppID, sczManifestValueName, &sceManifestValueRow); ExitOnFailure(hr, "Failed to find config value for legacy manifest after conversion (AppID: %u, Config Value named: %ls)", pcdb->dwCfgAppID, sczManifestValueName); hr = ValueRead(pcdb, sceManifestValueRow, &cvManifestContents); ExitOnFailure(hr, "Failed to read converted manifest contents"); } if (VALUE_BLOB != cvManifestContents.cvType) { hr = HRESULT_FROM_WIN32(ERROR_DATATYPE_MISMATCH); ExitOnFailure(hr, "Stored manifest value was not of type blob"); } if (CFG_BLOB_DB_STREAM != cvManifestContents.blob.cbType) { hr = HRESULT_FROM_WIN32(ERROR_DATATYPE_MISMATCH); ExitOnFailure(hr, "Stored manifest blob was not a database stream"); } hr = StreamRead(pcdb, cvManifestContents.blob.dbstream.dwContentID, NULL, &pbManifestBuffer, &iManifestBuffer); ExitOnFailure(hr, "Failed to get binary content of blob named: %ls, with content ID: %u", sczManifestValueName, pcdb->dwCfgAppID); hr = StrAllocString(&sczBlobManifestAsString, reinterpret_cast<LPWSTR>(pbManifestBuffer), iManifestBuffer / sizeof(WCHAR)); ExitOnFailure(hr, "Failed to add null terminator to manifest blob"); hr = ParseManifest(sczBlobManifestAsString, &pSyncProductSession->product); ExitOnFailure(hr, "Failed to parse manifest"); hr = ProductSet(pcdb, wzName, wzLegacyVersion, wzLegacyPublicKey, FALSE, NULL); ExitOnFailure(hr, "Failed to set product"); hr = ProductIsRegistered(pcdb, pcdb->sczProductName, wzLegacyVersion, wzLegacyPublicKey, &fWasRegistered); ExitOnFailure(hr, "Failed to check if product is registered"); hr = DetectProduct(pcdb, !pSyncSession->fDetect, &pSyncSession->arpProducts, &pSyncSession->exeProducts, pSyncProductSession); ExitOnFailure(hr, "Failed to detect product with AppID: %u", pcdb->dwAppID); // Don't bother writing new registration state data to the database if detect is disabled if (pSyncSession->fDetect) { hr = UpdateProductRegistrationState(pcdb, pSyncProductSession, pcdb->sczProductName, wzLegacyVersion, wzLegacyPublicKey); ExitOnFailure(hr, "Failed to update product registration state"); } hr = ProductIsRegistered(pcdb, pcdb->sczProductName, wzLegacyVersion, wzLegacyPublicKey, &pSyncProductSession->fRegistered); ExitOnFailure(hr, "Failed to check if product is registered"); pSyncProductSession->fNewlyRegistered = (!fWasRegistered && pSyncProductSession->fRegistered); for (DWORD i = 0; i < pSyncProductSession->product.cFiles; ++i) { pFile = pSyncProductSession->product.rgFiles + i; hr = UtilExpandLegacyPath(pFile->sczLocation, &pSyncProductSession->product.detect, &pFile->sczExpandedPath); if (E_NOTFOUND == hr) { hr = S_OK; } if (NULL != pFile->sczExpandedPath) { for (DWORD j = 0; j < pFile->cFileSpecials; ++j) { pFileSpecial = pFile->rgFileSpecials + j; if (0 < pFileSpecial->cIniInfo) { hr = MemEnsureArraySize(reinterpret_cast<void **>(&pSyncProductSession->rgIniFiles), pSyncProductSession->cIniFiles + 1, sizeof(LEGACY_INI_FILE), 5); ExitOnFailure(hr, "Failed to grow active IniFiles array"); pIniFile = pSyncProductSession->rgIniFiles + pSyncProductSession->cIniFiles; hr = IniFileOpen(pFile, pFileSpecial, pFileSpecial->rgIniInfo, pIniFile); ExitOnFailure(hr, "Failed to parse INI file"); hr = DictAddValue(pSyncProductSession->shIniFilesByNamespace, pIniFile); ExitOnFailure(hr, "Failed to add INI file to dict for namespace: %ls", pIniFile->sczNamespace); ++pSyncProductSession->cIniFiles; } } } } // IMPORTANT: Put all legacy database actions into a separate transaction // for each product. In the case of any kind of failure, it's OK to leave // changes written to local machine registry / file system, but not the // legacy database - they'll just appear as local machine changes on next // sync, and everything will be happy. The other way around is NOT happy, // because every sync would create another history entry, ad infinitum! hr = SceBeginTransaction(pcdb->psceDb); ExitOnFailure(hr, "Failed to begin transaction"); pSyncSession->fInSceTransaction = TRUE; LExit: ReleaseSceRow(sceManifestValueRow); ReleaseCfgValue(cvManifestContents); ReleaseCfgValue(cvManifestConvertedToBlob); ReleaseStr(sczManifestValueName); ReleaseMem(pbManifestBuffer); ReleaseStr(sczBlobManifestAsString); return hr; }
/****************************************************************** CaExecServiceConfig - entry point for ServiceConfig Custom Action. NOTE: deferred CustomAction since it modifies the machine NOTE: CustomActionData == wzServiceName\tfNewService\twzFirstFailureActionType\twzSecondFailureActionType\twzThirdFailureActionType\tdwResetPeriodInDays\tdwRestartServiceDelayInSeconds\twzProgramCommandLine\twzRebootMessage\twzServiceName\tfNewService\t... *******************************************************************/ extern "C" UINT __stdcall ExecServiceConfig( __in MSIHANDLE hInstall ) { //AssertSz(FALSE, "debug ExecServiceConfig"); HRESULT hr = S_OK; DWORD er = 0; LPWSTR pwzCustomActionData = NULL; LPWSTR pwz = NULL; LPWSTR pwzScriptKey = NULL; WCA_CASCRIPT_HANDLE hRollbackScript = NULL; LPWSTR pwzServiceName = NULL; BOOL fNewService = FALSE; LPWSTR pwzFirstFailureActionType = NULL; LPWSTR pwzSecondFailureActionType = NULL; LPWSTR pwzThirdFailureActionType = NULL; LPWSTR pwzProgramCommandLine = NULL; LPWSTR pwzRebootMessage = NULL; DWORD dwResetPeriodInDays = 0; DWORD dwRestartServiceDelayInSeconds = 0; LPVOID lpMsgBuf = NULL; SC_HANDLE hSCM = NULL; SC_HANDLE hService = NULL; DWORD dwRestartDelay = 0; WCHAR wzActionName[32] = { 0 }; DWORD cbExistingServiceConfig = 0; SERVICE_FAILURE_ACTIONSW* psfa = NULL; // initialize hr = WcaInitialize(hInstall, "ExecServiceConfig"); ExitOnFailure(hr, "failed to initialize"); // Open the Services Control Manager up front. hSCM = ::OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT); if (NULL == hSCM) { er = ::GetLastError(); hr = HRESULT_FROM_WIN32(er); #pragma prefast(push) #pragma prefast(disable:25028) ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, er, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&lpMsgBuf, 0, NULL); #pragma prefast(pop) ExitOnFailure1(hr, "Failed to get handle to SCM. Error: %ls", (LPWSTR)lpMsgBuf); } // First, get the script key out of the CustomActionData and // use that to create the rollback script for this action. hr = WcaGetProperty( L"CustomActionData", &pwzCustomActionData); ExitOnFailure(hr, "failed to get CustomActionData"); WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData); pwz = pwzCustomActionData; hr = WcaReadStringFromCaData(&pwz, &pwzScriptKey); if (!pwzScriptKey) { hr = E_UNEXPECTED; ExitOnFailure(hr, "Failed due to unexpected CustomActionData passed."); } ExitOnFailure(hr, "Failed to read encoding key from CustomActionData."); hr = WcaCaScriptCreate(WCA_ACTION_INSTALL, WCA_CASCRIPT_ROLLBACK, FALSE, pwzScriptKey, FALSE, &hRollbackScript); ExitOnFailure(hr, "Failed to open rollback CustomAction script."); // Next, loop through the rest of the CustomActionData, processing // each service config row in turn. while (pwz && *pwz) { hr = WcaReadStringFromCaData(&pwz, &pwzServiceName); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&fNewService)); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzFirstFailureActionType); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzSecondFailureActionType); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzThirdFailureActionType); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwResetPeriodInDays)); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwRestartServiceDelayInSeconds)); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzProgramCommandLine); ExitOnFailure(hr, "failed to process CustomActionData"); hr = WcaReadStringFromCaData(&pwz, &pwzRebootMessage); ExitOnFailure(hr, "failed to process CustomActionData"); WcaLog(LOGMSG_VERBOSE, "Configuring Service: %ls", pwzServiceName); // Open the handle with all the permissions we might need: // SERVICE_QUERY_CONFIG is needed for QueryServiceConfig2(). // SERVICE_CHANGE_CONFIG is needed for ChangeServiceConfig2(). // SERVICE_START is required in order to handle SC_ACTION_RESTART action. hr = GetService(hSCM, pwzServiceName, SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG | SERVICE_START, &hService); ExitOnFailure1(hr, "Failed to get service: %ls", pwzServiceName); // If we are configuring a service that existed on the machine, we need to // read the existing service configuration and write it out to the rollback // log so rollback can put it back if anything goes wrong. if (!fNewService) { // First, read the existing service config. if (!::QueryServiceConfig2W(hService, SERVICE_CONFIG_FAILURE_ACTIONS, NULL, 0, &cbExistingServiceConfig) && ERROR_INSUFFICIENT_BUFFER != ::GetLastError()) { ExitWithLastError(hr, "Failed to get current service config info."); } psfa = static_cast<LPSERVICE_FAILURE_ACTIONSW>(MemAlloc(cbExistingServiceConfig, TRUE)); ExitOnNull(psfa, hr, E_OUTOFMEMORY, "failed to allocate memory for service failure actions."); if (!::QueryServiceConfig2W(hService, SERVICE_CONFIG_FAILURE_ACTIONS, (LPBYTE)psfa, cbExistingServiceConfig, &cbExistingServiceConfig)) { ExitOnLastError(hr, "failed to Query Service."); } // Build up rollback log so we can restore service state if necessary hr = WcaCaScriptWriteString(hRollbackScript, pwzServiceName); ExitOnFailure(hr, "Failed to add service name to Rollback Log"); // If this service struct is empty, fill in default values if (3 > psfa->cActions) { hr = WcaCaScriptWriteString(hRollbackScript, c_wzActionTypeNone); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); hr = WcaCaScriptWriteString(hRollbackScript, c_wzActionTypeNone); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); hr = WcaCaScriptWriteString(hRollbackScript, c_wzActionTypeNone); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); } else { // psfa actually had actions defined, so use the first three. for (int i = 0; i < 3; ++i) { hr = GetSCActionTypeString(psfa->lpsaActions[i].Type, wzActionName, countof(wzActionName)); ExitOnFailure(hr, "failed to query SFA object"); if (SC_ACTION_RESTART == psfa->lpsaActions[i].Type) { dwRestartDelay = psfa->lpsaActions[i].Delay / 1000; } hr = WcaCaScriptWriteString(hRollbackScript, wzActionName); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); } } hr = WcaCaScriptWriteNumber(hRollbackScript, psfa->dwResetPeriod / (24 * 60 * 60)); ExitOnFailure(hr, "failed to add data to CustomActionData"); hr = WcaCaScriptWriteNumber(hRollbackScript, dwRestartDelay); ExitOnFailure(hr, "failed to add data to CustomActionData"); // Handle the null cases. if (!psfa->lpCommand) { psfa->lpCommand = L""; } hr = WcaCaScriptWriteString(hRollbackScript, psfa->lpCommand); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); // Handle the null cases. if (!psfa->lpRebootMsg) { psfa->lpRebootMsg = L""; } hr = WcaCaScriptWriteString(hRollbackScript, psfa->lpRebootMsg); ExitOnFailure(hr, "failed to add data to Rollback CustomActionData"); // Nudge the system to get all our rollback data written to disk. WcaCaScriptFlush(hRollbackScript); ReleaseNullMem(psfa); } hr = ConfigureService(hSCM, hService, pwzServiceName, dwRestartServiceDelayInSeconds, pwzFirstFailureActionType, pwzSecondFailureActionType, pwzThirdFailureActionType, dwResetPeriodInDays, pwzRebootMessage, pwzProgramCommandLine); ExitOnFailure1(hr, "Failed to configure service: %ls", pwzServiceName); hr = WcaProgressMessage(COST_SERVICECONFIG, FALSE); ExitOnFailure(hr, "failed to send progress message"); // Per-service cleanup ::CloseServiceHandle(hService); hService = NULL; dwResetPeriodInDays = 0; dwRestartServiceDelayInSeconds = 0; } LExit: WcaCaScriptClose(hRollbackScript, WCA_CASCRIPT_CLOSE_PRESERVE); if (lpMsgBuf) { ::LocalFree(lpMsgBuf); } if (hService) { ::CloseServiceHandle(hService); } if (hSCM) { ::CloseServiceHandle(hSCM); } ReleaseMem(psfa); ReleaseStr(pwzRebootMessage); ReleaseStr(pwzProgramCommandLine); ReleaseStr(pwzThirdFailureActionType); ReleaseStr(pwzSecondFailureActionType); ReleaseStr(pwzFirstFailureActionType); ReleaseStr(pwzServiceName); ReleaseStr(pwzScriptKey); ReleaseStr(pwzCustomActionData); er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; return WcaFinalize(er); }