C++ XLST transform not working using MSXML 3.0 - c++

I'm new to C++ and inherited the following code that is supposed to transform the given XML using the XSLT file to just spit out the text values.
It loads both the XML and XSLT fine and the transformnode() call returns success but no transformation has been applied. The original output at the bottom contains the original XML intact .
It is using MSXML 3.0. I've used Xselerator to validate that the XSLT is valid and works (i.e. the string "This is a test message.." is returned).
Here is the code (minus all the error handling):
IXMLDOMNode *m_pXslt;
ESSXsltData::Initialise(void)
{
IUnknown *l_pUnknown = NULL;
IXMLDOMDocument *l_pXSLDocument = NULL;
HRESULT hr = CoCreateInstance(__uuidof(DOMDocument), NULL, CLSCTX_ALL, IID_IUnknown, (LPVOID *)&l_pUnknown);
hr = l_pUnknown->QueryInterface(IID_IXMLDOMDocument,(LPVOID *)&l_pXSLDocument);
CString l_sFileName(RetrieveXsltFileName().c_str() );
hr = l_pXSLDocument->load(l_vFileName, &l_bSuccess);
hr = l_pXSLDocument->QueryInterface(IID_IXMLDOMNode, (LPVOID *)(&m_pXslt) );
}
HRESULT ESSXsltData::ApplyXslt(const char *p_pszESSXml, std::vector< std::string > &p_CommentLines)
{
IUnknown *l_pUnknown = NULL;
IXMLDOMDocument *l_pDocument = NULL;
if (p_pszESSXml)
{
VARIANT_BOOL l_bSuccess;
HRESULT hr = CoCreateInstance(__uuidof(DOMDocument), NULL, CLSCTX_ALL, IID_IUnknown, (LPVOID *)&l_pUnknown);
hr = l_pUnknown->QueryInterface(IID_IXMLDOMDocument,(LPVOID *)&l_pDocument);
hr = l_pDocument->loadXML(CComBSTR(p_pszESSXml) , &l_bSuccess);
hr = l_pDocument->QueryInterface(IID_IXMLDOMNode, (LPVOID *)(&m_pXslt) );
BSTR l_bsOutput = NULL;
hr = l_pDocument->transformNode(m_pXslt, &l_bsOutput);
COLE2T l_AsciiOutput(l_bsOutput);
log << "AsciiOutput: " << l_AsciiOutput << "\n";
}
}
The p_pszESSXml string is:
<ESS><Message>This is a test message...</Message></ESS>
The XSLT is:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="text" indent="yes"/>
<xsl:template match="ESS">
<xsl:apply-templates select="Message"/>
</xsl:template>
<xsl:template match="Message">
<xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>

Fixed it.
It was reassigning m_pXslt to equal the XML is was supposed to be validating.
Someone has been copying and pasting.

Related

XPath query is not working in IXMLDOMDocument2, but passes online XPath tester

