Beispiel #1
0
/********************************************************************
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;
}
Beispiel #2
0
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));
}
Beispiel #3
0
/********************************************************************
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;
}
Beispiel #4
0
/******************************************************************
 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;
}
Beispiel #5
0
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;
    }
}
Beispiel #6
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;
    }
}
Beispiel #7
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;
}
Beispiel #8
0
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);
}