bool XmlProfileParser::ParseFile()

in XmlProfileParser/XmlProfileParser.cpp [76:287]


bool XmlProfileParser::ParseFile(const char *pszPath, Profile *pProfile, vector<Target> *pvSubstTargets, HMODULE hModule)
{
    assert(pszPath != nullptr);
    assert(pProfile != nullptr);

    // import schema from the named resource
    HRSRC hSchemaXmlResource = FindResource(hModule, L"DISKSPD.XSD", RT_HTML);
    assert(hSchemaXmlResource != NULL);
    HGLOBAL hSchemaXml = LoadResource(hModule, hSchemaXmlResource);
    assert(hSchemaXml != NULL);
    LPVOID pSchemaXml = LockResource(hSchemaXml);
    assert(pSchemaXml != NULL);
    
    // convert from utf-8 produced by the xsd authoring tool to utf-16
    int cchSchemaXml = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)pSchemaXml, -1, NULL, 0);
	vector<WCHAR> vWideSchemaXml(cchSchemaXml);
    int dwcchWritten = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)pSchemaXml, -1, vWideSchemaXml.data(), cchSchemaXml);
    UNREFERENCED_PARAMETER(dwcchWritten);
    assert(dwcchWritten == cchSchemaXml);
    // ... and finally, packed in a bstr for the loadXml interface
    CComBSTR bSchemaXml(vWideSchemaXml.data());

    bool fComInitialized = false;
    HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
    if (SUCCEEDED(hr))
    {
        fComInitialized = true;
        CComPtr<IXMLDOMDocument2> spXmlDoc = nullptr;
        CComPtr<IXMLDOMDocument2> spXmlSchema = nullptr;
        CComPtr<IXMLDOMSchemaCollection2> spXmlSchemaColl = nullptr;
        CComPtr<IXMLDOMParseError> spXmlParseError = nullptr;

        // create com objects and decorate
        hr = CoCreateInstance(__uuidof(DOMDocument60), nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&spXmlSchema));
        if (SUCCEEDED(hr))
        {
            hr = spXmlSchema->put_async(VARIANT_FALSE);
        }
        if (SUCCEEDED(hr))
        {
            hr = spXmlSchema->setProperty(CComBSTR("ProhibitDTD"), CComVariant(VARIANT_FALSE));
        }
        if (SUCCEEDED(hr))
        {
            hr = CoCreateInstance(__uuidof(XMLSchemaCache60), nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&spXmlSchemaColl));
        }
        if (SUCCEEDED(hr))
        {
            hr = spXmlSchemaColl->put_validateOnLoad(VARIANT_TRUE);
        }
        if (SUCCEEDED(hr))
        {
            hr = CoCreateInstance(__uuidof(DOMDocument60), nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&spXmlDoc));
        }
        if (SUCCEEDED(hr))
        {
            hr = spXmlDoc->put_async(VARIANT_FALSE);
        }
        if (SUCCEEDED(hr))
        {
            hr = spXmlDoc->put_validateOnParse(VARIANT_TRUE);
        }
        if (SUCCEEDED(hr))
        {
            VARIANT_BOOL fvIsOk;
            hr = spXmlSchema->loadXML(bSchemaXml, &fvIsOk);
            if (FAILED(hr) || fvIsOk != VARIANT_TRUE)
            {
                hr = spXmlSchema->get_parseError(&spXmlParseError);
                if (SUCCEEDED(hr))
                {
                    ReportXmlError("schema", spXmlParseError);
                }
                hr = E_FAIL;
            }
        }
        if (SUCCEEDED(hr))
        {
            CComVariant vXmlSchema(spXmlSchema);
            CComBSTR bNull("");
            hr = spXmlSchemaColl->add(bNull, vXmlSchema);
        }
        if (SUCCEEDED(hr))
        {
            CComVariant vSchemaCache(spXmlSchemaColl);
            hr = spXmlDoc->putref_schemas(vSchemaCache);
        }
        if (SUCCEEDED(hr))
        {
            VARIANT_BOOL fvIsOk;
            CComVariant vPath(pszPath);
            hr = spXmlDoc->load(vPath, &fvIsOk);
            if (FAILED(hr) || fvIsOk != VARIANT_TRUE)
            {
                hr = spXmlDoc->get_parseError(&spXmlParseError);
                if (SUCCEEDED(hr))
                {
                    ReportXmlError("profile", spXmlParseError);
                }
                hr = E_FAIL;
            }
        }

        //
        // XML has now passed basic schema validation. Bulld the target substitutions and parse the profile.
        //

        vector<pair<string, bool>> vSubsts;

        if (pvSubstTargets)
        {
            for (auto target : *pvSubstTargets)
            {
                vSubsts.emplace_back(make_pair(target.GetPath(), false));
            }
        }

        if (SUCCEEDED(hr))
        {
            bool b;
            hr = _GetBool(spXmlDoc, "//Profile/Verbose", &b);
            if (SUCCEEDED(hr) && (hr != S_FALSE))
            {
                pProfile->SetVerbose(b);
            }
        }

        if (SUCCEEDED(hr))
        {
            DWORD i;
            hr = _GetDWORD(spXmlDoc, "//Profile/Progress", &i);
            if (SUCCEEDED(hr) && (hr != S_FALSE))
            {
                pProfile->SetProgress(i);
            }
        }

        if (SUCCEEDED(hr))
        {
            string sResultFormat;
            hr = _GetString(spXmlDoc, "//Profile/ResultFormat", &sResultFormat);
            if (SUCCEEDED(hr) && (hr != S_FALSE) && sResultFormat == "xml")
            {
                pProfile->SetResultsFormat(ResultsFormat::Xml);
            }
        }

        if (SUCCEEDED(hr))
        {
            string sCreateFiles;
            hr = _GetString(spXmlDoc, "//Profile/PrecreateFiles", &sCreateFiles);
            if (SUCCEEDED(hr) && (hr != S_FALSE))
            {
                if (sCreateFiles == "UseMaxSize")
                {
                    pProfile->SetPrecreateFiles(PrecreateFiles::UseMaxSize);
                }
                else if (sCreateFiles == "CreateOnlyFilesWithConstantSizes")
                {
                    pProfile->SetPrecreateFiles(PrecreateFiles::OnlyFilesWithConstantSizes);
                }
                else if (sCreateFiles == "CreateOnlyFilesWithConstantOrZeroSizes")
                {
                    pProfile->SetPrecreateFiles(PrecreateFiles::OnlyFilesWithConstantOrZeroSizes);
                }
                else
                {
                    hr = E_INVALIDARG;
                }
            }
        }

        if (SUCCEEDED(hr))
        {
            hr = _ParseEtw(spXmlDoc, pProfile);
        }

        if (SUCCEEDED(hr))
        {
            hr = _ParseTimeSpans(spXmlDoc, pProfile, vSubsts);
        }

        //
        // Error on unused substitutions - user should ensure these match up.
        //
        // Note that no (zero) substitutions are OK at the point of parsing, which allows
        // for -Rp forms on template profiles. Validation for executed profiles will occur
        // later during common validation.
        //
        // Generate an error for each unused substitution.
        //

        if (SUCCEEDED(hr))        
        {
            for (size_t i = 1; i <= vSubsts.size(); ++i)
            {
                if (!vSubsts[i - 1].second)
                {
                    fprintf(stderr, "ERROR: unused template target substitution _%u -> %s - check profile\n", (int) i, vSubsts[i - 1].first.c_str());
                    hr = E_INVALIDARG;
                }
            }
        }
    }

    if (fComInitialized)
    {
        CoUninitialize();
    }

    return SUCCEEDED(hr);
}