Click here to Skip to main content
Email Password   helpLost your password?

Contents

  1. Introduction
  2. What is MSXMLCPP
  3. Concepts
  4. XML SAX wrapper nodes
  5. Drawbacks
  6. Requirements
  7. Unicode support
  8. How the wrapper classes have been generated
  9. The code examples

Introduction

No matter if you want to do interprocess communication, store your application's settings or documents or format text - XML provides a good solution for all those cases. Further on, Microsoft provides a full implementation of the XML DOM (Document Object Model) and SAX (Simple API for XML), which allows reading and writing XML in an abstract manner: The Microsoft XML parser (MSXML).

Besides the advantage of providing all features you will ever need when handling XML data the Microsoft XML parser also provides the advantage of being available on every future Windows system. The drawback for us, as C++ developers is, that the Microsoft XML parser provides all these features through COM-interfaces.

Using COM-interfaces in C++ always produces a lot of overhead in the source code - you have to handle the HRESULT return values, you have to provide variables for the logical return values, no matter if you are interested in it or not, because they are passed by the parameter list, you have to convert between zero terminated strings and BSTR. You have to take care about reference counting and so on.

Simply take a look at the two following code examples. Both are doing the same things: They are reading the value of an attribute of an XML element using Microsoft's implementation of the XML DOM:

C++
CString GetAttributeValue(IXMLDOMDocument *pDoc, CString strElementPath, 
                                              CString strAttribute)
{
  IXMLDOMNode         *pNode = NULL;
  IXMLDOMNamedNodeMap *pAttributes = NULL;
  BSTR                bstrPath = strElementPath.AllocSysString();
  BSTR                bstrAttribute = strAttribute.AllocSysString();

  try
  {
    HRESULT hr;
    
    hr = pDoc->selectSingleNode(strElementPath, &pNode);
    if (hr != S_OK)
      throw hr;
  
    hr = pNode->get_attributes(&pAttributes);
    if (hr != S_OK)
      throw hr;
      
    hr = pAttributes->getNamedItem(bstrAttribute, &pNode);
    if (hr != S_OK)
      throw hr;
      
    BSTR  bstrValue;
    pNode->Release();
    hr = pNode->get_text(&bstrValue);
    if (hr != S_OK)
      throw hr;
    
    pNode->Release();
    pAttributes->Release();
      
    return (LPCTSTR)_bstr_t(bstrValue, false);
  }
  catch (HRESULT hr)
  {
    SysFreeString(bstrPath);
    SysFreeString(bstrAttribute);
    if (pNode)
      pNode->Release();
    if (pAttributes)
      pAttributes->Release();
      
    return _T("");
  }
}
Visual Basic
Function GetAttributeValue(Doc As IXMLDOMDocument, _
        strElementPath As String, strAttribute As String) As String
    GetAttributeValue = Doc.selectSingleNode(strElementPath)._
                     attributes.getNamedItem(strAttribute).text
End Function

It's nearly unbelievable: Both code examples are doing the same. OK, admittedly the C++ example does a better error handling ;-). But the differences shown here are making clear, why so many people are writing there own XML-classes, instead of using the Microsoft XML parser - the usage is much to unwieldy.

What is MSXMLCPP

That is where MSXMLCPP (MSXML C++) comes in. MSXMLCPP is a MFC extension DLL which provides wrapper classes for all the interfaces of the Microsoft DOM and SAX implementation.

The features are:

If you have ever dreamed of writing:

CString GetAttributeValue(CXMLDOMDocument &Doc, 
           CString strElementPath, CString strAttribute)
{
  return Doc.SelectSingleNode(strElementPath).GetAttributes().
                         GetNamedItem(strAttribute).GetText();
}

in C++, instead of the multi-lined source example given above, then: Hey, welcome to your solution.

Concepts

This section describes the basic concepts of MSXMLCPP.

Wrapper Types

MSXMLCPP provides three kinds of wrapper classes:

  1. Calling wrappers
  2. Implementation wrappers
  3. Coclass wrappers

Calling wrappers

Calling wrappers are classes you can use to invoke methods of the interface wrapped by the specific class. All calling wrappers are derived from the template class CInterfaceCallingWrapper (defined in InterfaceWrapper.h).

Before using calling wrappers you have to attach an interface to an instance of the class. This can be done using a constructor, the assignment operator or explicitly using the Attach() method. Before assigning a new interface pointer to a wrapper, you always have to detach a previously attached one by calling the Detach() method.