In the below XML, I'm trying to use an XPath query to select the first <Event> node that has RenderingInfo/Task = StartRemoteSessionRdpClientBegin.
<Events>
<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
<System>
<Provider Guid="{9e814aad-3204-11d2-9a82-006008a86939}" />
<EventID>0</EventID>
</System>
<EventData>
<Data Name="EventVersion">0</Data>
<Data Name="Reserved">0</Data>
<Data Name="PartitionType"> 0</Data>
</EventData>
<RenderingInfo Culture="en-US">
<Opcode>PartitionInfoExtensionV2</Opcode>
<Provider>MSNT_SystemTrace</Provider>
<EventName xmlns="http://schemas.microsoft.com/win/2004/08/events/trace">EventTrace</EventName>
</RenderingInfo>
<ExtendedTracingInfo xmlns="http://schemas.microsoft.com/win/2004/08/events/trace">
<EventGuid>{68fdd900-4a3e-11d1-84f4-0000f80464e3}</EventGuid>
</ExtendedTracingInfo>
</Event>
<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
<System>
<Provider Name="Microsoft.Windows.HVSI.Manager" Guid="{5e3f60ef-a60f-45a9-84ae-e224f761baa3}" />
<EventID>0</EventID>
</System>
<EventData>
<Data Name="ContainerName">2fb583e6-3769-4465-9de0-ce56a1aa84c4</Data>
<Data Name="RDPSessionID">{24259afa-2028-4206-9856-257ea519bfdd}</Data>
<Data Name="PartA_PrivTags">16777216</Data>
</EventData>
<RenderingInfo Culture="en-US">
<Task>StartRemoteSessionRdpClientBegin</Task>
</RenderingInfo>
</Event>
</Events>
From W3School examples I've looked at, I'd think this should be possible with an XPath query like: /Events/Event[RenderingInfo/Task="StartRemoteSessionRdpClientBegin"].
I've tried this with my XML at https://www.uccollabing.com/free-online-xpath-tester-evaluator-tool/ and it seemed to work there, which makes me suspect an issue with my code. Simple queries work e.g. /Events or /Events/*.
Should my above query work?
HRESULT SearchTraceFile(std::wstring fileName)
{
HRESULT hr = S_OK;
_bstr_t xPath = L"/Events/Event[RenderingInfo/Task=\"StartRemoteSessionRdpClientBegin\"]";
wprintf(L"Query path: %s\n", static_cast<wchar_t*>(xPath));
RETURN_IF_FAILED(CoInitialize(NULL));
wrl::ComPtr<IXMLDOMDocument2> xmlDoc;
RETURN_IF_FAILED(CoCreateInstance(
CLSID_DOMDocument60,
nullptr,
CLSCTX_INPROC_SERVER,
IID_IXMLDOMDocument2, (void**)&xmlDoc));
_variant_t localFileName(fileName.c_str());
VARIANT_BOOL successful = VARIANT_FALSE;
RETURN_IF_FAILED(xmlDoc->load(localFileName, &successful));
RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_DATA), (successful != VARIANT_TRUE));
_variant_t varXPath(L"XPath");
xmlDoc->setProperty(L"SelectionLanguage", varXPath);
xmlDoc->setProperty(L"MaxElementDepth", CComVariant(10));
wrl::ComPtr<IXMLDOMNodeList> events;
wrl::ComPtr<IXMLDOMNode> spTaskNode;
hr = xmlDoc->selectSingleNode(xPath, &spTaskNode);
if (FAILED(hr)) return hr;
if (S_FALSE == hr)
{
wprintf(L"Not found\n");
return S_OK;
}
BSTR value = NULL;
hr = spTaskNode->get_text(&value);
if (FAILED(hr)) wprintf(L"get_text failed 0x%08x\n", hr);
else wprintf(L"get_text succedded\n");
wprintf(L"value:\n%s\n", value);
::SysFreeString(value);
return hr;
}
Your XML involves namespaces, so you have to take that into account in your XPath query. This is documented on MSDN:
Specify Namespace when you query the DOM with XPath
For example:
HRESULT SearchTraceFile(std::wstring fileName)
{
_bstr_t xPath = L"/Events/e:Event[e:RenderingInfo/e:Task=\"StartRemoteSessionRdpClientBegin\"]"; // <-- NOTE THE 'e:' PREFIXES!
wprintf(L"Query path: %s\n", static_cast<wchar_t*>(xPath));
RETURN_IF_FAILED(CoInitialize(NULL));
wrl::ComPtr<IXMLDOMDocument2> xmlDoc;
RETURN_IF_FAILED(CoCreateInstance(
CLSID_DOMDocument60,
nullptr,
CLSCTX_INPROC_SERVER,
IID_IXMLDOMDocument2, (void**)&xmlDoc));
_variant_t localFileName(fileName.c_str());
VARIANT_BOOL successful = VARIANT_FALSE;
RETURN_IF_FAILED(xmlDoc->load(localFileName, &successful));
RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_DATA), (successful != VARIANT_TRUE));
xmlDoc->setProperty(L"SelectionLanguage", _variant_t(L"XPath"));
xmlDoc->setProperty(L"SelectionNamespaces", _variant_t(L"xmlns:e='http://schemas.microsoft.com/win/2004/08/events/event'")); // <-- ADD THIS!
xmlDoc->setProperty(L"MaxElementDepth", _variant_t(10));
wrl::ComPtr<IXMLDOMNodeList> events;
wrl::ComPtr<IXMLDOMNode> spTaskNode;
HRESULT hr = xmlDoc->selectSingleNode(xPath, &spTaskNode);
if (FAILED(hr)) {
wprintf(L"selectSingleNode failed 0x%08x\n", hr);
return hr;
}
if (S_FALSE == hr)
{
wprintf(L"Not found\n");
return S_OK;
}
_bstr_t value;
hr = spTaskNode->get_text(value.GetAddress());
if (FAILED(hr)) {
wprintf(L"get_text failed 0x%08x\n", hr);
return hr;
}
wprintf(L"get_text succeeded\nvalue:\n%s\n", static_cast<wchar_t*>(value));
return S_OK;
}

