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

26 בדצמבר 2008

תגיות: ,
אין תגובות

Part 1 is the first post in this series.

In Part 4 I described the first of the 5 project pairs provided with this article, DOMAndXPath.

In this post I will review the second project – SAXReader. The SAX programming model is very different to the DOM model: SAX models the parser, whereas DOM models the XML document. SAX provides a forward only push model, where as DOM provides random access to nodes in the document. The SAXReader project is based on the MSDN example you can find here. It defines a content handler, attaches it to a SAX reader and then directs the SAX reader to load an XML file. Appropriate content handler methods are called when the SAX reader encounters significant syntactical elements in the file.

The .Net Framework does not come equipped with a push-mode SAX reader. The XmlReader, the nearest equivalent provides a pull-mode reader.

So, what C# code can we use to compare our C++ code to for elegance?

In this project I used COM interop to call the SAX COM interfaces from managed code. I used the Add Reference wizard to add the MSXML6 dll from the COM tab. This provides a proxy for all the COM visible types in the MSXML2 namespace (recall, we are using the MSXML2 interface in C++ too).

I will admit, that I didn’t clean up the implementation of the content handler, but lets compare the main method in C++ and in C#, which are quite similar. Here they are, side-by-side:

C++ C#

#include "stdafx.h"

 

#include "MyContentHandler.h"

#include "SAXErrorHandlerImpl.h"

 

inline unsigned short* ToUnsignedShort (_bstr_t bstr)

{

    return (unsigned short*) (wchar_t*) bstr;

}

 

void ReadXmlFile (char* xmlFileName)

{

    ISAXXMLReaderPtr saxReader (CLSID_SAXXMLReader);

 

    if (saxReader == NULL)

    {

        throw Error ("Failed to create ISAXXMLReader");

    }

 

    MyContentHandler *contentHandler = new MyContentHandler();

 

    HRESULT hr = saxReader->putContentHandler(contentHandler);

    if (FAILED(hr))

    {

        throw Error ("Failed to set content handler");

    }

 

    // An illustration how to set other handlers

 

    // SAXErrorHandlerImpl * eh = new SAXErrorHandlerImpl();

    // hr = saxReader->putErrorHandler(eh);

 

    // SAXDTDHandlerImpl * dh = new SAXDTDHandlerImpl();

    // hr = saxReader->putDTDHandler(dh);

 

    hr = saxReader->parseURL (ToUnsignedShort(xmlFileName));

 

    saxReader->putContentHandler(NULL);

    delete contentHandler;

 

    if (FAILED(hr))

    {

        throw Error ("Failed to parse file with SAX Reader");

    }

}

 

void main(int argc, char* argv[])

{

    ComInit comInit;

 

    try

    {

        ReadXmlFile ("Sample.xml");

    }

    catch (Error e)

    {

        printf (e);

    }

    catch (_com_error e)

    {

        printf (e.ErrorMessage());

    }

 

    printf ("\nDone\n");

    _getch ();

}

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace SAXReaderCS

{

    class Program

    {

        void ReadXmlFile(string xmlFileName)

        {

            MSXML2.SAXXMLReader saxReader = new MSXML2.SAXXMLReader();

 

            if (saxReader == null)

            {

                throw new Exception("Failed to create ISAXXMLReader");

            }

 

            saxReader.contentHandler = new MyContentHandler();

 

            // An illustration how to set other handlers

 

            // SAXErrorHandlerImpl * eh = new SAXErrorHandlerImpl();

            // hr = saxReader.putErrorHandler(eh);

 

            // SAXDTDHandlerImpl * dh = new SAXDTDHandlerImpl();

            // hr = saxReader.putDTDHandler(dh);

 

            saxReader.parseURL(xmlFileName);

        }

 

        static void Main(string[] args)

        {

            try

            {

                new Program().ReadXmlFile(

                    @"..\..\Samples\Sample.xml");

            }

            catch (Exception ex)

            {

                Console.WriteLine(ex);

            }

 

            Console.WriteLine("Done");

            Console.ReadLine();

        }

    }

}

As in the DOMAndXPath project (see Part 4) the following key points make our C++ code on the left look like the C# code on the right.

  1. ComInit is used for handling the calls to CoInitialize and CoUninitialize
  2. Exceptions are handled using the _com_error and Error classes.
  3. Smart pointers are used to hold COM interfaces (for instance ISAXXMLReaderPtr).
  4. The _bstr_t class is used to convert an char* string to an unsigned short* string.

In Part 3 I mentioned that some of the tricks we are using are provided by MSXML in a set of interfaces that wrap the raw interfaces. I also mentioned that in some cases we might actually want to import only the raw interfaces. The SAXReader project is one of those cases.

You see, in this project we are not only using COM interfaces implemented by MSXML; we are also implementing our own. For instance the MyContentHandler class implements the ISAXContentHandler interface.

In this case, we do not want burden ourselves with the implementation of two sets of additional interfaces, so we opt out by defining RAW_INTERFACES_ONLY in the stdafx.h precompiled header. This makes sure the raw_interfaces_only keyword is added to the #import statement in ImportMSXML.h.

The interface we are now implementing is therefore only the raw interface. See MyContentHandler.h. There are no smart types or smart pointers there. However, if we were to import the non-raw interfaces two, we have to implement these methods (they would then have the prefix raw_) and the smart set.

At this point, we have reviewed all the key points in the 5 project pairs provided with this article. Hopefully I have convinced you that C++ and MSXML provide us with the tools to write C++ code that is as elegant as C#.

In the next and final post in the article, I will simply be showing the fruit of our work – a comparison of the C++ and the C# code for the remaining 3 projects.

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

כתיבת תגובה

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