The calling wrapper classes are handling the reference counting for the attached interface pointers, so that you won't have to care about it with normal usage. The classes are providing several operator overridings that allow you to use the wrapper class everywhere, instead of the original interface pointer. For more information about the calling wrappers, take a look at the well documented declaration of CInterfaceCallingWrapper in InterfaceWrapper.h

A calling wrapper class provides all the methods and properties provided by the belonging COM interface, but there are some slight differences:

Because the calling wrapper classes are not returning any HRESULT values, they are using the C++ exception mechanism to inform you about unexpected values returned by the original interface methods. Each time a HRESULT value is received for which the SUCCEEDED macro does not return TRUE, an exception of the type CComException* is thrown.

Implementation wrappers

Implementation wrappers are classes that allow you to implement the belonging interface simply by deriving your class from the implementation wrapper class and overriding the high level pure virtual functions. The implementation wrappers are derived from CInterfaceImplementationWrapper or CDispatchInterfaceImplementationWrapper, depending on if there related interfaces are derived from IUnknown or IDispatch.

Implementation wrapping is simply done by aggregation: The implementation wrapper class contains a member (m_x...) that is an implementation of the original interface. Each method of this implementation calls the virtual method MethodPrologue() of the aggregating class and afterwards the belonging virtual wrapper method of the aggregating class. Those wrapper methods are pure virtual methods you have to implement in your derived class. They are identical with the interface's original method, apart from some changes:

This makes implementation much more easier than implementing the original interfaces with all their BSTRs, VARIANTs and HRESULTs.

If your are implementing an interface based on the implementation wrappers, as a stand alone class, the standard mechanisms for reference counting implemented in CInterfaceImplementationWrapper should be OK, but if you want to use your interface implementation as part of a class that implements more interfaces or as part of a CCmdTarget derived class, you will have to modify the reference counting by overriding the virtual AddRef() and Release() methods. Further on you can override the QueryInterface() method and the MethodPrologue() method, which will be necessary in most MFC applications to activate the right module state (i.e. calling AFX_MANAGE_STATE(AfxGetStaticModuleState()) would be a good idea).

Because you do not have the possibility of signaling errors by a HRESULT return value in your derived classes, you can throw a CComException by calling the global AfxThrowComException() - All the method calls in the implementation wrapper are encapsulated by a try statement with a catch(CComException *pE) chained to. If you throw a CComException in your implementation, the implementation wrapper will return the given HRESULT value to the caller instead of the usual S_OK.

CoClass wrappers

The CoClass wrappers are small wrapper classes which allow you to create COM objects and retrieve the specified interfaces. This frees you from calling CoCreateInstance().

CComException

Because the HRESULT values are encapsulated by the wrapper classes, it was necessary to provide a mechanism that allows your software to handle unexpected HRESULTs. Therefore the class CComException has been declared in InterfaceWrapper.h. The class is derived from CException and contains a single attribute m_hr of the type HRESULT.

To throw a CComException you should use the static CComException::Throw() method or the global AfxThrowComException() function. Both are creating a CComException object on the heap, setting its m_hr attribute to the specified value and throwing the pointer.

If you have caught a pointer to a CComException object and you do not need it any more, you should call its Delete() method to destroy it.

XML SAX wrapper nodes

For the SAX implementation, Microsoft provides two kinds of interfaces: the C++ interfaces and the Visual Basic interfaces. Because I have generated all the wrapper classes with a tool, that analysis the parameters of each method to generate the method wrapper, I needed to use the Visual Basic interfaces to get "nice" wrapper classes. The problem with the C++ interfaces (which may be a little bit faster) is, that they are using two parameters for each string: the string itself and a length argument. I did not want to modify the generation tool to recognize this, because this case is very seldom and so I used the somehow slower Visual Basic interfaces, which are using normal BSTRs, which are recognized by the generation tool.

Drawbacks

As everything good, even MSXMLCPP has a drawback: The wrapper classes are producing some time-overhead for all the type conversion and additional function calls. So if your application has to be very fast, it may be better to work on the raw interfaces, but I think the time-overhead should be that small, to justify the advantage of more readable code.

Requirements