XMLLite ignore encoding

I have to read XML files in C++ and we are using XMLLite the problem I have is that the first line of my XML specifies the encoding
<?xml version="1.0" encoding="ISO-8859-15"?>
and when I try to parse the file with XMLLite, I'm getting the error MX_E_ENCODINGSIGNATURE. If I remove the encoding part, the XML parses fine.
<?xml version="1.0"?>
So is it possible to programmatically tell XMLLite to ignore the encoding resp. what other options do I have?
One solution would be to write my own input stream class and suppress this header, and inject the short version, but it would be nicer to have a clean solution.
Even though the documentation says that XMLLite can not handle different encodings it seems that you can go around this by using IMultiLanguage2, which is mentioned in the documentation but no example is given.
So here is how to enable it:
#include <mlang.h>
XMLLiteReader::XMLLiteReader(void)
{
mLanguage = NULL;
mXMLLiteReader = NULL;
mCOMInitialized = false;
HRESULT hr;
if(CoInitialize(NULL) != S_OK)
return;
mCOMInitialized = true;
if((hr = CoCreateInstance(CLSID_CMultiLanguage, NULL, CLSCTX_ALL, IID_IMultiLanguage2, (void **)&mLanguage)) != S_OK)
return;
hr = CreateXmlReader(__uuidof(IXmlReader), (void**) &mXMLLiteReader, NULL);
if(hr != S_OK)
{
mXMLLiteReader = NULL;
return;
}
hr = mXMLLiteReader->SetProperty(XmlReaderProperty_MultiLanguage, (LONG_PTR)mLanguage);
}

COM script activation sometimes fails

