Beispiel #1
0
/********************************************************************
WcaGetFormattedString - gets a formatted string value from
the active install

********************************************************************/
extern "C" HRESULT WIXAPI WcaGetFormattedString(
    __in_z LPCWSTR wzString,
    __out LPWSTR* ppwzData
    )
{
    if (!wzString || !*wzString || !ppwzData)
    {
        return E_INVALIDARG;
    }

    HRESULT hr = S_OK;
    UINT er = ERROR_SUCCESS;
    PMSIHANDLE hRecord = ::MsiCreateRecord(1);
    DWORD_PTR cch = 0;

    er = ::MsiRecordSetStringW(hRecord, 0, wzString);
    ExitOnWin32Error1(er, hr, "Failed to set record field 0 with '%ls'", wzString);

    if (!*ppwzData)
    {
        WCHAR szEmpty[1] = L"";
        er = ::MsiFormatRecordW(WcaGetInstallHandle(), hRecord, szEmpty, (DWORD *)&cch);
        if (ERROR_MORE_DATA == er || ERROR_SUCCESS == er)
        {
            hr = StrAlloc(ppwzData, ++cch);
        }
        else
        {
            hr = HRESULT_FROM_WIN32(er);
        }
        ExitOnFailure1(hr, "Failed to allocate string for formatted string: '%ls'", wzString);
    }
    else
    {
        hr = StrMaxLength(*ppwzData, &cch);
        ExitOnFailure(hr, "Failed to get previous size of property data string");
    }

    er = ::MsiFormatRecordW(WcaGetInstallHandle(), hRecord, *ppwzData, (DWORD *)&cch);
    if (ERROR_MORE_DATA == er)
    {
        hr = StrAlloc(ppwzData, ++cch);
        ExitOnFailure1(hr, "Failed to allocate string for formatted string: '%ls'", wzString);

        er = ::MsiFormatRecordW(WcaGetInstallHandle(), hRecord, *ppwzData, (DWORD *)&cch);
    }
    ExitOnWin32Error1(er, hr, "Failed to get formatted string: '%ls'", wzString);

LExit:
    return hr;
}
Beispiel #2
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 #3
0
/********************************************************************
WcaGetTargetPath - gets the target path for a specified folder

********************************************************************/
extern "C" HRESULT WIXAPI WcaGetTargetPath(
    __in_z LPCWSTR wzFolder,
    __out LPWSTR* ppwzData
    )
{
    if (!wzFolder || !*wzFolder || !ppwzData)
        return E_INVALIDARG;

    HRESULT hr = S_OK;

    UINT er = ERROR_SUCCESS;
    DWORD_PTR cch = 0;

    if (!*ppwzData)
    {
        WCHAR szEmpty[1] = L"";
        er = ::MsiGetTargetPathW(WcaGetInstallHandle(), wzFolder, szEmpty, (DWORD*)&cch);
        if (ERROR_MORE_DATA == er || ERROR_SUCCESS == er)
        {
            ++cch; //Add one for the null terminator
            hr = StrAlloc(ppwzData, cch);
        }
        else
        {
            hr = HRESULT_FROM_WIN32(er);
        }
        ExitOnFailure1(hr, "Failed to allocate string for target path of folder: '%ls'", wzFolder);
    }
    else
    {
        hr = StrMaxLength(*ppwzData, &cch);
        ExitOnFailure(hr, "Failed to get previous size of string");
    }

    er = ::MsiGetTargetPathW(WcaGetInstallHandle(), wzFolder, *ppwzData, (DWORD*)&cch);
    if (ERROR_MORE_DATA == er)
    {
        ++cch;
        hr = StrAlloc(ppwzData, cch);
        ExitOnFailure1(hr, "Failed to allocate string for target path of folder: '%ls'", wzFolder);

        er = ::MsiGetTargetPathW(WcaGetInstallHandle(), wzFolder, *ppwzData, (DWORD*)&cch);
    }
    ExitOnWin32Error1(er, hr, "Failed to get target path for folder '%ls'", wzFolder);

LExit:
    return hr;
}
Beispiel #4
0
/********************************************************************
WcaGetProperty - gets a string property value from the active install

********************************************************************/
extern "C" HRESULT WIXAPI WcaGetProperty(
    __in_z LPCWSTR wzProperty,
    __inout LPWSTR* ppwzData
    )
{
    if (!wzProperty || !*wzProperty || !ppwzData)
    {
        return E_INVALIDARG;
    }

    HRESULT hr = S_OK;
    UINT er = ERROR_SUCCESS;
    DWORD_PTR cch = 0;

    if (!*ppwzData)
    {
        WCHAR szEmpty[1] = L"";
        er = ::MsiGetPropertyW(WcaGetInstallHandle(), wzProperty, szEmpty, (DWORD *)&cch);
        if (ERROR_MORE_DATA == er || ERROR_SUCCESS == er)
        {
            hr = StrAlloc(ppwzData, ++cch);
        }
        else
        {
            hr = HRESULT_FROM_WIN32(er);
        }
        ExitOnFailure1(hr, "Failed to allocate string for Property '%ls'", wzProperty);
    }
    else
    {
        hr = StrMaxLength(*ppwzData, &cch);
        ExitOnFailure(hr, "Failed to get previous size of property data string.");
    }

    er = ::MsiGetPropertyW(WcaGetInstallHandle(), wzProperty, *ppwzData, (DWORD *)&cch);
    if (ERROR_MORE_DATA == er)
    {
        Assert(*ppwzData);
        hr = StrAlloc(ppwzData, ++cch);
        ExitOnFailure1(hr, "Failed to allocate string for Property '%ls'", wzProperty);

        er = ::MsiGetPropertyW(WcaGetInstallHandle(), wzProperty, *ppwzData, (DWORD *)&cch);
    }
    ExitOnWin32Error1(er, hr, "Failed to get data for property '%ls'", wzProperty);

LExit:
    return hr;
}
Beispiel #5
0
/********************************************************************
WcaSetProperty - sets a string property value in the active install

********************************************************************/
extern "C" HRESULT WIXAPI WcaSetProperty(
    __in_z LPCWSTR wzPropertyName,
    __in_z LPCWSTR wzPropertyValue
    )
{
    HRESULT hr = S_OK;

    if (!wzPropertyName || !*wzPropertyName || !wzPropertyValue)
        return E_INVALIDARG;

    UINT er = ::MsiSetPropertyW(WcaGetInstallHandle(), wzPropertyName, wzPropertyValue);
    ExitOnWin32Error1(er, hr, "failed to set property: %ls", wzPropertyName);

LExit:
    return hr;
}
Beispiel #6
0
/********************************************************************
WcaOpenView() - opens a view on the installing database

********************************************************************/
extern "C" HRESULT WIXAPI WcaOpenView(
    __in_z LPCWSTR wzSql,
    __out MSIHANDLE* phView
    )
{
    if (!wzSql || !*wzSql|| !phView)
    {
        return E_INVALIDARG;
    }

    HRESULT hr = S_OK;
    UINT er = ::MsiDatabaseOpenViewW(WcaGetDatabaseHandle(), wzSql, phView);
    ExitOnWin32Error1(er, hr, "failed to open view on database with SQL: %ls", wzSql);

LExit:
    return hr;
}
Beispiel #7
0
/********************************************************************
WcaSetIntProperty - sets a integer property value in the active install

********************************************************************/
extern "C" HRESULT WIXAPI WcaSetIntProperty(
    __in_z LPCWSTR wzPropertyName,
    __in int nPropertyValue
    )
{
    if (!wzPropertyName || !*wzPropertyName)
        return E_INVALIDARG;

    // 12 characters should be enough for a 32-bit int: 10 digits, 1 sign, 1 null
    WCHAR wzPropertyValue[13];
    HRESULT hr = StringCchPrintfW(wzPropertyValue, countof(wzPropertyValue), L"%d", nPropertyValue);
    ExitOnFailure1(hr, "failed to convert into string property value: %d", nPropertyValue);

    UINT er = ::MsiSetPropertyW(WcaGetInstallHandle(), wzPropertyName, wzPropertyValue);
    ExitOnWin32Error1(er, hr, "failed to set property: %ls", wzPropertyName);

LExit:
    return hr;
}
Beispiel #8
0
/********************************************************************
WcaGetIntProperty - gets an integer property value from the active install

********************************************************************/
extern "C" HRESULT WIXAPI WcaGetIntProperty(
    __in_z LPCWSTR wzProperty,
    __inout int* piData
    )
{
    if (!piData)
        return E_INVALIDARG;

    HRESULT hr = S_OK;
    UINT er;

    WCHAR wzValue[32];
    DWORD cch = countof(wzValue) - 1;

    er = ::MsiGetPropertyW(WcaGetInstallHandle(), wzProperty, wzValue, &cch);
    ExitOnWin32Error1(er, hr, "Failed to get data for property '%ls'", wzProperty);

    *piData = wcstol(wzValue, NULL, 10);

LExit:
    return hr;
}
Beispiel #9
0
/******************************************************************
 SchedNetFx - entry point for NetFx Custom Action

********************************************************************/
extern "C" UINT __stdcall SchedNetFx(
    __in MSIHANDLE hInstall
    )
{
    // AssertSz(FALSE, "debug SchedNetFx");

    HRESULT hr = S_OK;
    UINT er = ERROR_SUCCESS;

    LPWSTR pwzInstallCustomActionData = NULL;
    LPWSTR pwzUninstallCustomActionData = NULL;
    UINT uiCost = 0;

    PMSIHANDLE hView = NULL;
    PMSIHANDLE hRec = NULL;
    PMSIHANDLE hViewGac = NULL;
    PMSIHANDLE hRecGac = NULL;

    LPWSTR pwzId = NULL;
    LPWSTR pwzData = NULL;
    LPWSTR pwzTemp = NULL;
    LPWSTR pwzFile = NULL;
    int iPriority = 0;
    int iAssemblyCost = 0;
    int iAttributes = 0;
    LPWSTR pwzFileApp = NULL;
    LPWSTR pwzDirAppBase = NULL;
    LPWSTR pwzComponent = NULL;

    INSTALLSTATE isInstalled;
    INSTALLSTATE isAction;

    LPWSTR pwz32Ngen = NULL;
    LPWSTR pwz64Ngen = NULL;

    BOOL f32NgenExeExists = FALSE;
    BOOL f64NgenExeExists = FALSE;

    BOOL fNeedInstallUpdate32 = FALSE;
    BOOL fNeedUninstallUpdate32 = FALSE;
    BOOL fNeedInstallUpdate64 = FALSE;
    BOOL fNeedUninstallUpdate64 = FALSE;

    // initialize
    hr = WcaInitialize(hInstall, "SchedNetFx");
    ExitOnFailure(hr, "failed to initialize");

    hr = GetNgenPath(&pwz32Ngen, FALSE);
    f32NgenExeExists = SUCCEEDED(hr);
    if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr)
    {
        hr = ERROR_SUCCESS;
        WcaLog(LOGMSG_STANDARD, "Failed to find 32bit ngen. No actions will be scheduled to create native images for 32bit.");
    }
    ExitOnFailure(hr, "failed to get 32bit ngen.exe path");

    hr = GetNgenPath(&pwz64Ngen, TRUE);
    f64NgenExeExists = SUCCEEDED(hr);
    if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr)
    {
        hr = ERROR_SUCCESS;
        WcaLog(LOGMSG_STANDARD, "Failed to find 64bit ngen. No actions will be scheduled to create native images for 64bit.");
    }
    ExitOnFailure(hr, "failed to get 64bit ngen.exe path");

    // loop through all the NetFx records
    hr = WcaOpenExecuteView(vcsNgenQuery, &hView);
    ExitOnFailure(hr, "failed to open view on NetFxNativeImage table");

    while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
    {
        // Get Id
        hr = WcaGetRecordString(hRec, ngqId, &pwzId);
        ExitOnFailure(hr, "failed to get NetFxNativeImage.NetFxNativeImage");

        // Get File
        hr = WcaGetRecordString(hRec, ngqFile, &pwzData);
        ExitOnFailure1(hr, "failed to get NetFxNativeImage.File_ for record: %ls", pwzId);
        hr = StrAllocFormatted(&pwzTemp, vpwzUnformattedQuotedFile, pwzData);
        ExitOnFailure1(hr, "failed to format file string for file: %ls", pwzData);
        hr = WcaGetFormattedString(pwzTemp, &pwzFile);
        ExitOnFailure1(hr, "failed to get formatted string for file: %ls", pwzData);

        // Get Priority
        hr = WcaGetRecordInteger(hRec, ngqPriority, &iPriority);
        ExitOnFailure1(hr, "failed to get NetFxNativeImage.Priority for record: %ls", pwzId);

        if (0 == iPriority)
            iAssemblyCost = COST_NGEN_BLOCKING;
        else
            iAssemblyCost = COST_NGEN_NONBLOCKING;

        // Get Attributes
        hr = WcaGetRecordInteger(hRec, ngqAttributes, &iAttributes);
        ExitOnFailure1(hr, "failed to get NetFxNativeImage.Attributes for record: %ls", pwzId);

        // Get File_Application or leave pwzFileApp NULL.
        hr = WcaGetRecordFormattedString(hRec, ngqFileApp, &pwzData);
        ExitOnFailure1(hr, "failed to get NetFxNativeImage.File_Application for record: %ls", pwzId);

        // Check if the value resolves to a valid file ID.
        if (S_OK == FileIdExists(pwzData))
        {
            // Resolve the file ID to a path.
            hr = StrAllocFormatted(&pwzTemp, vpwzUnformattedQuotedFile, pwzData);
            ExitOnFailure1(hr, "failed to format file application string for file: %ls", pwzData);

            hr = WcaGetFormattedString(pwzTemp, &pwzFileApp);
            ExitOnFailure1(hr, "failed to get formatted string for file application: %ls", pwzData);
        }
        else
        {
            // Assume record formatted to a path already.
            hr = StrAllocString(&pwzFileApp, pwzData, 0);
            ExitOnFailure1(hr, "failed to allocate string for file path: %ls", pwzData);

            hr = PathEnsureQuoted(&pwzFileApp, FALSE);
            ExitOnFailure1(hr, "failed to quote file path: %ls", pwzData);
        }

        // Get Directory_ApplicationBase or leave pwzDirAppBase NULL.
        hr = WcaGetRecordFormattedString(hRec, ngqDirAppBase, &pwzData);
        ExitOnFailure1(hr, "failed to get NetFxNativeImage.Directory_ApplicationBase for record: %ls", pwzId);

        if (WcaIsUnicodePropertySet(pwzData))
        {
            // Resolve the directory ID to a path.
            hr = StrAllocFormatted(&pwzTemp, vpwzUnformattedQuotedDirectory, pwzData);
            ExitOnFailure1(hr, "failed to format directory application base string for property: %ls", pwzData);

            hr = WcaGetFormattedString(pwzTemp, &pwzDirAppBase);
            ExitOnFailure1(hr, "failed to get formatted string for directory application base: %ls", pwzData);
        }
        else
        {
            // Assume record formatted to a path already.
            hr = StrAllocString(&pwzDirAppBase, pwzData, 0);
            ExitOnFailure1(hr, "failed to allocate string for directory path: %ls", pwzData);

            hr = PathEnsureQuoted(&pwzDirAppBase, TRUE);
            ExitOnFailure1(hr, "failed to quote and backslashify directory: %ls", pwzData);
        }

        // Get Component
        hr = WcaGetRecordString(hRec, ngqComponent, &pwzComponent);
        ExitOnFailure1(hr, "failed to get NetFxNativeImage.Directory_ApplicationBase for record: %ls", pwzId);
        er = ::MsiGetComponentStateW(hInstall, pwzComponent, &isInstalled, &isAction);
        ExitOnWin32Error1(er, hr, "failed to get install state for Component: %ls", pwzComponent);

        //
        // Figure out if it's going to be GAC'd.  The possibility exists that no assemblies are going to be GAC'd 
        // so we have to check for the MsiAssembly table first.
        //
        if (S_OK == WcaTableExists(L"MsiAssembly"))
        {
            hr = WcaOpenView(vcsNgenGac, &hViewGac);
            ExitOnFailure(hr, "failed to open view on File/MsiAssembly table");

            hr = WcaExecuteView(hViewGac, hRec);
            ExitOnFailure(hr, "failed to execute view on File/MsiAssembly table");

            hr = WcaFetchSingleRecord(hViewGac, &hRecGac);
            ExitOnFailure(hr, "failed to fetch File_Assembly from File/MsiAssembly table");

            if (S_FALSE != hr)
            {
                hr = WcaGetRecordString(hRecGac, nggApplication, &pwzData);
                ExitOnFailure(hr, "failed to get MsiAssembly.File_Application");

                // If it's in the GAC replace the file name with the strong name
                if (L'\0' == pwzData[0])
                {
                    hr = GetStrongName(&pwzFile, pwzComponent);
                    ExitOnFailure1(hr, "failed to get strong name for component: %ls", pwzData);
                }
            }
        }

        //
        // Schedule the work
        //
        if (!(iAttributes & NGEN_32BIT) && !(iAttributes & NGEN_64BIT))
            ExitOnFailure1(hr = E_INVALIDARG, "Neither 32bit nor 64bit is specified for NGEN of file: %ls", pwzFile);

        if (WcaIsInstalling(isInstalled, isAction) || WcaIsReInstalling(isInstalled, isAction))
        {
            if (iAttributes & NGEN_32BIT && f32NgenExeExists)
            {
                // Assemble the install command line
                hr = CreateInstallCommand(&pwzData, pwz32Ngen, pwzFile, iPriority, iAttributes, pwzFileApp, pwzDirAppBase);
                ExitOnFailure(hr, "failed to create install command line");

                hr = WcaWriteStringToCaData(pwzData, &pwzInstallCustomActionData);
                ExitOnFailure1(hr, "failed to add install command to custom action data: %ls", pwzData);

                hr = WcaWriteIntegerToCaData(iAssemblyCost, &pwzInstallCustomActionData);
                ExitOnFailure1(hr, "failed to add cost to custom action data: %ls", pwzData);

                uiCost += iAssemblyCost;

                fNeedInstallUpdate32 = TRUE;
            }

            if (iAttributes & NGEN_64BIT && f64NgenExeExists)
            {
                // Assemble the install command line
                hr = CreateInstallCommand(&pwzData, pwz64Ngen, pwzFile, iPriority, iAttributes, pwzFileApp, pwzDirAppBase);
                ExitOnFailure(hr, "failed to create install command line");

                hr = WcaWriteStringToCaData(pwzData, &pwzInstallCustomActionData); // command
                ExitOnFailure1(hr, "failed to add install command to custom action data: %ls", pwzData);

                hr = WcaWriteIntegerToCaData(iAssemblyCost, &pwzInstallCustomActionData); // cost
                ExitOnFailure1(hr, "failed to add cost to custom action data: %ls", pwzData);

                uiCost += iAssemblyCost;

                fNeedInstallUpdate64 = TRUE;
            }
        }
        else if (WcaIsUninstalling(isInstalled, isAction))
        {
            if (iAttributes & NGEN_32BIT && f32NgenExeExists)
            {
                hr = StrAllocFormatted(&pwzData, L"%s uninstall %s", pwz32Ngen, pwzFile);
                ExitOnFailure(hr, "failed to create update 32 command line");

                hr = WcaWriteStringToCaData(pwzData, &pwzUninstallCustomActionData); // command
                ExitOnFailure1(hr, "failed to add install command to custom action data: %ls", pwzData);

                hr = WcaWriteIntegerToCaData(COST_NGEN_NONBLOCKING, &pwzUninstallCustomActionData); // cost
                ExitOnFailure1(hr, "failed to add cost to custom action data: %ls", pwzData);

                uiCost += COST_NGEN_NONBLOCKING;

                fNeedUninstallUpdate32 = TRUE;
            }

            if (iAttributes & NGEN_64BIT && f64NgenExeExists)
            {
                hr = StrAllocFormatted(&pwzData, L"%s uninstall %s", pwz64Ngen, pwzFile);
                ExitOnFailure(hr, "failed to create update 64 command line");

                hr = WcaWriteStringToCaData(pwzData, &pwzUninstallCustomActionData); // command
                ExitOnFailure1(hr, "failed to add install command to custom action data: %ls", pwzData);

                hr = WcaWriteIntegerToCaData(COST_NGEN_NONBLOCKING, &pwzUninstallCustomActionData); // cost
                ExitOnFailure1(hr, "failed to add cost to custom action data: %ls", pwzData);

                uiCost += COST_NGEN_NONBLOCKING;

                fNeedUninstallUpdate64 = TRUE;
            }
        }
    }
    if (E_NOMOREITEMS == hr)
        hr = S_OK;
    ExitOnFailure(hr, "failed while looping through all files to create native images for");

    // If we need 32 bit install update
    if (fNeedInstallUpdate32)
    {
        hr = StrAllocFormatted(&pwzData, L"%s update /queue", pwz32Ngen);
        ExitOnFailure(hr, "failed to create install update 32 command line");

        hr = WcaWriteStringToCaData(pwzData, &pwzInstallCustomActionData); // command
        ExitOnFailure1(hr, "failed to add install command to install custom action data: %ls", pwzData);

        hr = WcaWriteIntegerToCaData(COST_NGEN_NONBLOCKING, &pwzInstallCustomActionData); // cost
        ExitOnFailure1(hr, "failed to add cost to install custom action data: %ls", pwzData);

        uiCost += COST_NGEN_NONBLOCKING;
    }

    // If we need 32 bit uninstall update
    if (fNeedUninstallUpdate32)
    {
        hr = StrAllocFormatted(&pwzData, L"%s update /queue", pwz32Ngen);
        ExitOnFailure(hr, "failed to create uninstall update 32 command line");

        hr = WcaWriteStringToCaData(pwzData, &pwzUninstallCustomActionData); // command
        ExitOnFailure1(hr, "failed to add install command to uninstall custom action data: %ls", pwzData);

        hr = WcaWriteIntegerToCaData(COST_NGEN_NONBLOCKING, &pwzUninstallCustomActionData); // cost
        ExitOnFailure1(hr, "failed to add cost to uninstall custom action data: %ls", pwzData);

        uiCost += COST_NGEN_NONBLOCKING;
    }

    // If we need 64 bit install update
    if (fNeedInstallUpdate64)
    {
        hr = StrAllocFormatted(&pwzData, L"%s update /queue", pwz64Ngen);
        ExitOnFailure(hr, "failed to create install update 64 command line");

        hr = WcaWriteStringToCaData(pwzData, &pwzInstallCustomActionData); // command
        ExitOnFailure1(hr, "failed to add install command to install custom action data: %ls", pwzData);

        hr = WcaWriteIntegerToCaData(COST_NGEN_NONBLOCKING, &pwzInstallCustomActionData); // cost
        ExitOnFailure1(hr, "failed to add cost to install custom action data: %ls", pwzData);

        uiCost += COST_NGEN_NONBLOCKING;
    }

    // If we need 64 bit install update
    if (fNeedUninstallUpdate64)
    {
        hr = StrAllocFormatted(&pwzData, L"%s update /queue", pwz64Ngen);
        ExitOnFailure(hr, "failed to create uninstall update 64 command line");

        hr = WcaWriteStringToCaData(pwzData, &pwzUninstallCustomActionData); // command
        ExitOnFailure1(hr, "failed to add install command to uninstall custom action data: %ls", pwzData);

        hr = WcaWriteIntegerToCaData(COST_NGEN_NONBLOCKING, &pwzUninstallCustomActionData); // cost
        ExitOnFailure1(hr, "failed to add cost to uninstall custom action data: %ls", pwzData);

        uiCost += COST_NGEN_NONBLOCKING;
    }

    // Add to progress bar
    if ((pwzInstallCustomActionData && *pwzInstallCustomActionData) || (pwzUninstallCustomActionData && *pwzUninstallCustomActionData))
    {
        hr = WcaProgressMessage(uiCost, TRUE);
        ExitOnFailure(hr, "failed to extend progress bar for NetFxExecuteNativeImage");
    }

    // Schedule the install custom action
    if (pwzInstallCustomActionData && *pwzInstallCustomActionData)
    {
        hr = WcaSetProperty(L"NetFxExecuteNativeImageInstall", pwzInstallCustomActionData);
        ExitOnFailure(hr, "failed to schedule NetFxExecuteNativeImageInstall action");

        hr = WcaSetProperty(L"NetFxExecuteNativeImageCommitInstall", pwzInstallCustomActionData);
        ExitOnFailure(hr, "failed to schedule NetFxExecuteNativeImageCommitInstall action");
    }

    // Schedule the uninstall custom action
    if (pwzUninstallCustomActionData && *pwzUninstallCustomActionData)
    {
        hr = WcaSetProperty(L"NetFxExecuteNativeImageUninstall", pwzUninstallCustomActionData);
        ExitOnFailure(hr, "failed to schedule NetFxExecuteNativeImageUninstall action");

        hr = WcaSetProperty(L"NetFxExecuteNativeImageCommitUninstall", pwzUninstallCustomActionData);
        ExitOnFailure(hr, "failed to schedule NetFxExecuteNativeImageCommitUninstall action");
    }


LExit:
    ReleaseStr(pwzInstallCustomActionData);
    ReleaseStr(pwzUninstallCustomActionData);
    ReleaseStr(pwzId);
    ReleaseStr(pwzData);
    ReleaseStr(pwzTemp);
    ReleaseStr(pwzFile);
    ReleaseStr(pwzFileApp);
    ReleaseStr(pwzDirAppBase);
    ReleaseStr(pwzComponent);
    ReleaseStr(pwz32Ngen);
    ReleaseStr(pwz64Ngen);

    if (FAILED(hr))
        er = ERROR_INSTALL_FAILURE;
    return WcaFinalize(er);
}