To use the MSXMLCPP library with your application, simply include the msxmlcpp.h header file into your stdafx.h (or anywhere else) and link to the msxmlcpp.lib (msxmlcppD.lib for debug version). MSXMLCPP uses RTTI (Runtime Type Information), so it would be a good idea to enable it for your application too.

To run an application using the MSXMLCPP DLL, the Microsoft XML parser (MSXML) has to be installed on the system. You can download it from Microsoft homepage.

This should run on Windows 95 or later and Windows NT 3.1 or later.

Unicode support

Though I haven't tested it, there is no reason, why this should not work with Unicode.

How the wrapper classes have been generated

All the wrapper classes have been generated with a tool of mine, that gets a typelibrary and some rules as input and generates the wrapper classes out of the information in the typelibrary. I hope I will be able to provide a first free beta of this tool in a few weeks, on CodeProject. I think this will make the work for all of us C++ programmers, much more easier.

The code examples

Two code examples, which are using the library are supported with the full download. Both examples are simple console applications, to concentrate on the interesting stuff. Both examples are expecting a file name as command line parameter to be used as XML file - a valid file (books.xml) is contained in the Output directory of the ZIP-archive.

The application DOMXMLDemo, demonstrates how to read and write an XML file using the DOM wrapper classes.

The application SAXXMLDemo demonstrates how to read XML files using the SAX wrapper classes. This also shows how to implement interfaces using implementation wrapper classes.

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
QuestionSave with line breaks and indenting
premod
21:40 9 Jul '09  
How do we use CXMLDOMDocument2:Save() to include line breaks and indents? Right now the entire data is saved in a single line.
GeneralChanging to MsXml6
cbo
23:55 18 Aug '08  
Hello

I modified MsXmlCpp project in order to use MsXml6.
I got exception problem when loading xml file (works fine with MsXml4)

What I did exactly :
1/ Replacing msxml2.h calls by msxml6.h (file included in SDK downloaded from microsoft)
2/ Create CDOMDocument60Class similar to CDOMDocument40Class except m_ClsId fitted with relevant GUID
3/ in my program, replaced CDOMDocument60Class calls by CDOMDocument40Class
4/ No other modification
5/ When running, init of class is OK
6/ Could load Xsl files
7/ Could NOT load Xml files => exception occurs in CXMLDOMDocument::Load method when calling load function

Any Ideas ?
Thaks a lot for help
Sigh
Generalunicode
prudnik
3:43 20 Oct '07  
as far as i can see, it does not work in UNICODE. I have tried, but first got linker errors for all functions using LPCTSTR or CString in their function definitions, even when I compiled the library in UNICODE. When I replaced them be _variant_t, it linked. Running the stuff however, caused exeptione faults. Then I gave it up hating that stupid COM interface ever more. I don't know, who invented the stuff, but its a big waste of time with all that BSTR - VARIANT - CString conversion. If you add to that string, wstring, CString, unicode, UTF8 etc you get quite mad with strings.

prudnik

GeneralmsxmlcppD.lib
djerbi
22:31 13 Aug '07  
Hi

When I Try to compile your code, I get the following error:
INK : fatal error LNK1104: cannot open file "msxmlcppD.lib"
Error executing link.exe.

Babak

Generalstatic linking
Galatei
3:13 18 Jan '07  
Hi,

Is there any way to have it statically linked?

Regards
GeneralMSXMLCPP.DLL Terminating status code
plm5
8:07 7 Dec '06  
Sven,

I'm new to the XML/DOM game, so if I'm asking a dumb question you'll know why.

When the MS VC6.0 app exits(Debug version), the MSXMLCPP.Dll Terminating message is displayed in the debug pane, and has the following status code returned:

The thread 0x142C has exited with code 57602 (0xE102).

There are no detected memory leaks.

Is this normal? If not any ideas as to what the error code is and its associated cause?


thanks

Pat

BTW. The classes have been a tremendous help. Thanks a ton!!

Generalbug: BOOL vs VARIANT_BOOL in DOMXML.cpp
rblaa
15:19 5 Dec '06  
The method signatures accept BOOL args which are then passed internally to COM methods taking VARIANT_BOOL args.

However, TRUE is defined to be 1, and it doesn't work. VARIANT_TRUE is defined to be -1 and must be used instead.

Basically, all cases of passing a BOOL value to a VARIANT_BOOL arg need to be converted, e.g.

