It is not really a question because I have already found a solution. It took me a lot of time, that's why I want to explain it here.
Msxml is based on COM so it is not really easy to use in C++ even when you have helpful classes to deal with memory allocation issues. But writing a new XML parser would be much more difficult so I wanted to use msxml.
The problem:
I was able to find enough examples on the internet to use msxml with the help of CComPtr
(smart pointer to avoid having to call Release() for each IXMLDOMNode manually), CComBSTR
(to convert C++ strings to the COM format for strings) and CComVariant
. This 3 helpful classes are ATL classes and need an #include <atlbase.h>
.
Problem: Visual Studio 2008 Express (the free version) doesn't include ATL.
Solution:
Use comutil.h
and comdef.h
, which include some simple helper classes:
_bstr_t
replaces more or less CComBSTR
_variant_t
replaces more or less CComVariant
_com_ptr_t
replaces indirectly CComPtr
through the use of _COM_SMARTPTR_TYPEDEF
Small example:
#include <msxml.h>
#include <comdef.h>
#include <comutil.h>
// Define some smart pointers for MSXML
_COM_SMARTPTR_TYPEDEF(IXMLDOMDocument, __uuidof(IXMLDOMDocument)); // IXMLDOMDocumentPtr
_COM_SMARTPTR_TYPEDEF(IXMLDOMElement, __uuidof(IXMLDOMElement)); // IXMLDOMElementPtr
_COM_SMARTPTR_TYPEDEF(IXMLDOMNodeList, __uuidof(IXMLDOMNodeList)); // IXMLDOMNodeListPtr
_COM_SMARTPTR_TYPEDEF(IXMLDOMNamedNodeMap, __uuidof(IXMLDOMNamedNodeMap)); // IXMLDOMNamedNodeMapPtr
_COM_SMARTPTR_TYPEDEF(IXMLDOMNode, __uuidof(IXMLDOMNode)); // IXMLDOMNodePtr
void test_msxml()
{
// This program will use COM
CoInitializeEx(NULL, COINIT_MULTITHREADED);
{
// Create parser
IXMLDOMDocumentPtr pXMLDoc;
HRESULT hr = CoCreateInstance(__uuidof (DOMDocument), NULL, CLSCTX_INPROC_SERVER, IID_IXMLDOMDocument, (void**)&pXMLDoc);
pXMLDoc->put_validateOnParse(VARIANT_FALSE);
pXMLDoc->put_resolveExternals(VARIANT_FALSE);
pXMLDoc->put_preserveWhiteSpace(VARIANT_FALSE);
// Open file
VARIANT_BOOL bLoadOk;
std::wstring sfilename = L"testfile.xml";
hr = pXMLDoc->load(_variant_t(sfilename.c_str()), &bLoadOk);
// Search for node <testtag>
IXMLDOMNodePtr pNode;
hr = pXMLDoc->selectSingleNode(_bstr_t(L"testtag"), &pNode);
// Read something
_bstr_t bstrText;
hr = pNode->get_text(bstrText.GetAddress());
std::string sSomething = bstrText;
}
// I'm finished with COM
// (Don't call before all IXMLDOMNodePtr are out of scope)
CoUninitialize();
}
Maybe try using the #import
statement.
I've used it in a VS6 project I have hanging around, you do something like this (for illustrative purposes only; this worked for me but I don't claim to be error proof):
#import "msxml6.dll"
...
MSXML2::IXMLDOMDocument2Ptr pdoc;
HRESULT hr = pdoc.CreateInstance(__uuidof(MSXML2::DOMDocument60));
if (!SUCCEEDED(hr)) return hr;
MSXML2::IXMLDOMDocument2Ptr pschema;
HRESULT hr = pschema.CreateInstance(__uuidof(MSXML2::DOMDocument60));
if (!SUCCEEDED(hr)) return hr;
pschema->async=VARIANT_FALSE;
VARIANT_BOOL b;
b = pschema->loadXML(_bstr_t( /* your schema XML here */ ));
MSXML2::IXMLDOMSchemaCollection2Ptr pSchemaCache;
hr = pSchemaCache.CreateInstance(__uuidof(MSXML2::XMLSchemaCache60));
if (!SUCCEEDED(hr)) return hr;
_variant_t vp=pschema.GetInterfacePtr();
pSchemaCache->add(_bstr_t( /* your namespace here */ ),vp);
pdoc->async=VARIANT_FALSE;
pdoc->schemas = pSchemaCache.GetInterfacePtr();
pdoc->validateOnParse=VARIANT_TRUE;
if (how == e_filename)
b = pdoc->load(v);
else
b = pdoc->loadXML(bxmldoc);
pXMLError = pdoc->parseError;
if (pXMLError->errorCode != 0)
return E_FAIL; // an unhelpful return code, sigh....
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With