Example #1
0
/*******************************************************************
 XmlLoadDocumentFromFile

********************************************************************/
extern "C" HRESULT DAPI XmlLoadDocumentFromFile(
    __in_z LPCWSTR wzPath,
    __out IXMLDOMDocument** ppixdDocument
    )
{
    return XmlLoadDocumentFromFileEx(wzPath, 0, ppixdDocument);
}
Example #2
0
/******************************************************************
 ExecXmlConfig - entry point for XmlConfig Custom Action

*******************************************************************/
extern "C" UINT __stdcall ExecXmlConfig(
    __in MSIHANDLE hInstall
    )
{
    //AssertSz(FALSE, "debug ExecXmlConfig");
    HRESULT hr = S_OK;
    HRESULT hrOpenFailure = S_OK;
    UINT er = ERROR_SUCCESS;

    BOOL fIsWow64Process = FALSE;
    BOOL fIsFSRedirectDisabled = FALSE;
    BOOL fPreserveDate = FALSE;

    LPWSTR pwzCustomActionData = NULL;
    LPWSTR pwzData = NULL;
    LPWSTR pwzFile = NULL;
    LPWSTR pwzElementPath = NULL;
    LPWSTR pwzVerifyPath = NULL;
    LPWSTR pwzName = NULL;
    LPWSTR pwzValue = NULL;
    LPWSTR pwz = NULL;
    int cAdditionalChanges = 0;

    IXMLDOMDocument* pixd = NULL;
    IXMLDOMNode* pixn = NULL;
    IXMLDOMNode* pixnVerify = NULL;
    IXMLDOMNode* pixnNewNode = NULL;
    IXMLDOMNode* pixnRemovedChild = NULL;

    IXMLDOMDocument* pixdNew = NULL;
    IXMLDOMElement* pixeNew = NULL;

    FILETIME ft;

    int id = IDRETRY;

    eXmlAction xa;
    eXmlPreserveDate xd;

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

    hr = XmlInitialize();
    ExitOnFailure(hr, "failed to initialize xml utilities");

    hr = WcaGetProperty( L"CustomActionData", &pwzCustomActionData);
    ExitOnFailure(hr, "failed to get CustomActionData");

    WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData);

    pwz = pwzCustomActionData;

    hr = WcaReadIntegerFromCaData(&pwz, (int*) &xa);
    ExitOnFailure(hr, "failed to process CustomActionData");

    // Initialize the Wow64 API - store the result in fWow64APIPresent
    // If it fails, this doesn't warrant an error yet, because we only need the Wow64 API in some cases
    WcaInitializeWow64();
    fIsWow64Process = WcaIsWow64Process();

    if (xaOpenFile != xa && xaOpenFilex64 != xa)
    {
        ExitOnFailure(hr = E_INVALIDARG, "invalid custom action data");
    }

    // loop through all the passed in data
    while (pwz && *pwz)
    {
        hr = WcaReadStringFromCaData(&pwz, &pwzFile);
        ExitOnFailure(hr, "failed to read file name from custom action data");

        // Default to not preserve date, preserve it if any modifications require us to
        fPreserveDate = FALSE;

        // Open the file
        ReleaseNullObject(pixd);

        if (xaOpenFilex64 == xa)
        {
            if (!fIsWow64Process)
            {
                hr = E_NOTIMPL;
                ExitOnFailure(hr, "Custom action was told to act on a 64-bit component, but the custom action process is not running in WOW.");
            }

            hr = WcaDisableWow64FSRedirection();
            ExitOnFailure(hr, "Custom action was told to act on a 64-bit component, but was unable to disable filesystem redirection through the Wow64 API.");

            fIsFSRedirectDisabled = TRUE;
        }

        hr = XmlLoadDocumentFromFileEx(pwzFile, XML_LOAD_PRESERVE_WHITESPACE, &pixd);
        if (FAILED(hr))
        {
            // Ignore the return code for now.  If they try to add something, we'll fail the install.  If all they do is remove stuff then it doesn't matter.
            hrOpenFailure = hr;
            hr = S_OK;
        }
        else
        {
            hrOpenFailure = S_OK;
        }

        WcaLog(LOGMSG_VERBOSE, "Configuring Xml File: %ls", pwzFile);

        while (pwz && *pwz)
        {
            // If we skip past an element that has additional changes we need to strip them off the stream before
            // moving on to the next element. Do that now and then restart the outer loop.
            if (cAdditionalChanges > 0)
            {
                while (cAdditionalChanges > 0)
                {
                    hr = WcaReadStringFromCaData(&pwz, &pwzName);
                    ExitOnFailure(hr, "failed to process CustomActionData");
                    hr = WcaReadStringFromCaData(&pwz, &pwzValue);
                    ExitOnFailure(hr, "failed to process CustomActionData");

                    cAdditionalChanges--;
                }
                continue;
            }

            hr = WcaReadIntegerFromCaData(&pwz, (int*) &xa);
            ExitOnFailure(hr, "failed to process CustomActionData");

            // Break if we need to move on to a different file
            if (xaOpenFile == xa || xaOpenFilex64 == xa)
            {
                break;
            }

            hr = WcaReadIntegerFromCaData(&pwz, (int*) &xd);
            ExitOnFailure(hr, "failed to process CustomActionData");

            if (xdPreserve == xd)
            {
                fPreserveDate = TRUE;
            }

            // Get path, name, and value to be written
            hr = WcaReadStringFromCaData(&pwz, &pwzElementPath);
            ExitOnFailure(hr, "failed to process CustomActionData");
            hr = WcaReadStringFromCaData(&pwz, &pwzVerifyPath);
            ExitOnFailure(hr, "failed to process CustomActionData");
            hr = WcaReadStringFromCaData(&pwz, &pwzName);
            ExitOnFailure(hr, "failed to process CustomActionData");
            hr = WcaReadStringFromCaData(&pwz, &pwzValue);
            ExitOnFailure(hr, "failed to process CustomActionData");
            hr = WcaReadIntegerFromCaData(&pwz, &cAdditionalChanges);
            ExitOnFailure(hr, "failed to process CustomActionData");

            // If we failed to open the file and we're adding something to the file, we've got a problem.  Otherwise, just continue on since the file's already gone.
            if (FAILED(hrOpenFailure))
            {
                if (xaCreateElement == xa || xaWriteValue == xa || xaWriteDocument == xa)
                {
                    MessageExitOnFailure1(hr = hrOpenFailure, msierrXmlConfigFailedOpen, "failed to load XML file: %ls", pwzFile);
                }
                else
                {
                    continue;
                }
            }

            // Select the node we're about to modify
            ReleaseNullObject(pixn);

            hr = XmlSelectSingleNode(pixd, pwzElementPath, &pixn);

            // If we failed to find the node that we are going to add to, we've got a problem. Otherwise, just continue since the node's already gone.
            if (S_FALSE == hr)
            {
                if (xaCreateElement == xa || xaWriteValue == xa || xaWriteDocument == xa)
                {
                    hr = HRESULT_FROM_WIN32(ERROR_OBJECT_NOT_FOUND);
                }
                else
                {
                    hr = S_OK;
                    continue;
                }
            }

            MessageExitOnFailure2(hr, msierrXmlConfigFailedSelect, "failed to find node: %ls in XML file: %ls", pwzElementPath, pwzFile);

            // Make the modification
            switch (xa)
            {
            case xaWriteValue:
                if (pwzName && *pwzName)
                {
                    // We're setting an attribute
                    hr = XmlSetAttribute(pixn, pwzName, pwzValue);
                    ExitOnFailure2(hr, "failed to set attribute: %ls to value %ls", pwzName, pwzValue);
                }
                else
                {
                    // We're setting the text of the node
                    hr = XmlSetText(pixn, pwzValue);
                    ExitOnFailure2(hr, "failed to set text to: %ls for element %ls.  Make sure that XPath points to an element.", pwzValue, pwzElementPath);
                }
                break;
            case xaWriteDocument:
                if (NULL != pwzVerifyPath && 0 != pwzVerifyPath[0])
                {
                    hr = XmlSelectSingleNode(pixn, pwzVerifyPath, &pixnVerify);
                    if (S_OK == hr)
                    {
                        // We found the verify path which means we have no further work to do
                        continue;
                    }
                    ExitOnFailure1(hr, "failed to query verify path: %ls", pwzVerifyPath);
                }

                hr = XmlLoadDocumentEx(pwzValue, XML_LOAD_PRESERVE_WHITESPACE, &pixdNew);
                ExitOnFailure(hr, "Failed to load value as document.");

                hr = pixdNew->get_documentElement(&pixeNew);
                ExitOnFailure(hr, "Failed to get document element.");

                hr = pixn->appendChild(pixeNew, NULL);
                ExitOnFailure(hr, "Failed to append document element on to parent element.");

                ReleaseNullObject(pixeNew);
                ReleaseNullObject(pixdNew);
                break;

            case xaCreateElement:
                if (NULL != pwzVerifyPath && 0 != pwzVerifyPath[0])
                {
                    hr = XmlSelectSingleNode(pixn, pwzVerifyPath, &pixnVerify);
                    if (S_OK == hr)
                    {
                        // We found the verify path which means we have no further work to do
                        continue;
                    }
                    ExitOnFailure1(hr, "failed to query verify path: %ls", pwzVerifyPath);
                }

                hr = XmlCreateChild(pixn, pwzName, &pixnNewNode);
                ExitOnFailure1(hr, "failed to create child element: %ls", pwzName);

                if (pwzValue && *pwzValue)
                {
                    hr = XmlSetText(pixnNewNode, pwzValue);
                    ExitOnFailure2(hr, "failed to set text to: %ls for node: %ls", pwzValue, pwzName);
                }

                while (cAdditionalChanges > 0)
                {
                    hr = WcaReadStringFromCaData(&pwz, &pwzName);
                    ExitOnFailure(hr, "failed to process CustomActionData");
                    hr = WcaReadStringFromCaData(&pwz, &pwzValue);
                    ExitOnFailure(hr, "failed to process CustomActionData");

                    // Set the additional attribute
                    hr = XmlSetAttribute(pixnNewNode, pwzName, pwzValue);
                    ExitOnFailure2(hr, "failed to set attribute: %ls to value %ls", pwzName, pwzValue);

                    cAdditionalChanges--;
                }

                ReleaseNullObject(pixnNewNode);
                break;
            case xaDeleteValue:
                if (pwzName && *pwzName)
                {
                    // Delete the attribute
                    hr = XmlRemoveAttribute(pixn, pwzName);
                    ExitOnFailure1(hr, "failed to remove attribute: %ls", pwzName);
                }
                else
                {
                    // Clear the text value for the node
                    hr = XmlSetText(pixn, L"");
                    ExitOnFailure(hr, "failed to clear text value");
                }
                break;
            case xaDeleteElement:
                if (NULL != pwzVerifyPath && 0 != pwzVerifyPath[0])
                {
                    hr = XmlSelectSingleNode(pixn, pwzVerifyPath, &pixnVerify);
                    if (S_OK == hr)
                    {
                        hr = pixn->removeChild(pixnVerify, &pixnRemovedChild);
                        ExitOnFailure(hr, "failed to remove created child element");

                        ReleaseNullObject(pixnRemovedChild);
                    }
                    else
                    {
                        WcaLog(LOGMSG_VERBOSE, "Failed to select path %ls for deleting.  Skipping...", pwzVerifyPath);
                        hr = S_OK;
                    }
                }
                else
                {
                    // TODO: This requires a VerifyPath to delete an element.  Should we support not having one?
                    WcaLog(LOGMSG_VERBOSE, "No VerifyPath specified for delete element of ID: %ls", pwzElementPath);
                }
                break;
            default:
                ExitOnFailure(hr = E_UNEXPECTED, "Invalid modification specified in custom action data");
                break;
            }
        }


        // Now that we've made all of the changes to this file, save it and move on to the next
        if (S_OK == hrOpenFailure)
        {
            if (fPreserveDate)
            {
                hr = FileGetTime(pwzFile, NULL, NULL, &ft);
                ExitOnFailure1(hr, "failed to get modified time of file : %ls", pwzFile);
            }

            int iSaveAttempt = 0;

            do
            {
                hr = XmlSaveDocument(pixd, pwzFile);
                if (FAILED(hr))
                {
                    id = WcaErrorMessage(msierrXmlConfigFailedSave, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 1, pwzFile);
                    switch (id)
                    {
                    case IDABORT:
                        ExitOnFailure1(hr, "Failed to save changes to XML file: %ls", pwzFile);
                    case IDRETRY:
                        hr = S_FALSE;   // hit me, baby, one more time
                        break;
                    case IDIGNORE:
                        hr = S_OK;  // pretend everything is okay and bail
                        break;
                    case 0: // No UI case, MsiProcessMessage returns 0
                        if (STIERR_SHARING_VIOLATION == hr)
                        {
                            // Only in case of sharing violation do we retry 30 times, once a second.
                            if (iSaveAttempt < 30)
                            {
                                hr = S_FALSE;
                                ++iSaveAttempt;
                                WcaLog(LOGMSG_VERBOSE, "Unable to save changes to XML file: %ls, retry attempt: %x", pwzFile, iSaveAttempt);
                                Sleep(1000);
                            }
                            else
                            {
                                ExitOnFailure1(hr, "Failed to save changes to XML file: %ls", pwzFile);
                            }
                        }
                        break;
                    default: // Unknown error
                        ExitOnFailure1(hr, "Failed to save changes to XML file: %ls", pwzFile);
                    }
                }
            } while (S_FALSE == hr);

            if (fPreserveDate)
            {
                hr = FileSetTime(pwzFile, NULL, NULL, &ft);
                ExitOnFailure1(hr, "failed to set modified time of file : %ls", pwzFile);
            }

            if (fIsFSRedirectDisabled)
            {
                fIsFSRedirectDisabled = FALSE;
                WcaRevertWow64FSRedirection();
            }
        }
    }

LExit:
    // Make sure we revert FS Redirection if necessary before exiting
    if (fIsFSRedirectDisabled)
    {
        fIsFSRedirectDisabled = FALSE;
        WcaRevertWow64FSRedirection();
    }
    WcaFinalizeWow64();

    ReleaseStr(pwzCustomActionData);
    ReleaseStr(pwzData);
    ReleaseStr(pwzFile);
    ReleaseStr(pwzElementPath);
    ReleaseStr(pwzVerifyPath);
    ReleaseStr(pwzName);
    ReleaseStr(pwzValue);

    ReleaseObject(pixeNew);
    ReleaseObject(pixdNew);

    ReleaseObject(pixn);
    ReleaseObject(pixd);
    ReleaseObject(pixnNewNode);
    ReleaseObject(pixnRemovedChild);

    XmlUninitialize();

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