Microsoft XML Core Services (MSXML) 5.0 for Microsoft Office - DOM Developer's Guide

Source: dynamDOM.cpp

This application creates a simple, but complete, XML DOM object, with <root> as the document element. This element contains three child elements: <node1>, <node2>, and <node3>. The first child element contains character data. The second child element contains a CDATA section. The last child element contains three empty child elements: <subnode1>, <subnode2>, and <subnode3>.

Programmatically, the dynamDOM application performs the following steps:

  1. Creates an XML DOM instance (pXMLDom).
  2. Calls the createProcessInstruction method on pXMLDom. This creates a processing instruction node (pi) targeted for XML 1.0.
  3. Calls the appendChild method on pXMLDom. This adds the processing instruction node (pi) to pXMLDom.
  4. Calls the createComment method on the DOM object (pXMLDom) to create a comment node (pc) and then append it pXMLDom.
  5. Creates a <root> element as the document element, with a created attribute whose value is set to a string value of "using DOM". Adds this element (<root>) to the DOM object (pXMLDom).
  6. Creates a <node1> element with some character data as its content. Appends this element (pe) to the <root> element of the DOM object (pXMLDom).
  7. Creates a <node2> element that contains a CDATA section (pcd) with markup text. Appends this element (pe) to the <root> element of the DOM object (pXMLDom).
  8. Creates a <node3> element that contains a DOM document fragment (pdf). This fragment contains three other empty child elements: <subNode1>, <subNode2>, and <subNode3>. The code then appends this element (pe) to the <root> element of the DOM object (pXMLDom).
  9. Saves this dynamically created DOM object to the project's main directory, and prints the XML data in the application console.

C/C++ Source File (dynamDOM.cpp)

#include <stdio.h>
#include <windows.h>
#import <msxml5.dll> raw_interfaces_only
using namespace MSXML2;

// 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( 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(DOMDocument50),
                      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");
    HRCALL( pxmldoc->put_preserveWhiteSpace(VARIANT_TRUE),
            "should never fail");

   return pxmldoc;
clean:
   if (pxmldoc)
    {
      pxmldoc->Release();
    }
   return NULL;
}

VARIANT VariantString(BSTR str)
{
   VARIANT var;
   VariantInit(&var);
   V_BSTR(&var) = SysAllocString(str);
   V_VT(&var) = VT_BSTR;
   return var;
}

// Helper function to display xml parse error:
void ReportParseError(IXMLDOMDocument *pDom, char *desc) {
   IXMLDOMParseError *pXMLErr=NULL;
   BSTR bstrReason = NULL;
   HRESULT hr;
   HRCALL(pDom->get_parseError(&pXMLErr),
            "dom->get_parseError: ");
   HRCALL(pXMLErr->get_reason(&bstrReason),
            "parseError->get_reason: ");
   
   dprintf("%s %S\n",desc, bstrReason);
clean:
   if (pXMLErr) pXMLErr->Release();
   if (bstrReason) SysFreeString(bstrReason);
}

// Helper function to append a whitespace text node to a 
// specified element:
void AddWhiteSpaceToNode(IXMLDOMDocument* pDom,
                   BSTR bstrWs,
                   IXMLDOMNode *pNode)
{
   HRESULT hr;
   IXMLDOMText *pws=NULL;
   IXMLDOMNode *pBuf=NULL;
    HRCALL(pDom->createTextNode(bstrWs,&pws), " ");
    HRCALL(pNode->appendChild(pws,&pBuf)," ");
clean:
    if (pws) pws->Release();
    pws=NULL;
    if (pBuf) pBuf->Release();
    pBuf=NULL;
}

// Helper function to append a child to a parent node:
void AppendChildToParent(IXMLDOMNode *pChild, IXMLDOMNode *pParent)
{
   HRESULT hr;
   IXMLDOMNode *pNode=NULL;
    HRCALL(pParent->appendChild(pChild, &pNode), "");
clean:
    if (pNode) pNode->Release();
    pNode=NULL;
}