I am running com scripts from c++ on windows xp
Usually the script executes without problems, but sometimes the execution fails with hresult DISP_E_MEMBERNOTFOUND. (Invoke is the function that fails)
Does anybody have any idea why it happens?
The code is below
CLSID clsid;
MULTI_QI mqi;
HRESULT hr ;
mqi.hr = 0;
mqi.pIID = &IID_IDispatch;
mqi.pItf = NULL;
hr = CLSIDFromProgID(T2W(progId.c_str()), &clsid);
CHECK_HRESULT(hr);
hr = CoCreateInstanceEx(clsid, NULL, CLSCTX_INPROC_SERVER, NULL, 1, &mqi);
CHECK_HRESULT(hr);
tMethodArguments argTemp(arguments.size()) ;
USES_CONVERSION ;
HRESULT hr;
DISPID index ;
OLECHAR FAR* szMember = T2W(method.c_str());
unsigned int err ;
hr = m_pScriptComObj->GetIDsOfNames( IID_NULL, &szMember, 1, LOCALE_SYSTEM_DEFAULT, &index ) ;
CHECK_HRESULT(hr);
DISPPARAMS params ;
params.rgvarg = pArgs ;
params.rgdispidNamedArgs = NULL ;
params.cArgs = arguments.size() ;
params.cNamedArgs = 0 ;
EXCEPINFO excep_info;
hr = m_pScriptComObj->Invoke(index,
IID_NULL,
LOCALE_SYSTEM_DEFAULT,
DISPATCH_METHOD,
&params,
&res,
&excep_info,
&err);
I omitted method and arguments initialization
The vbscript that is called by INVOKE looks like this
<?xml version="1.0"?>
<component id="script_wsc_script">
<?component?>
<registration
description="script_wsc_script"
progid="wscScript.WSC"
version="1.00"
classid="{A1C14070-EBAB-41A0-BC9C-B4330A73437D}"
remotable="true"
>
</registration>
<public>
<method name="PrintMessage">
<PARAMETER name="first"/>
<PARAMETER name="second"/>
</method>
</public>
<script language="VBScript">
<![CDATA[
Function PrintMessage ( first, second)
''do something
End Function

IXMLDOMDocument::selectNodes doesn't work as expected

It's amazing IXMLDOMDocument::selectNodes doesn't work as expected. I know it's because xmlns="http://stackoverflow.com/questions/ask", but I don't know how to make it work if xmlns is present.
V_BSTR(&varParam) = SysAllocString(LR"iPhone5s(XPath)iPhone5s");
V_VT(&varParam) = VT_BSTR;
hr = ptrXMLDom->setProperty(L"SelectionLanguage", varParam);
ptrXMLDom->selectNodes(L"//bookstore", &ptrDomNodeList);
ptrDomNodeList->get_length(&len);//the value is always 0
Remarks: the xml is read-only. Don't tell me to modify original XML.
Here is my code:
#include <msxml6.h>
#include <msxml2.h>//necessary in Visual Studio 2013
HRESULT hr = CoInitialize(NULL);
HRESULT hr = CoCreateInstance(__uuidof(DOMDocument60), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(ppDoc));
if (SUCCEEDED(hr))
{
// these methods should not fail so don't inspect result
(*ppDoc)->put_async(VARIANT_FALSE);
(*ppDoc)->put_validateOnParse(VARIANT_FALSE);
(*ppDoc)->put_resolveExternals(VARIANT_FALSE);
(*ppDoc)->put_preserveWhiteSpace(VARIANT_TRUE);
}
HRESULT hr = S_OK;
IXMLDOMDocument2 *ptrXMLDom = NULL;
VARIANT_BOOL varBool = VARIANT_FALSE;
BSTR bstrXML = nullptr;
VARIANT varParam;
VariantInit(&varParam);
V_BSTR(&varParam) = SysAllocString(LR"iPhone5s(E:\Work\book.xml)iPhone5s");
V_VT(&varParam) = VT_BSTR;
hr = ptrXMLDom->load(varParam, &varBool);
V_BSTR(&varParam) = SysAllocString(LR"iPhone5s(XPath)iPhone5s");
V_VT(&varParam) = VT_BSTR;
hr = ptrXMLDom->setProperty(L"SelectionLanguage", varParam);
ptrXMLDom->selectNodes(L"//bookstore", &ptrDomNodeList);
ptrDomNodeList->get_length(&len);//the value is always 0
CoUninitialize();
Here is the xml:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- Copyright w3school.com.cn -->
<!-- W3School.com.cn bookstore example -->
<bookstore xmlns="http://stackoverflow.com/questions/ask" version="1.0">
<book category="children">
<title lang="en">Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
<book category="cooking">
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>
<book category="web" cover="paperback">
<title lang="en">Learning XML</title>
<author>Erik T. Ray</author>
<year>2003</year>
<price>39.95</price>
</book>
<book category="web">
<title lang="en">XQuery Kick Start</title>
<author>James McGovern</author>
<author>Per Bothner</author>
<author>Kurt Cagle</author>
<author>James Linn</author>
<author>Vaidyanathan Nagarajan</author>
<year>2003</year>
<price>49.99</price>
</book>
</bookstore>
i have found the answer by myself.
V_BSTR(&varParam) = SysAllocString(LR"iPhone5s(xmlns:pf='http://stackoverflow.com/questions/ask')iPhone5s");
V_VT(&varParam) = VT_BSTR;
hr = ptrXMLDom->setProperty(L"SelectionNamespaces", varParam);
ptrXMLDom->selectNodes(L"//pf:bookstore/pf:book", &ptrDomNodeList);
ptrDomNodeList->get_length(&len);//4

Set the Default Search Engine Provider of IE with IOpenServiceManager::InstallService

I would like to set the Default Search Engine Provider of IE with IOpenServiceManager::InstallService:
Belong to the link http://www.opensearch.org/Specifications/OpenSearch/1.1#OpenSearch_description_elements. I created the SearchProviderInfo.xml like this:
<?xml version="1.0" encoding="UTF-8"?> <OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/"> <ShortName>Web Search</ShortName> <Description>Use Example.com to search the Web.</Description> <Tags>example web</Tags> <Contact>admin#example.com</Contact> <Url type="application/atom+xml" template="http://example.com/?q={searchTerms}&pw={startPage?}&format=atom"/> <Url type="application/rss+xml" template="http://example.com/?q={searchTerms}&pw={startPage?}&format=rss"/> <Url type="text/html" template="http://example.com/?q={searchTerms}&pw={startPage?}"/> <LongName>Example.com Web Search</LongName> <Image height="64" width="64" type="image/png">http://example.com/websearch.png</Image> <Image height="16" width="16" type="image/vnd.microsoft.icon">http://example.com/websearch.ico</Image> <Query role="example" searchTerms="cat" /> <Developer>Example.com Development Team</Developer> <Attribution> Search data Copyright 2005, Example.com, Inc., All Rights Reserved </Attribution> <SyndicationRight>open</SyndicationRight> <AdultContent>false</AdultContent> <Language>en-us</Language> <OutputEncoding>UTF-8</OutputEncoding> <InputEncoding>UTF-8</InputEncoding> </OpenSearchDescription>
Belong to the link http://msdn.microsoft.com/en-us/library/cc849088%28v=vs.85%29.aspx. I create the project "SetDefaultHelper" like this:
#include <windows.h>
#include <atlbase.h>
#include <wininet.h>
#include <urlmon.h>
#include <string>
#include "openservice.h"
#pragma comment(lib, "urlmon.lib")
void DisplayUsage()
{
wprintf(L"\r\nSetDefaultHelper.exe -- Call SetDefault API on a search provider");
wprintf(L"\r\n");
wprintf(L"\r\nUSAGE: SetDefaultHelper.exe <option>");
wprintf(L"\r\n");
wprintf(L"\r\nOptions (these are mutually exclusive!):");
wprintf(L"\r\n");
wprintf(L"\r\n /guid <guid> GUID of an installed search provider");
wprintf(L"\r\n /url <url> URL of an OpenSearch Description file");
wprintf(L"\r\n");
}
int __cdecl wmain(__in int argc, __in_ecount(argc) WCHAR* argv[])
{
HRESULT hr = E_FAIL;
BOOL fComInitialized = FALSE;
if (3 != argc)
{
DisplayUsage();
}
else if (SUCCEEDED(CoInitialize(NULL)))
{
fComInitialized = TRUE;
CComPtr<IOpenServiceManager> spManager;
hr = spManager.CoCreateInstance(CLSID_OpenServiceManager);
if (SUCCEEDED(hr))
{
CComPtr<IOpenService> spService;
if (0 == _wcsicmp(argv[1], L"/guid"))
{
// Get an IOpenService pointer from the GUID.
WCHAR szEscaped[INTERNET_MAX_URL_LENGTH] = L"";
DWORD cchEscaped = ARRAYSIZE(szEscaped);
hr = UrlEscape(argv[2], szEscaped, &cchEscaped, URL_ESCAPE_SEGMENT_ONLY);
if (SUCCEEDED(hr))
{
std::wstring wsOsid(L"x-osid:1:search:");
wsOsid += szEscaped;
hr = spManager->GetServiceByID(wsOsid.c_str(), &spService);
}
}
else if (0 == _wcsicmp(argv[1], L"/url"))
{
// Install the provider to get an IOpenService pointer.
//CComPtr<IUri> spUri;
//hr = CreateUri(argv[2], 0, 0, &spUri);
//if (SUCCEEDED(hr))
//{
hr = spManager->InstallService(argv[2], &spService);
//}
}
else
{
DisplayUsage();
hr = E_FAIL;
}
if (SUCCEEDED(hr))
{
hr = spService->SetDefault(TRUE, NULL);
}
}
}
if (fComInitialized)
{
CoUninitialize();
}
return hr;
}
I build the project ok. Both file SetDefaultHelper.exe and SearchProviderInfo.xml are same folder. In the project setting, set Configuration Properties > Debugging > Commands Arguments = /url absolutePaht/searchProvider.xml. Then run debug (F10), at line "hr = CreateUri(argv[2], 0, 0, &spUri);", the rusult hr is so stranger. I don't know why. Can you help me?
Thank you very much.
[Resolved]:
1. Don't need CreateUri //commented
2. Use a absolutely path.
Use absolute path and UrlCreateFromPath to create a file:/// like URL, pass that URL to InstallService.
Between, it seems that your XML has error.
WCHAR szURL[MAX_PATH] = L"";
DWORD cchURL = ARRAYSIZE(szURL);
hr = ::UrlCreateFromPath(argv[2], szURL, &cchURL, 0);
if (SUCCEEDED(hr))
{
hr = spManager->InstallService(argv[2], &spService);
// Now we can set it as the default.
if (SUCCEEDED(hr))
{
hr = spService->SetDefault(TRUE, NULL);
if (hr == OS_E_CANCELLED)
{
hr = E_ACCESSDENIED; // by the user!
}
}
}