MSXML in C++ but as elegant as in C# (Part 1)

20 בדצמבר 2008

תגיות: ,
3 תגובות

Well, I did say that I would change subjects from time to time : )

This week I taught a class of C++ Programmers how to use MSXML and I sat down to write samples and demos for the lesson.

Well, I love C++, but C# has spoiled me.

Programming COM directly can get ugly and working with MSXML in C++ is no exception: all those AddRefs and Releases you need to call, HRESULTS you need to handle, and CLSIDs you need to find.

Code that accesses COM directly is often error prone, difficult to understand and difficult to maintain. (Isn’t it interesting how ugly code nearly always has these problems whereas beautiful code nearly always doesn’t?).

Well, eventually I created some elegant examples, and I would like to share them with you.

My benchmark for elegance was quite simple. It should look like it does in C# : )

Here is an example of what I mean. The following example (from MSDN) loads an XML as a DOM document. Scroll down below this long snippet to see the goal that I set myself for the samples.

// LoadDOM.cpp : Defines the entry point for the console application.

//

 

#include "stdafx.h"

#include <stdio.h>

#include <windows.h>

#import <msxml3.dll> raw_interfaces_only

 

// Macro that calls a COM method returning HRESULT value:

#define HRCALL(a, errmsg) \

do { \

    hr = (a); \

    if (FAILED(hr)) { \

        dprintf( "%s:%d  HRCALL Failed: %s\n  0x%.8x = %s\n", \

                __FILE__, __LINE__, errmsg, hr, #a ); \

        goto clean; \

    } \

} while (0)

 

// Helper function that put output in stdout and debug window

// in Visual Studio:

void dprintf( char * format, …)

{

    static char buf[1024];

    va_list args;

    va_start( args, format );

    vsprintf_s( buf, format, args );

    va_end( args);

    OutputDebugStringA( buf);

    printf("%s", buf);

}

 

// Helper function to create a DOM instance:

IXMLDOMDocument * DomFromCOM()

{

   HRESULT hr;

   IXMLDOMDocument *pxmldoc = NULL;

 

   HRCALL( CoCreateInstance(__uuidof(MSXML2::DOMDocument30),

                  NULL,

                  CLSCTX_INPROC_SERVER,

                  __uuidof(IXMLDOMDocument),

                  (void**)&pxmldoc),

                  "Create a new DOMDocument");

 

    HRCALL( pxmldoc->put_async(VARIANT_FALSE),

            "should never fail");

    HRCALL( pxmldoc->put_validateOnParse(VARIANT_FALSE),

            "should never fail");

    HRCALL( pxmldoc->put_resolveExternals(VARIANT_FALSE),

            "should never fail");

 

    return pxmldoc;

clean:

    if (pxmldoc)

    {

        pxmldoc->Release();

    }

    return NULL;

}

 

int _tmain(int argc, _TCHAR* argv[])

{

    IXMLDOMDocument *pXMLDom=NULL;

    IXMLDOMParseError *pXMLErr=NULL;

    BSTR bstr = NULL;

    VARIANT_BOOL status;

    VARIANT var;

    HRESULT hr;

 

    CoInitialize(NULL);

 

    pXMLDom = DomFromCOM();

    if (!pXMLDom) goto clean;

 

    VariantInit(&var);

    V_BSTR(&var) = SysAllocString(L"stocks.xml");

    V_VT(&var) = VT_BSTR;

    HRCALL(pXMLDom->load(var, &status), "");

 

    if (status!=VARIANT_TRUE) {

        HRCALL(pXMLDom->get_parseError(&pXMLErr),"");

        HRCALL(pXMLErr->get_reason(&bstr),"");

        dprintf("Failed to load DOM from stocks.xml. %S\n",

                    bstr);

        goto clean;

    }

    HRCALL(pXMLDom->get_xml(&bstr), "");

    dprintf("XML DOM loaded from stocks.xml:\n%S\n",bstr);

 

clean:

    if (bstr) SysFreeString(bstr);

    if (&var) VariantClear(&var);

    if (pXMLErr) pXMLErr->Release();

    if (pXMLDom) pXMLDom->Release();

 

    CoUninitialize();

    return 0;

}

I don’t know about you but I cringed on every line.

Why can’t COM in C++ look as elegant as this equivalent code in (C#)?

using System;

using System.Xml;

 

static void Main(string[] args)

{

    try

    {

        XmlDocument xmlDoc = new XmlDocument();

 

        xmlDoc.Load("stocks.xml");

        Console.WriteLine("Loaded stocks.xml");

    }

    catch (Exception e)

    {

        Console.WriteLine(e.Message);

    }

}

Well, it can!

As you probably know, there are some neat tricks in C++ that allow you to clean up the mess and hide the details of resource management and type conversions. I am referring to RAI (resource allocation as initialization), smart pointers and operator overloads.

MSDN does document how to use this smart approach for MSXML, but, in my humble opinion, these tools can do even more than the samples there demonstrate.

In my next post(s) I will provide a solution with 5 samples implementing the most basic MSXML services:

  1. DOMAndXPath

    Load an XML file into a DOM, recursively traverse and display its nodes, select and display a subset of its nodes using XPath.

  2. SAXReader

    Read an XML file using the SAX model.

  3. XMLBuilder

    Build a DOM model programmatically and displaying the result as an XML string.

  4. XSDValidator

    Validate an XML file against a schema in three scenarios:

    • The schema is inline
    • The schema is stored in a separate file referenced by the xml file.
    • The schema is cached in memory and applied to an xml file.
  5. XSLTTransformer

    Apply a transform stored in a file to XML stored in another file and display the result.

Well, there are quite a few of samples like these to be found on the net.

But my emphasis is not on the what the samples can do, but what they look like. In my humble opinion, if I can get them to look as clean and elegant as they would in C#, not only will they do the job, but they will be easy to understand, easy to maintain and error free.

To demonstrate this, each project comes as a pair for comparison: One in C# and one in C++.

The C++ code compiles and runs in both Visual C++ 6.0 and in Visual Studio 2008. That might sound trivial, but in the case of MSXML, unfortunately it is not. I will point out the differences when we go through the code.

Stay tuned for Part 2

הוסף תגובה
facebook linkedin twitter email

כתיבת תגובה

האימייל לא יוצג באתר. שדות החובה מסומנים *

3 תגובות

  1. Nathan23 באפריל 2009 ב 6:27

    Any chance you might post the next posts showing these examples? C++ usage of MSXML is normally so ugly I'm wondering what you decided to use – ATL smart wrappers – etc?

    הגב
  2. Nathan23 באפריל 2009 ב 6:28

    Oops – I didn't see the other posts show up before because I came in off a search engine and they don't rank higher than this one. You should tag them with MSXML so it shows in your tag cloud.
    Cheers,
    Nathan

    הגב
  3. Victoria 27 במרץ 2012 ב 21:11

    So-so. Something was not impressed.

    הגב