int main(int argc, char* argv[])
{
   IXMLDOMDocument *pXMLDom=NULL;
   IXMLDOMProcessingInstruction *pi=NULL;
   IXMLDOMComment *pc=NULL;
   IXMLDOMElement *pe=NULL;
   IXMLDOMElement *pRoot=NULL;
   IXMLDOMAttribute *pa=NULL;
   IXMLDOMAttribute *pa1=NULL;
   IXMLDOMCDATASection *pcd=NULL;
   IXMLDOMElement *peSub=NULL;
   IXMLDOMDocumentFragment *pdf=NULL;

   
   BSTR bstr = NULL;
   BSTR bstr1 = NULL;
   BSTR bstr_wsn = SysAllocString(L"\n");
   BSTR bstr_wsnt= SysAllocString(L"\n\t");
   BSTR bstr_wsntt=SysAllocString(L"\n\t\t");

   VARIANT var;
   HRESULT hr;

   CoInitialize(NULL);
   VariantInit(&var);
  
   pXMLDom = DomFromCOM();
   if (!pXMLDom) goto clean;

   // Create a processing instruction element.
   bstr = SysAllocString(L"xml");
   bstr1 = SysAllocString(L"version='1.0'");
   HRCALL(pXMLDom->createProcessingInstruction(
                  bstr,bstr1, &pi),
         "createProcessingInstruction:");
   AppendChildToParent(pi, pXMLDom);
   pi->Release();
   pi=NULL;
   SysFreeString(bstr);
   bstr=NULL;
   SysFreeString(bstr1);
   bstr1=NULL;

   // Create a comment element.
   bstr = SysAllocString(L"sample xml file created using XML DOM object.");
   HRCALL(pXMLDom->createComment(bstr, &pc), "");
   AppendChildToParent(pc, pXMLDom);
   SysFreeString(bstr);
   bstr=NULL;
   pc->Release();
   pc=NULL;

   // Create the root element.
   bstr = SysAllocString(L"root");
   HRCALL(pXMLDom->createElement(bstr, &pRoot), "");
   SysFreeString(bstr);
   bstr = NULL;

   // Create a "created" attribute for the <root> element, and
   // assign the "using dom" character data as the attribute value.
   bstr = SysAllocString(L"created");
   var = VariantString(L"using dom");
   HRCALL(pXMLDom->createAttribute(bstr,&pa), "");
   HRCALL(pa->put_value(var), "");
   HRCALL(pRoot->setAttributeNode(pa, &pa1), "");
   AppendChildToParent(pRoot, pXMLDom);
   SysFreeString(bstr);
   bstr=NULL;
   if (pa1) {
      pa1->Release();
      pa1=NULL;
   }
   pa->Release();
   pa=NULL;
   VariantClear(&var);

   // Next, we will create and add three nodes to the <root> element.

   // Add NEWLINE+TAB for identation before <node1>.
   AddWhiteSpaceToNode(pXMLDom, bstr_wsnt, pRoot);
   
   // Create a <node1> to hold text content.
   bstr = SysAllocString(L"node1");
   HRCALL(pXMLDom->createElement(bstr,&pe),"");
   SysFreeString(bstr);
   bstr=NULL;

   bstr=SysAllocString(L"some character data");
   HRCALL(pe->put_text(bstr), "");
   SysFreeString(bstr);
   bstr=NULL;

   // Append <node1> to <root>.
   AppendChildToParent(pe, pRoot);
   pe->Release();
   pe=NULL;
   
   // Add NEWLINE+TAB for identation before <node2>.
   AddWhiteSpaceToNode(pXMLDom, bstr_wsnt, pRoot);
   
   // Create a <node2> to hold a CDATA section.
   bstr = SysAllocString(L"node2");
   HRCALL(pXMLDom->createElement(bstr,&pe),"create <node2> ");
   SysFreeString(bstr);
   bstr=NULL;

   bstr = SysAllocString(L"<some mark-up text>");
   HRCALL(pXMLDom->createCDATASection(bstr,&pcd),"");
   SysFreeString(bstr);
   bstr=NULL;
   if (!pcd) goto clean;

   AppendChildToParent(pcd, pe);
   pcd->Release();
   pcd=NULL;
   
   // Append <node2> to <root>.
   AppendChildToParent(pe, pRoot);
   pe->Release();
   pe=NULL;

   // Add NEWLINE+TAB for identation before <node3>.
   AddWhiteSpaceToNode(pXMLDom, bstr_wsnt, pRoot);

   // Create <node3> to hold a doc fragment with three sub-elements.
   bstr = SysAllocString(L"node3");
   HRCALL(pXMLDom->createElement(bstr,&pe),"");
   SysFreeString(bstr);
   bstr=NULL;

   // Create a document fragment to hold three sub-elements.
   HRCALL(pXMLDom->createDocumentFragment(&pdf), "");

   // Add NEWLINE+TAB+TAB for identation before <subnode1>.
   AddWhiteSpaceToNode(pXMLDom, bstr_wsntt, pdf);

   // Create <subnode1>.
   bstr = SysAllocString(L"subnode1");
   HRCALL(pXMLDom->createElement(bstr,&peSub), "");
   SysFreeString(bstr);
   bstr=NULL;

   // Add <subnode1> to pdf.
   AppendChildToParent(peSub, pdf);
   peSub->Release();
   peSub=NULL;

   // Add NEWLINE+TAB+TAB for identation before <subnode2>.
   AddWhiteSpaceToNode(pXMLDom, bstr_wsntt, pdf);

   // Create and append <subnode2>.
   bstr = SysAllocString(L"subnode2");
   HRCALL(pXMLDom->createElement(bstr,&peSub), "");
   SysFreeString(bstr);
   bstr=NULL;

   // Add <subnode1> to pdf.
   AppendChildToParent(peSub, pdf);
   peSub->Release();
   peSub=NULL;

   // Add NEWLINE+TAB+TAB for identation before <subnode3>.
   AddWhiteSpaceToNode(pXMLDom, bstr_wsntt, pdf);

   // Create and append <subnode3>.
   bstr = SysAllocString(L"subnode3");
   HRCALL(pXMLDom->createElement(bstr,&peSub), "");
   SysFreeString(bstr);
   bstr=NULL;
   if (!peSub) goto clean;

   // Add <subnode1> to pdf.
   AppendChildToParent(peSub, pdf);
   peSub->Release();
   peSub=NULL;

   // Add NEWLINE+TAB after </subnode> in pdf.
   AddWhiteSpaceToNode(pXMLDom, bstr_wsnt, pdf);
   // Append pdf to <node3> (pe).
   AppendChildToParent(pdf, pe);

   // Append <node3> to <root>.
   AppendChildToParent(pe, pRoot);

   // Add NEWLINE for identation before </root>.
   AddWhiteSpaceToNode(pXMLDom, bstr_wsn, pRoot);

   HRCALL(pXMLDom->get_xml(&bstr), "dom->get_xml");
   dprintf("Dynamically created DOM:\n%S\n", bstr);

   VariantClear(&var);
   var = VariantString(L"dynamDom.xml");
   HRCALL(pXMLDom->save(var), "");
   dprintf("DOM saved to dynamDom.xml\n");

clean:
   if (bstr) SysFreeString(bstr);
   if (bstr1) SysFreeString(bstr1);
   if (&var) VariantClear(&var);

   if (pXMLDom) pXMLDom->Release();
   if (pRoot) pRoot->Release();
   if (pe) pe->Release();
   if (peSub) peSub->Release();
   if (pi) pi->Release();
   if (pa) pa->Release();
   if (pa1) pa1->Release();
   if (pc) pc->Release();
   if (pcd) pcd->Release();
   if (pdf) pdf->Release();

   CoUninitialize();
   return 0;
}

To add dynamDOM.cpp to the project

  1. Create a new C++ source file. For detailed instructions on how to do this, see Set Up My Visual C++ Project. Name the new file dynamDOM.cpp.
  2. Copy the C/C++ source code above and paste it into the source file you just created.

Next, build and run the dynamDOM project. The result should be the output shown in the following topic.