#define VARIANT_BOOL_OF(b) (b ? VARIANT_TRUE : VARIANT_FALSE)
...
CXMLDOMNode CXMLDOMDocument::CloneNode(BOOL deep)
{
IXMLDOMNode* o;

HRESULT hr = P()->cloneNode(VARIANT_BOOL_OF(deep), &o);
if (!SUCCEEDED(hr)) AfxThrowComException(hr);

return o;
}

GeneralText converting
HN12345
1:05 4 Nov '06  
Hi

This is a good way to parse XML.
But I have a problem with the text from attributs.
&ltProperty ProjectNotice = "X
Y"/>
If I read the text from the Attribut "ProjectNotice" I get the String
"X\nY" but it should be "X\r\nY"


Wenn I write the file I get the line
&ltProperty ProjectNotice = "X
Y"/>

Has anyone an idea to read and write the correct string

Herbert
Questionhow do i add msxml to my installer ?
code4jigar
3:14 5 Sep '06  
I have made an application. Tht requires msxml6. Now, suppose if client does not have msxml 6 installed then what can be done to install it on client machine ?
Please help.....
GeneralMSXML 6 ???
Hochiminh
17:13 7 May '06  
D'Oh! Don't you have any plan to update for "MSXML6"?
GeneralRe: MSXML 6 ???
Satriani
22:07 18 May '06  
Hallo,

it's not difficult to get a first running version with MSXML6. I've made because I need to port an application to 64bit.

For a simple way, we use the Msxml2.DOMDocument.4.0 as a synonym for Msxml2.DOMDocument.6.0. You only have to change two positions in the existing code to work with the DomDocument:

Open msxml2.h and find the following code:

#ifdef __cplusplus

class DECLSPEC_UUID("88d969c0-f192-11d4-a65f-0040963251e5")
DOMDocument40;
#endif

Open domxml.cpp and find the following code:

#ifdef __cplusplus

class DECLSPEC_UUID("88d969c0-f192-11d4-a65f-0040963251e5")
DOMDocument40;
#endif

The only thing you have to do is to replace the 4.0-UIDs with the new 6.0-UIDs. The UIDs can be found here:

MSXML 6.0 GUIDs and ProgIDs
Now you can work with DomDocument40 and DomDocument60 will be used.

You can do it for the other classes. Now feel free to add 6.0 classes to msxmlcpp.

Ciao

GeneralHow to create xml file from scratch with CXMLDOMDocument2?
z16166
18:22 19 Apr '06  
I need some some sample code. Thanks!
GeneralVS 2003 - Building example fails - Linker error
Satriani
2:12 16 Jul '05  
Hallo Sven,

I've build the libraries in VS 2003. But building the Example fails. The linker tell me about two unresovlfed externals:

DOMXMLDemo error LNK2019: Nicht aufgelöstes externes Symbol '"__declspec(dllimport) public: __thiscall CInterfaceCallingWrapper<struct IXMLDOMElement>::operator struct IXMLDOMElement *(void)" (__imp_??B?$CInterfaceCallingWrapper@UIXMLDOMElement@@@@QAEPAUIXMLDOMElement@@XZ)', verwiesen in Funktion '"void __cdecl AddBook(class CXMLDOMDocument2 &)" (?AddBook@@YAXAAVCXMLDOMDocument2@@@Z)'

DOMXMLDemo error LNK2019: Nicht aufgelöstes externes Symbol '"__declspec(dllimport) public: __thiscall CInterfaceCallingWrapper<struct IXMLDOMNode>::operator struct IXMLDOMNode *(void)" (__imp_??B?$CInterfaceCallingWrapper@UIXMLDOMNode@@@@QAEPAUIXMLDOMNode@@XZ)', verwiesen in Funktion '"void __cdecl DeleteBook(class CXMLDOMDocument2 &)" (?DeleteBook@@YAXAAVCXMLDOMDocument2@@@Z)'

Do you have any ideas?

Thank you

Armin

GeneralRe: VS 2003 - Building example fails - Linker error
yrleu
4:52 25 Jul '05  
A previous thread titled "Re: dllimport definition error" gave the answer:

In this article, the CInterfaceCallingWrapper::P() function is called. I found CInterfaceCallingWrapper::GetP() can also be used instead. I think this linker error is due to the incompatibility between Smart Pointer (CComPtr) and CInterfaceCallingWrapper::operator T*(). Something to do with type conversion.

