perfectxml.com
 Basic Search  Advanced Search   
Topics Resources Free Library Software XML News About Us
  You are here: home Focus MSXML C++ Samples » July 10, 2002 :: DOM Saturday, 23 February 2008
 
Microsoft XML Core Services

Summary
This sample application illustrates the MSXML 4.0 DOM API. This is a Visual C++ 6.0 Win32 console application that uses MSXML 4.0 to load a remote or local XML document, adds an attribute to the document's root element; then uses XPath to search for a node, if found, updates the node's value with the current datetime, else creates a new node and sets its value.


Steps
Steps that we followed to create this application:
1. Start Visual C++ 6.0 and create a new Win32 Console Application project.
2. Add the following lines in the stdafx.h:
#include <TCHAR.H>
#include <stdio.h>
#include <time.h>

#import "msxml4.dll"
//      ^^^^^^^^^^^^
// If this import statement fails, you need to install MSXML 4.0 SP1 from:
//
// http://msdn.microsoft.com/downloads/sample.asp?url=/MSDN-FILES/027/001/766/msdncompositedoc.xml

#include <msxml2.h>
//       ^^^^^^^^^^
// If this include statement fails, you need to install MSXML 4.0 SP1 SDK from:
//
// http://msdn.microsoft.com/downloads/sample.asp?url=/MSDN-FILES/027/001/766/msdncompositedoc.xml
//
// You also need to add the include file and library search path
// to Visual C++'s list of directories (Tools > Options... > Directories).

using namespace MSXML2;

inline void EVAL_HR( HRESULT _hr ) 
   { if FAILED(_hr) throw(_hr); }
							
3. The above lines import the MSXML 4 type library, include the MSXML header file, and contain a tiny utility function to check the HRESULT value.
4. The main function looks like as below:
int main(int argc, char* argv[])
{
	try
	{
		EVAL_HR(CoInitialize(NULL));

		//	Make sure MSXML 4.0 is installed
		if (!isMSXMLInstalled())
			return -1;
		
		IXMLDOMDocument2Ptr pXMLDoc = NULL;
		TCHAR	szHTTPURL[MAX_PATH] = {0};

		_tcscpy(szHTTPURL, g_szHTTPURL);
		_tcscat(szHTTPURL, g_szISBN);

		//	Load the XML document
		if (loadDocument(pXMLDoc, szHTTPURL, true))
		{
			//	Add an attribute and an element
			updateDocument(pXMLDoc);

			//	Save the updated document as a local XML file
			pXMLDoc->save(g_szLocalFile);
		}
		else
		{//	Load Failed
			printMSXMLError(pXMLDoc);
		}

	}
	catch(...)
	{//	Exception handling
	}
	
	CoUninitialize();
	return 0;
}
5. You'll notice that the main function simply calls some utility functions to load the XML document, update it and print an error if the document fails to load. These functions are declared and defined in a header file called Utils.h. Let's look at each function individually.
6. Let's first look at a function that makes sure that MSXML 4.0 is installed:
bool isMSXMLInstalled()
{
	try
	{
		HKEY hKey;
		DWORD retCode;


		retCode = RegOpenKeyEx(HKEY_CLASSES_ROOT, 
					_T("CLSID\\{88d969c0-f192-11d4-a65f-0040963251e5}\\InProcServer32"), 
					0, 
					KEY_QUERY_VALUE, 
					&hKey);

		if (retCode != ERROR_SUCCESS) return false;

		retCode = RegQueryValueEx(hKey, _T(""), NULL, NULL, 
					(LPBYTE)szTemp, &(dwLen = sizeof(szTemp)));

		if (retCode != ERROR_SUCCESS) return false;

		RegCloseKey(hKey);

		double dVer;
		int i;

		for (i = _tcslen(szTemp); i >= 0; --i)
		{
			if (szTemp[i] == _T('\\'))
				break;
		}

		if (_stscanf(szTemp + i + 1, _T("msxml%lf"), &dVer) == 0 || dVer < 4.0)
		{
			_ftprintf(stderr, _T("\nError: MSXML 4.0 is not installed. Exiting.\n"));
			return false;
		}
		else
		{
			return true;
		}
	}
	catch(...)
	{//	Exception handling
	}
	
	return false;
}
7. The above function simply uses the MSXML 4.0 DOMDocument (Msxml2.DOMDocument.4.0) CLSID to search the corresponding COM DLL in the registry. If it is MSXML4.dll, the method returns true, else it returns false.
8. The next function in the Utils.h is loadDocument. It creates MSXML DOMDocument instance using the version dependent ProgID and then loads the document. The method initializes the first parameter (pXMLDoc), and returns True or False based on if the document was successfully loaded or not.
_variant_t loadDocument(IXMLDOMDocument2Ptr& pXMLDoc, LPCTSTR szDocURL, bool bOverHTTP)
{
	_variant_t varLoadResult((bool)FALSE);

	try
	{
		//	Create MSXML DOM object
		EVAL_HR(pXMLDoc.CreateInstance("Msxml2.DOMDocument.4.0"));
	
		//	Load the document synchronously
		pXMLDoc->async = false;

		if (bOverHTTP)
		{
			//	If loading the document over HTTP (see KB Q321125 for details)
			pXMLDoc->setProperty("ServerHTTPRequest", VARIANT_TRUE);
		}

		//	Load the XML document
		varLoadResult = pXMLDoc->load(szDocURL);
	}
	catch(...)
	{//Exception handling
	}

	return varLoadResult;
}
9. Once the XML document is loaded in the DOM tree, the following function is used to add an attribute to the root element, and update/add an element under the root element.
void updateDocument(IXMLDOMDocument2Ptr& pXMLDoc)
{
	try
	{
		IXMLDOMNodePtr pLastSavedNode = NULL;
		IXMLDOMElementPtr pRootElem = NULL;

		//	Get root element
		EVAL_HR(pXMLDoc->get_documentElement(&pRootElem));

		//	Add the ISBN attribute to the root element
		pRootElem->setAttribute("ISBN", g_szISBN);


		//	See KB Article Q313372 for details on 
		//	default namespace and XPath expression evaluation
		TCHAR szTemp[MAX_PATH] = "xmlns:defNS='";

		//	Get the default namespace on the root element
		_bstr_t bstrNamespaceURI =  pRootElem->namespaceURI;
		_tcscat(szTemp, bstrNamespaceURI);
		_tcscat(szTemp, "'");
		
		pXMLDoc->setProperty("SelectionNamespaces", szTemp);

		//	The XPath expression
		_tcscpy(szTemp, "//defNS:");
		_tcscat(szTemp, g_szNode);


		//	See if the "LastSaved" element exists
		pLastSavedNode = pRootElem->selectSingleNode(_bstr_t(szTemp));

		if(pLastSavedNode == NULL)
		{//	Element not found!
			
			_variant_t varNodeType((short)MSXML2::NODE_ELEMENT);

			//	create the LastSaved node
			pLastSavedNode = pXMLDoc->createNode(varNodeType, g_szNode, bstrNamespaceURI);

			//	append this new node under the document root element
			pRootElem->appendChild(pLastSavedNode);

		}

		//	Get current Date and Time
		time_t timCurDate;
		time (&timCurDate);

		//	And set it's value to current date and time
		pLastSavedNode->nodeTypedValue = ctime (&timCurDate);

	}
	catch(...)
	{//Exception Handling
	}
}
10. The above method first gets the document's root element. It then adds/updates the ISBN attribute on the root node.