...
void AddBook(CXMLDOMDocument2 &Doc)
{
CXMLDOMElement book(Doc.CreateElement(_T("book")));

CString strId(Doc.SelectSingleNode("//catalog").GetLastChild().GetAttributes().GetNamedItem(_T("id")).GetText());
strId.Format(_T("bk%03d"), _ttoi(strId.Right(3))+1);
book.SetAttribute(_T("id"), (LPCTSTR)strId);
book.AppendChild(Doc.CreateElement(_T("author")).P());
book.AppendChild(Doc.CreateElement(_T("title")).P());
book.AppendChild(Doc.CreateElement(_T("genre")).P());
book.AppendChild(Doc.CreateElement(_T("price")).P());
book.AppendChild(Doc.CreateElement(_T("publish_date")).P());
book.AppendChild(Doc.CreateElement(_T("description")).P());
...
GeneralRe: VS 2003 - Building example fails - Solved
Satriani
0:39 3 Aug '05  
Tanks for your answer,

but the Problem is the template handling in VS2003. The linker (the msxmlcpp project) don't produce code for every class which inherits from CInterfaceCallingWrapper. Solving the problem is very easy. I have placed the following code for every class in an own cpp-file. An example for CXMLDOMDocument2:

// This line tells the compiler/linker to generate code without instantiating the class
template class CInterfaceCallingWrapper<IXMLDOMDocument2>;

Now it works fine.

GeneralRe: VS 2003 - Building example fails - Solved
yrleu
21:38 5 Sep '05  
This is true. To be more specific, I have added the following lines to DOMXML.cpp to solve several linker errors:
template class CInterfaceCallingWrapper<IXMLDOMDocument2>;
template class CInterfaceCallingWrapper<IXMLDOMDocument>;
template class CInterfaceCallingWrapper<IXMLDOMElement> template class CInterfaceCallingWrapper<IXMLDOMNode>;

-- modified at 2:38 Tuesday 6th September, 2005
General[Message Deleted]
Vicky_Jain
8:40 7 Feb '08  

AnswerRe: VS 2003 - Building example fails - Solved
shazzababs
1:39 8 Feb '08  
I had the same problem after having to change my application to UNICODE, and the above didn't fix it.

However, I eventually found that I had a mixture of settings for the 'Treat wchar_t as build in type' flag in my projects. (Properties->c++->Language).

I set this flag to 'No' in all projects, and like magic the errors went away.

Great library by the way !
Generalmsxmlcpp library
ikasovitch
21:20 19 May '05  
Hi I am using the msxmlcpp library in my project. At the end of one of my methods
a CComException is throw while CComPtr class tries to call Release on a CXMLDOMNode
instance. I have no idea what is wrong with my code. Can you give me some hints?
GeneralRe: msxmlcpp library
z16166
22:12 4 Jun '07  
I have encountered the same problem.
GeneralVS 7 and error C2491
simos75
0:14 19 Apr '05  
Hi!

Visual Studio 7 implements C++ standard little better than VC 6 did.

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclang/html/_core_Members_of_Class_Templates.asp[^]

Says that C++ member templates are supported as long as they are fully defined within the enclosing class.

This is not the case in InterfaceWrapper.h file. You have to move the member function implementations of CInterfaceCallingWrapper to the definition part. See the link and examples and you get it.

Move all member function implementations and you get rid of error C2491.

Hope this helps!

.Simo
GeneralRe: VS 7 and error C2491
R Dawson
3:58 4 Aug '05  
Good Call!
QuestionRe: VS 7 and error C2491
vincent_denoiseux
2:25 15 Mar '07  
Interesting,

Do you have the final version of the corrected files?
I'm unable to get a working version ...

Many thanks

Vincent

GeneralHelp! Help! Help!
Anonymous
22:56 3 Mar '05  
Why when the programme goes to:
try
{
Doc = CDOMDocumentClass::CreateXMLDOMDocument2();
}
there always an "Exception" be catched??
please, help!!!!!
GeneralRe: Help! Help! Help!
R Dawson
13:16 31 Jul '05  
Do you have the MSXML4 Parser installed and registered?

Try stepping into the call to CreateXMLDOMDocument2() and catching the HRESULT (hr) returned. If you do an 'Error Lookup' on that you may find that the call to CoCreateInstance is returning a 'Class Not Registered' error which may mean you dont have the expected version of MSXML4.DLL installed (or registered).


Last Updated 2 Jan 2002 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010