Then we use the XPath expression and selectSingleNode method to see if the node named LastSaved already exists as a child of the root element. If found, we just update its value, else we create a new node and attach it as a child to the root element.

When creating the new node (by calling createNode), we make sure to give the same namespace as the default namespace on the root element, so that the newly created node does not contain a blank namespace declaration, and in fact belongs to the document's default namespace.
11. The final utility function, printMSXMLError, simply uses the IXMLDOMParseError interface to print some error information on the stderr stream:
void printMSXMLError(const IXMLDOMDocument2Ptr& pXMLDoc)
{
	try
	{
		//	Get parseError interface
		IXMLDOMParseErrorPtr pError = NULL;
		EVAL_HR(pXMLDoc->get_parseError(&pError));

		_ftprintf(stderr, pError->reason);
	}
	catch(...)
	{//Exception handling
	}
}
12. The code uses some global variables defined in another header file named GlobalDefs.h:
LPCTSTR	g_szHTTPURL	= 
_T("http://www.PerfectXML.net/WebServices/SalesRankNPrice/BookService.asmx/GetAll?ISBN=");
LPCTSTR	g_szISBN		= _T("0072223693");

//	Uncomment following two lines in case not connected to the internet
//LPCTSTR g_szHTTPURL		= _T("c:\\SalesRankNPrice.xml");
//LPCTSTR	g_szISBN		= _T("");


LPCTSTR g_szLocalFile	= _T("c:\\SalesRankNPrice.xml");
LPCTSTR g_szNode		= _T("LastSaved");

#define TEMP_SIZE  _MAX_PATH               // size of short buffer
static _TCHAR   szTemp[TEMP_SIZE];         // multipurpose buffer on stack
static DWORD    dwLen;                     // buffer size



Here is the text from the ReadMe.TXT file included with the code download for this sample application:
========================================================================
       CONSOLE APPLICATION : MSXML4DOM
========================================================================

	MSXML4DOM.cpp
	Sample code to illustrate using MSXML 4.0 (SP1) basic DOM 
	(Document Object Model) features in C++.

	This application creates 
		a.) loads an XML document from an HTTP location 
				(
					actually, sends a HTTP GET request to a 
					Web service (SalesRankNPrice) method.
				)
				
		b.) Modifies that document
				(	
					creates the element named LastSaved
					under the root element, and sets its value
					to current date and time.
					Also adds the ISBN attribute to the root element
				)
		c.) Saves the updated document on the local hard disk

	After the program finishes, open the C:\SalesRankNPrice.xml (g_szLocalFile)
	file using Internet Explorer to see the results.

--------------------------------------------------------------
	Copyright (C) 2002 http://www.perfectxml.com

	Created: July 10, 2002

	Author: perfectxml.com Team (msxml@perfectxml.com)

	See http://www.perfectxml.com/CPPMSXML/20020710.asp for details.

/////////////////////////////////////////////////////////////////////////////


More Information


  Contact Us | E-mail Us | Site Guide | About PerfectXML | Advertise ©2004 perfectxml.com. All rights reserved. | Privacy