XML Object Model

XML Object Model


This document defines the eXtensible Markup Language (XML) Object Model for Microsoft Internet Explorer 4.0. XML is a simplified subset of SGML, designed specifically for Web applications. Unlike HTML, in which every tag or element needs to be known in advance, the XML elements or attributes do not. An XML parser can use a document type definition (DTD) for a specific XML document if one is available.

The most generic object model for an XML document will be able to take in any XML document as input and provide to the application a means of manipulating this XML document. For an FAQ on XML, please see http://www.ucc.ie/xml/.

XML-based documents are being used for at least two different applications for Microsoft® Internet Explorer 4.0:

The interfaces in the following section describe a minimal set of objects that can be used to construct an object model for XML. In the future (post Internet Explorer 4.0), this object model will be extended to allow for a richer set of methods. The goals for the design of this set of interfaces as well as the justification for those goals are, in order of priority:

arrowy.gifObject Model Overview

arrowy.gifReference

arrowy.gifSamples

Object Model Overview

The object model is free-threaded and hence no marshaling is required.

An XML document can be thought of as a tree. A container tag is a nonleaf node in this tree. Tags that do not contain any other tags end up as leaf nodes of this tree. A root element whose children are all the top-level nodes is imposed.

Reference

- contains the following references:
IXMLDocument Interface IDispatch
get_root
get_fileSize
get_fileModifiedDate
get_fileUpdatedDate
get_URL
put_URL
get_mimeType
get_readyState
get_charset
put_charset
get_version
get_doctype
createElement
IXMLElement Interface IDispatch
get_tagName
put_tagName
get_parent
set_Attribute
get_Attribute
removeAttribute
get_children
get_type
get_text
put_text
addChild
removeChild
IXMLElementCollection Interface get_length
put_length
get_newEnum
item

XML Document

The object model allows an application to load an XML document and manipulate it. The document can be loaded by specifying a URL moniker or by simply providing a stream. The application can then get several properties of the document, such as URL, mime type, and created/accessed/modified time. The entire XML document is navigated by starting at the root element. In addition, the object model provides for notifications on the addition of immediate children of any given node.

The XML object inherits from the following interfaces:
IUnknown
IDispatch
IXMLDocument
IXMLError
IPersistMoniker
IPersistStream
IPersistStreamInit

The application can use either IPersistMoniker::Load or IPersistStreamInit::Load to load an XML document. It can use IPersistStreamInit::Save to save the XML document to a stream. You can also use the IXMLDocument::put_URL

IXMLDocument::IDispatch

IXMLDocument is the one new interface; its definition in Msxml.idl is given below. This is followed by a detailed description of each method in this interface.

{
[propget, id(DISPID_XMLDOCUMENT_ROOT)] HRESULT root ([retval, out] IXMLElement * * p);
[propget, id(DISPID_XMLDOCUMENT_FILESIZE)] HRESULT fileSize([retval, out] BSTR * p);
[propget, id(DISPID_XMLDOCUMENT_FILEMODIFIEDDATE)] HRESULT fileModifiedDate([retval, out] BSTR * p);
[propget, id(DISPID_XMLDOCUMENT_FILEUPDATEDDATE)] HRESULT fileUpdatedDate([retval, out] BSTR * p);
[propget, id(DISPID_XMLDOCUMENT_URL)] HRESULT URL([retval, out] BSTR * p);
[propput, id(DISPID_XMLDOCUMENT_URL)] HRESULT URL([in] BSTR p);
[propget, id(DISPID_XMLDOCUMENT_MIMETYPE)] HRESULT mimeType([retval, out] BSTR * p);
[propget, id(DISPID_XMLDOCUMENT_READYSTATE)] HRESULT readyState([retval, out]long *pl);
[propget, id(DISPID_XMLDOCUMENT_ENCODING)] HRESULT encoding([retval, out]BSTR *p);
[propput, id(DISPID_XMLDOCUMENT_ENCODING)] HRESULT encoding([in]BSTR p);
[propget, id(DISPID_XMLDOCUMENT_VERSION)]  HRESULT version([retval, out]BSTR *p);
[propget, id(DISPID_XMLDOCUMENT_DOCTYPE)]  HRESULT doctype([retval, out]BSTR *p);
[id(DISPID_XMLDOCUMENT_CREATEELEMENT)] HRESULT createElement([in] VARIANT vType, [optional, in] VARIANT var1, [retval, out] IXMLElement * * ppElem);
IXMLDocument::get_root
HRESULT IXMLDocument::get_root(
    IXMLElement **ppElem)

Returns the root element of the document. The CHANNEL element is considered the root element. The caller must call Release on the document.

ppElem
Indirect pointer in which to return the address of the root element IXMLElement interface.
IXMLDocument::get_fileSize
HRESULT IXMLDocument::get(
    BSTR  * p)

Returns the file size of the document.

p
Pointer to a string containing the file size.
IXMLDocument::get_fileModifiedDate
HRESULT IXMLDocument::get_fileModifiedDate(
    BSTR  * p)

Returns the date when the file was last modified.

p
Pointer to a string containing the modified date.

This method is not yet implemented.

IXMLDocument::get_fileUpdatedDate
HRESULT IXMLDocument::get_fileUpdatedDate(
    BSTR  * p)

Returns the date when the file was last updated.

p
Pointer to a string containing the updated date.

This method is not yet implemented.

IXMLDocument::get_URL
HRESULT IXMLDocument::get_URL(
     BSTR  * p)

Returns the URL assciated with the document, if one exist.

p
Pointer to a string containing a URL value.
IXMLDocument::put_URL
HRESULT IXMLDocument::put_URL(
     BSTR  p)

Loads a new document based on the URL provided. The current document will be destroyed and the new document will be loaded in its place.

p
String containing a URL address.
IXMLDocument::get_mimeType
HRESULT IXMLDocument::get_mimeType(
    BSTR   p)

Returns the MIME (Multipurpose Internet Mail Extension) type. MIME is a set of enhancements to SMTP allowing an Internet message to include a mixture of audio, image, video, and text components, and to accommodate a variety of international character sets. MIME messages are identified by a MIME header in the top level of the SMTP header. The MIME mechanisms are specified in the RFC 1341 document.

p
String containing a MIME type.
IMXLDocument::get_readyState
HRESULT IXMLDocument::get_readyState(
    LONG *pl)

Determines if the document is done loading.

pl
Address of a storage area in which to return the readystate property.

The returned readystate property, such as READYSTATE_LOADED, READYSTATE_LOADING, or READYSTATE_COMPLETE, indicates whether the document has been loaded completely. If the document is still loading, the returned value is READYSTATE_LOADING; if it is successfully loaded, it is READYSTATE_COMPLETE.

IXMLDocument::get_charset
HRESULT IXMLDocument::get_charset(
    BSTR *pOutString)

Returns a string which specifies the character set of the input document according to ISO standards.

pOutString
Pointer to a string containing character set format.
IXMLDocument::put_charset
HRESULT IXMLDocument::put_charset(
    BSTR *p)

Specifies the output character set of the a document according to ISO standards.

p
Pointer to a string containing character set format.
IXMLDocument::get_version
HRESULT IXMLDocument::get_version(
    BSTR *p)

Returns the version of the XML specification being used.

p
Pointer to a string containing the version of XML being used.

The caller needs to deallocate the string using SysFreeString.

IXMLDocument::get_doctype
HRESULT IXMLDocument::get_doctype(
    BSTR *p)

Returns the content specified in the !DOCTYPE element.

p
Pointer to a string that contains document specific information.
IXMLDocument::createElement
HRESULT IXMLDocument::createElement(

    [in] VARIANT vType, 
    [optional, in] VARIANT var1,
    [retval, out] IXMLElement **ppElem)

Creates a new element that can then be added to a portion of the tree.

vType
Variant of type VT_I4, which contains an integer from the xmlelemTYPE enumeration, and indicates the type of the element that is desired:
typedef enum xmlelemTYPE {  
    XMLELEMTYPE_ELEMENT,
    XMLELEMTYPE_TEXT,
    XMLELEMTYPE_COMMENT,
    XMLELEMTYPE_DOCUMENT,
    XMLELEMTYPE_DTD,
    XMLELEMTYPE_OTHER
} XMLELEM_TYPE;
var1
Optional. Defines a string used by put_tagName to bind the name to the newly formed element.
ppElem
Indirect pointer in which to return the desired element.

XML Element

Each XML tag is represented by an object. This object derives from the following interfaces:
IUnknown
IDispatch
IXMLElement

IXMLElement::IDispatch

The definition of IXMLElement in Msxml.idl is shown below.

{
[propget, id(DISPID_XMLELEMENT_TAGNAME)] HRESULT tagName([retval, out] BSTR * p);
[propput, id(DISPID_XMLELEMENT_TAGNAME)] HRESULT tagName([in] BSTR p);
[propget, id(DISPID_XMLELEMENT_PARENT)] HRESULT parent([retval, out]IXMLElement **ppParent);
[id(DISPID_XMLELEMENT_SETMEMBER)] HRESULT setAttribute([in] BSTR strPropertyName,[in] VARIANT PropertyValue);
[id(DISPID_XMLELEMENT_GETMEMBER)] HRESULT getAttribute([in] BSTR strPropertyName,[retval, out] VARIANT* PropertyValue);
[id(DISPID_XMLELEMENT_REMOVEMEMBER)] HRESULT removeAttribute([in] BSTR strPropertyName);
[propget, id(DISPID_XMLELEMENT_CHILDREN)] HRESULT children([retval, out] IXMLElementCollection * * pp); 
[propget, id(DISPID_XMLELEMENT_TYPE)] HRESULT type([retval, out] long *plType);
[propget, id(DISPID_XMLELEMENT_TEXT)] HRESULT text([retval, out] BSTR *p);
[propput, id(DISPID_XMLELEMENT_TEXT)] HRESULT text([in] BSTR p);
[id(DISPID_XMLELEMENT_ADDCHILD)] HRESULT addChild([in] IXMLElement *pChildElem, long lIndex, long lReserved); 
[id(DISPID_XMLELEMENT_REMOVECHILD)] HRESULT removeChild([in]IXMLElement *pChildElem);
IXMLElement::get_tagName
HRESULT IXMLElement::get_tagName(
    BSTR * pTagName)

Returns the name of the tag as a string.

pTagName
Indirect pointer in which to return the tag name.

The caller can use SysFreeString to free the string. The name is always all uppercase. The name of the comment element is returned as "!". The other META tags have names that correspond to the special character followed by the META tag name, such as the following:

The name of <?XML ...> is "?XML".

IXMLElement::put_tagName
HRESULT IXMLElement::put_tagName(
    BSTR  pTagName)

Sets the name of the tag and stores a copy of the string.

pTagName
Pointer to a string containing the tag name.

The caller can use SysFreeString to free the string. The name is always all uppercase. The name of the comment element is returned as "!". The other META tags have names that correspond to the special character followed by the META tag name, such as the following:

The name of <?XML ...> is "?XML".

IXMLElement::get_parent
HRESULT IXMLElement::get_parent(
    IXMLElement ** pParent)

Every element in the tree except the root of a document has a parent. This parent element is AddRef'ed before being returned, and it is up to the caller to call Release on the returned parent element.

pParent
Indirect pointer in which to return an address of the parent element. The parent element is always nonnull except for the root.
IXMLElement::setAttribute
HRESULT IXMLElement::setAttribute(
    BSTR strPropertyName, 
    VARIANT PropertyValue)

Allows the setting of an attribute of this element.

strPropertyName
Name of the attribute
PropertyValue
Value of the attribute. The allowed input type is VT_BSTR.

The attribute's name and its new value are provided. The previous value of this attribute is lost. A private copy of the input value is made. The attribute value is always stored as a string. Hence, it should be sent in as a string.

IXMLElement::get_Attribute
HRESULT IXMLElement::get_Attribute(
    BSTR strPropertyName, 
    VARIANT* PropertyValue)

Allows for the retrieval of an attribute's value.

strPropertyName
Attribute name.
PropertyValue
Indirect pointer in which to return the value. Always returned as a BSTR.

An attribute's name is provided. The value of the attribute is returned as a variant. The returned variant is always of type VT_BSTR. The caller must release the value by calling VariantClear.

IXMLElement::removeAttribute
HRESULT IXMLElement::removeAttribute(
    BSTR strPropertyName)

Deletes an attribute from an element. The attribute is specified using the attribute name.

strPropertyName
Attribute name.
IXMLElement::get_children
HRESULT IXMLElement::get_children(
    IXMLElementCollection * * ppChildren)

Retrieves the collection of an element's children.

ppChildren
Indirect pointer in which to return an address of the element collection. The ppChildren parameter is NULL if no children are present.

The collection allows the application to make queries about the size of the collection as well as enumerate through the children or access any of them by index. If no children exist, ppChildren is set to NULL and the return code is S_FALSE. The caller must call (ppChildren)->Release when it is done using this collection. Further discussion of IXMLElementCollection is available later in this document.

IXMLElement::get_type
HRESULT IXMLElement::get_type(
    long *plType)

plType
Indirect pointer to return the type.

Each node of the tree is labeled as being one of the following types:

typedef  enum xmlelemTYPE {  
    XMLELEMTYPE_ELEMENT,
    XMLELEMTYPE_TEXT,
    XMLELEMTYPE_COMMENT,
<!--    XMLELEMTYPE_DOCUMENT,
    XMLELEMTYPE_DTD, -->
    XMLELEMTYPE_OTHER
} XMLELEM_TYPE;

For Internet Explorer 4.0, the interesting element types are XMLELEMTYPE_ELEMENT, XMLELEMTYPE_TEXT, and XMLELEMTYPE_COMMENT.

In the following code, both the CHANNEL and the TITLE elements are marked as being of type XMLELEMTYPE_ELEMENT. Their names are returned as "CHANNEL" and "TITLE", respectively. (The names are always all uppercase.) The TITLE element has one child, which is a text element marked as being of type XMLELEMTYPE_TEXT.

<CHANNEL>
    <title> Breaking News </title>
    <!-- This is a comment element-->
</CHANNEL>
IXMLElement::get_text
HRESULT IXMLElement::get_text(
    BSTR *pContent)

Returns the content of text and comment elements.

pContent
Indirect pointer in which to return the content. The caller must free any returned content using SysFreeString.

The content of a text element is the text between the tags. Consider the following elements:

<CHANNEL>
<TITLE>Top Stories</TITLE>
....
....
...
</CHANNEL>

The text element child of the TITLE element has the content "Top Stories". Here the TITLE element has one child, which is the text element, and this child has the content "Top Stories".

However, calling get_text on the TITLE element strips markup from the content within the TITLE tag. Use this method to get all the text in a tag. For example:

<TITLE>
This is <H> bold </H> text
</TITLE>

A good downlevel client should use "This is bold text" after stripping away any markup by calling get_text on the TITLE element.

Finally, the content of a comment element is the comment. For example:

<! This is a comment />

has a name "!" and has the content " This is a comment ".

IXMLElement::put_text
HRESULT IXMLElement::put_text(
    BSTR pContent)

Adds or changes the content of text and comment elements.

pContent
Content. A private copy of this string is made.
IXMLElement::addChild
HRESULT IXMLElement::addChild(
    [in] IXMLElement *pChildElem, 
    long lIndex, 
    long lReserved)

Adds a child to this element.

pChildElem
Address of the new child element.
lIndex
Index of the child collection at which this child should be added. If this is zero, it is added at the first element of the collection. If this is less than zero, the element is simply added to the end of the child collection.
lReserved
Should always be -1

All the children and descendants of this new child element remain attached as before. Any element can have only one parent element, so the previous parent will lose this child from its subtree. Any threads that have already traversed the tree and expect this element to have the old parent will fail unpredictably. It may be noted that it is possible to use this method to break off a subtree from one XML document and attach it to another XML document. However, there are some stringent restrictions on when this can be done:

If these are not adhered to, an error will be returned.

Also, lReserved should always be -1.

The results when using the IXMLElementCollection::get_newEnum method from a collection obtained from this element are undefined if the enumerator is not reset.

Finally, the implementation of breaking off a node that has many children and attaching it to a tree of another document is somewhat more expensive than breaking off a node and attaching it to another node of the same document.

IXMLElement::removeChild
HRESULT IXMLElement::removeChild(
    [in] IXMLElement *pChildElem)

Removes a child of this element.

pChildElem
Address of the child element that should be removed.

The element remains in memory and can be reattached to a tree using addChild. All the children and descendants of this child element remain attached as before. Any threads that have already traversed the tree and expect this element to have the old parent will fail unpredictably. It is possible to use this method to break off a subtree from one XML document and attach it to another XML document. However, there are restrictions as explained in IXMLElement::addChild.

The results when using the IXMLElementCollection::get_newEnum method from a collection obtained from this element (the parent) are undefined if the enumerator is not reset.

Element Collections

The children of an element are manipulated in this object model using collections. It is possible to enumerate the members of a collection as well as to access them by index. It is also possible to create a specialized collection of all the elements that have a certain name. Such a specialized collection is then manipulated in an identical fashion.

The XML object derives from the following interfaces:
IDispatch
IXMLElementCollection
IConnectionPointCB
IConnectionPointContainer
IXMLNotifySink

A collection of the children of an element pElem is obtained by using the get_children method as follows:

IXMLElement *pElem = NULL;
IXMLElementCollection *pChildren = NULL;
....
....
....
hr = pElem->get_children(&pChildren);
if(SUCCEEDED(hr) && pChildren)
{

}

Following is the portion of the IDL file that describes collections.

interface IXMLElementCollection : IDispatch
{
    [propput, id(DISPID_XMLELEMENTCOLLECTION_LENGTH)] 
HRESULT length([in] long v);
    [propget, id(DISPID_XMLELEMENTCOLLECTION_LENGTH)] 
HRESULT length([retval, out] long * p);

    [propget, restricted, hidden, id(DISPID_XMLELEMENTCOLLECTION_NEWENUM)] 
HRESULT _newEnum([retval, out] IUnknown ** ppUnk);

    [id(DISPID_XMLELEMENTCOLLECTION_ITEM)]
HRESULT item([optional, in] VARIANT var1,[optional, in] 
VARIANT var2,[retval, out] IDispatch ** ppDisp);
};

The element collection object also supports IConnectionPointContainer.

The application can enumerate connection points or call IConnectionPointContainer::FindConnectionPoint and find the connection point for IID_IXMLElementNotificationSink. The application then needs to implement the interface.

interface IXMLElementNotificationSink : IDispatch
{
    [id(DISPID_XMLNOTIFSINK_CHILDADDED)] 
HRESULT ChildAdded([in]IDispatch *pChildElem); 
// If input param is NULL then parse of this elem is done.
}

The sink is called whenever any child is added to the parent of this collection, and the input parameter indicates the child that was added to it. The correct behavior of the application is to either use the input pointer by holding a reference to it or to evaluate the length of the collection again and enumerate. It is dangerous to make assumptions about the index of a given element when new elements are still being added.

Between calls to IXMLElement::addChild, the client application must call IXMLCollection::Get_length on the parent collection to insure that the application has the correct length and can request the correct index to an element when needed.

When it is guaranteed that no further elements will be added as children to this parent node, this API is called with a NULL parameter. It is guaranteed that this sink will be called at least once with a NULL parameter. The sink should then disconnect itself from this connection point when it knows that no further children are being added.

IXMLElementCollection Methods

IXMLElementCollection::get_length
HRESULT IXMLElementCollection::get_length(
    long *plLength)

Obtains the number of elements in the collection.

plLength
Indirect pointer in which to return the size of the collection.
IXMLElementCollection::put_length
HRESULT IXMLElementCollection::put_length(
    long length)

This method has no effect.

IXMLElementCollection::get_newEnum
HRESULT IXMLElementCollection::get__newEnum(
    IUnknown ** ppUnk)

Returns a pointer to an enumerator IEnumVARIANT that is obtained by calling QueryInterface on the returned object.

ppUnk
Indirect pointer in which to return the enumerator.

IEnumvariant is documented in the Microsoft Development Network (MSDN). It allows the enumeration of variants. Each variant that is returned is of type VT_DISPATCH. This returned object is merely an element object, and the IXMLElement interface on the returned object can be obtained by calling QueryInterface on it. It is up to the caller to call Release on the returned enumeration. It is also the responsibility of the caller to call VariantClear on every returned element.

IXMLElementCollection::item
HRESULT IXMLElementCollection::item(
    VARIANT Var1,
    VARIANT Var2,
    IDispatch **ppDisp)

Var1
Index or a name. The preferred variant type of index is VT_I4, and the preferred variant type of a name is VT_BSTR.
Var2
Empty (type VT_EMPTY) or an index.
ppDisp
Indirect pointer in which to return an address of the collection or element that is to be returned.

This method is a fairly involved method and can be used in different ways to retrieve members of a collection or another collection. If Var1 specifies an index and Var2 is empty, the item at that index is returned if such an element does exist. Var1 can also specify a name, in which case the returned object is a collection of all elements in this collection that have the specified name. However, if there is only one element with that name, the returned object is an XML element. It is also possible to specify Var1 as a name and Var2 as an index. In that case, the returned object is an XML element that is the Var2th object of the name specified by Var1. The caller must call Release on any returned objects.

Samples

This section contains some simple implementations of the XML Object Model. The follow examples include:

Sample 1: Parsing an XML file


#define UNICODE

#include <windows.h>
#include <windowsx.h>
#include <stdlib.h>
#include <stdio.h>
#include <io.h>
#include <urlmon.h>
#include <hlink.h>
#include <dispex.h>
#include "mshtml.h"
#include "msxml.h"

#define ASSERT(x)  if(!(x)) DebugBreak()
#define CHECK_ERROR(cond, err) if (!(cond)) {pszErr=(err); goto done;}
#define SAFERELEASE(p) if (p) {(p)->Release(); p = NULL;} else ;


int _cdecl main (int argc, char **argv)
{
    PSTR pszErr = NULL;
    IXMLDocument           *pDoc = NULL;
    IStream                *pStm = NULL;
    IPersistStreamInit     *pPSI = NULL;
    CHAR                  buf[MAX_PATH];
    CHAR                  *pszURL;

    HRESULT hr;

    //
    // Check usage.
    //
    if (argc != 2)
    {
        fprintf (stderr, "Usage:   %s URL\n", argv[0]);
        fprintf (stderr, "Eg %s c:\\nt\\private\\inet\\xml\\test\\channel.cdf\n", argv[0]);
        fprintf (stderr, "or %s http://ohserv/users/julianj/msnbc.cdf\n", argv[0]);
        exit (1);
    }

    //
    // HACK if passed in a file name expand it if it doesn't look like an URL
    //
    if (CompareStringA(LOCALE_USER_DEFAULT, NORM_IGNORECASE, argv[1], 7, "http://", 7) == CSTR_EQUAL)
    {
        pszURL = argv[1];
    }
    else
    {
        pszURL = buf;
        GetFullPathNameA(argv[1], MAX_PATH, pszURL, NULL);
    }

    hr = CoInitialize(NULL);
    ASSERT(SUCCEEDED(hr));

    //
    // Create an empty XML document
    //
    hr = CoCreateInstance(CLSID_XMLDocument, NULL, CLSCTX_INPROC_SERVER,
                                IID_IXMLDocument, (void**)&pDoc);

    CHECK_ERROR (pDoc, "CoCreateInstance Failed");

    //
    // Synchronously create a stream on an URL
    //
    hr = URLOpenBlockingStreamA(0, pszURL, &pStm, 0,0);    
    CHECK_ERROR(SUCCEEDED(hr) && pStm, "Couldn't open stream on URL")
    //
    // Get the IPersistStreamInit interface to the XML doc
    //
    hr = pDoc->QueryInterface(IID_IPersistStreamInit, (void **)&pPSI);
    CHECK_ERROR(SUCCEEDED(hr), "QI for IPersistStreamInit failed");

    //
    // Init the XML doc from the stream
    //
    hr = pPSI->Load(pStm);
    //CHECK_ERROR(SUCCEEDED(hr), "Couldn't load XML doc from stream");

    if(SUCCEEDED(hr))
    {
        printf("%s : XML File is well formed \r\n",argv[0]);

    }
    else
    {
        // Print Error Information !
        IXMLError *pXMLError = NULL ;
        XML_ERROR xmle;

        hr = pPSI->QueryInterface(IID_IXMLError, (void **)&pXMLError);
        CHECK_ERROR(SUCCEEDED(hr), "Couldn't get IXMLError");

        ASSERT(pXMLError);

        hr = pXMLError->GetErrorInfo(&xmle);
        SAFERELEASE(pXMLError);
        CHECK_ERROR(SUCCEEDED(hr), "GetErrorInfo Failed");

        printf("%s :", argv[0]);
        wprintf(TEXT(" Error on line %d. Found %s while expecting %s\r\n"),
                            xmle._nLine,
                            xmle._pszFound,
                            xmle._pszExpected); 
        
        SysFreeString(xmle._pszFound);
        SysFreeString(xmle._pszExpected);
        SysFreeString(xmle._pchBuf);
    }

    

                         
done: // Clean up.
    //
    // Release any used interfaces
    //
    SAFERELEASE(pPSI);
    SAFERELEASE(pStm);
    SAFERELEASE(pDoc);

    if (pszErr)
        fprintf (stderr, "%s, last error %d\n", pszErr, GetLastError());
    return 0;
}


Sample 2: Parsing a CDF file for specific tags


//*******************************
// XML OM test code
//
// *******************************

#include <windows.h>
#include <windowsx.h>
#include <stdlib.h>
#include <stdio.h>
#include <io.h>
#include <urlmon.h>
#include <hlink.h>
#include <dispex.h>
#include "mshtml.h"
#include "msxml.h"

#define ASSERT(x)  if(!(x)) DebugBreak()
#define CHECK_ERROR(cond, err) if (!(cond)) {pszErr=(err); goto done;}
#define SAFERELEASE(p) if (p) {(p)->Release(); p = NULL;} else ;


#define WALK_ELEMENT_COLLECTION(pCollection, pDispItem) \
    {\
        long length;\
        \
        if (SUCCEEDED(pChildren->get_length(&length)) && length > 0)\
        {\
            VARIANT vIndex, vEmpty;\
            vIndex.vt = VT_I4;\
            vEmpty.vt = VT_EMPTY;\
                                 \
            for (long i=0; i<length; i++)\
            {\
                vIndex.lVal = i;\
                IDispatch *pDispItem = NULL;\
                if (SUCCEEDED(pCollection->item(vIndex, vEmpty, &pDispItem)))\
                {

#define END_WALK_ELEMENT_COLLECTION(pDispItem) \
                    pDispItem->Release();\
                }\
            }\
        }\
    }





void Indent(int indent)
{
    while (indent-->0)
    {
        printf("   ");
    }
}

void Output(int indent, const char *format, ...)
{
    va_list argptr;

    va_start(argptr, format);

    Indent(indent);

     (void)vprintf(format, argptr);
}

void OutputLn(int indent, const char *format, ...)
{
    va_list argptr;

    va_start(argptr, format);

    printf("\n");

    Indent(indent);

    (void)vprintf(format, argptr);
}

//
// Dump an element attribute member if present
//
void DumpAttrib(IXMLElement *pElem, BSTR bstrAttribName)
{
    VARIANT vProp;
    
    VariantInit(&vProp);

    if (SUCCEEDED(pElem->getAttribute(bstrAttribName, &vProp)))
    {
        if (vProp.vt == VT_BSTR)
        {
            printf(" %S=\"%S\"", bstrAttribName, vProp.bstrVal);
        }
        VariantClear(&vProp);
    }
}

//
// Helper routine to quickly find out if this is a named element
// currently only used to improve the format of the output
//
BOOL IsNamedElement(IXMLElement *pElem)
{
    BSTR bstrName;

    if (SUCCEEDED(pElem->get_tagName(&bstrName)))
    {
        if (bstrName)
        {
            SysFreeString(bstrName);
            return TRUE;
        }
    }
    return FALSE;
}

//
// returns TRUE if this element had children
//
void DumpElement
(
    IXMLElement *pElem, // the XML element to dump
    int indent          // indentation level
)
{
    BSTR bstrTagName = NULL;
    BSTR bstrContent = NULL;
    IXMLElementCollection * pChildren;
    //
    // Note this nonsense with children and grand children is just so we get
    // nicely formatted output!
    //
    BOOL fHadNamedChild = FALSE;

    //
    // Dump the name of the NODE
    //
    pElem->get_tagName(&bstrTagName);
    if (bstrTagName) 
    {       
        //
        // Special case the output of the special tags
        //
        if (wcscmp(bstrTagName, L"XML") == 0)
        {
            Output(indent, "<\?XML version=\"1.0\"\?>\n");
            SysFreeString(bstrTagName);
            return;
        }
        else if (wcscmp(bstrTagName, L"DOCTYPE") == 0)
        {
            Output(indent,
              "<!DOCTYPE CHANNEL SYSTEM \"http://www.w3c.org/Channel.dtd\">\n");
            SysFreeString(bstrTagName);
            return;
        }
        else
        {
            OutputLn(indent, "<%S", bstrTagName);
        }
    }

    //
    // Dump the attribs if present
    //
    DumpAttrib(pElem, L"VALUE");
    DumpAttrib(pElem, L"HREF");
    DumpAttrib(pElem, L"HOUR");
    DumpAttrib(pElem, L"DAY");
    DumpAttrib(pElem, L"IsClonable");
    DumpAttrib(pElem, L"Type");

    //
    // Dump the text contents if present
    //
    XMLELEM_TYPE xmlElemType;
    if (SUCCEEDED(pElem->get_type((long *)&xmlElemType)))
    {
        if (xmlElemType == XMLELEMTYPE_TEXT)
        {
            if (SUCCEEDED(pElem->get_text(&bstrContent)))
            {
                if (bstrContent)
                {
                    printf("%S", bstrContent);
                    SysFreeString(bstrContent);
                }
            }
        }
    }

    //
    // Find the children if they exist
    //
    if (SUCCEEDED(pElem->get_children(&pChildren)) && pChildren)
    {
        //
        // Close the tag if present but with "children present" closure 
        // that is use > instead of />
        //
        if (bstrTagName) printf(">");

        WALK_ELEMENT_COLLECTION(pChildren, pDisp)
        {
            //
            // pDisp will iterate over an IDispatch for each item in the 
            // collection.
            //
            IXMLElement * pChild;
            if (SUCCEEDED(pDisp->QueryInterface(
                                     IID_IXMLElement, (void **)&pChild)))
            {
                if (IsNamedElement(pChild))
                    fHadNamedChild = TRUE;

                //
                // Recursively dump children
                //
                DumpElement(pChild, indent+1);

                pChild->Release();
            }
        }
        END_WALK_ELEMENT_COLLECTION(pDisp);
        pChildren->Release();

        //
        // display closing tag
        //
        if (bstrTagName)
        {
            if (fHadNamedChild)
            {
                OutputLn(indent, "</%S>", bstrTagName);
            }
            else
            {
                printf("</%S>", bstrTagName);
            }
        }
    }
    else
    {
        //
        // No children so terminate tag on same line
        //
        if (bstrTagName) printf("/>");
    }

    if (bstrTagName)
        SysFreeString(bstrTagName);
}


int MyStrToOleStrN(LPOLESTR pwsz, int cchWideChar, LPCTSTR psz)
{
    int i;
    i=MultiByteToWideChar(CP_ACP, 0, psz, -1, pwsz, cchWideChar);
    if (!i)
    {
        //DBG_WARN("MyStrToOleStrN string too long; truncated");
        pwsz[cchWideChar-1]=0;
    }
    else
        ZeroMemory(pwsz+i, sizeof(OLECHAR)*(cchWideChar-i));

    return i;
}

int _cdecl main (int argc, char **argv)
{
    PSTR pszErr = NULL;
    IXMLDocument           *pDoc = NULL;
    IStream                *pStm = NULL;
    IPersistStreamInit     *pPSI = NULL;
    IXMLElement            *pElem = NULL;
    TCHAR                  buf[MAX_PATH];
    TCHAR                  *pszURL;
    WCHAR                  *pwszURL=NULL;
    BSTR                   pBURL=NULL;

    HRESULT hr;

    //
    // Check usage.
    //
    if (argc != 2)
    {
        fprintf (stderr, "Usage:   omtest2 URL\n");
        fprintf (stderr, "Eg omtest2 c:\\nt\\private\\inet\\xml\\test\\channel.uni\n");
        fprintf (stderr, "or omtest2 http://ohserv/users/julianj/channel.xml\n");
        exit (1);
    }

    //
    // HACK if passed in a file name expand it if it doesn't look like an URL
    //
    if (CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, argv[1], 7, "http://", 7) == CSTR_EQUAL)
    {
        pszURL = argv[1];
    }
    else
    {
        pszURL = buf;
        GetFullPathName(argv[1], MAX_PATH, pszURL, NULL);
    }

    hr = CoInitialize(NULL);
    ASSERT(SUCCEEDED(hr));

    //
    // Create an empty XML document
    //
    hr = CoCreateInstance(CLSID_XMLDocument, NULL, CLSCTX_INPROC_SERVER,
                                IID_IXMLDocument, (void**)&pDoc);

    CHECK_ERROR (pDoc, "CoCreateInstance Failed");

    //
    // Synchronously create a stream on an URL
    //
    //hr = URLOpenBlockingStream(0, pszURL, &pStm, 0,0);    
    //CHECK_ERROR(SUCCEEDED(hr) && pStm, "Couldn't open stream on URL")
    
    //
    // Get the IPersistStreamInit interface to the XML doc
    //
    //hr = pDoc->QueryInterface(IID_IPersistStreamInit, (void **)&pPSI);
    //CHECK_ERROR(SUCCEEDED(hr), "QI for IPersistStreamInit failed");

    //
    // Init the XML doc from the stream
    //
    //hr = pPSI->Load(pStm);
    
    pwszURL = (WCHAR *)LocalAlloc(LMEM_FIXED, ((sizeof(WCHAR))*(strlen(pszURL) + 2)));
    CHECK_ERROR(pwszURL, "Mem Alloc Failure");

    hr = MyStrToOleStrN(pwszURL, (strlen(pszURL) + 1), pszURL);
    CHECK_ERROR(SUCCEEDED(hr), "Failed to convert to UNICODE");
    pBURL = SysAllocString(pwszURL);
    CHECK_ERROR(pBURL, "Mem Alloc Failure");
    LocalFree(pwszURL);

    hr = pDoc->put_URL(pBURL);
    
    if (SUCCEEDED(hr))
    {
        //
        // Now walk the OM and look at interesting things:
        //
        hr = pDoc->get_root(&pElem);
        CHECK_ERROR(SUCCEEDED(hr), "Failed to get_root of XML object");

        //
        // Dump the top level meta nodes of the document
        //
        BSTR bstrVal;
        hr = pDoc->get_version(&bstrVal);
        if(FAILED(hr) || !bstrVal)
        {
            bstrVal = SysAllocString(L"1.0");
        }
        // 
        Output(0, "<\?XML version=\"%S\" ",bstrVal);
        SysFreeString(bstrVal);
        bstrVal = NULL;
        
        hr = pDoc->get_encoding(&bstrVal);
        if(SUCCEEDED(hr) && bstrVal)
        {
            Output(0, "encoding=\"%S\" ",bstrVal);
            SysFreeString(bstrVal);
        }
        Output(0, "\?>\n");

        //
        // Look for doctype information
        //

        // Disabled till DavidSch makes a fix
        /*
        bstrVal = NULL;
        hr = pDoc->get_doctype(&bstrVal);
        if(SUCCEEDED(hr) && bstrVal)
        {   
            Output(0, "<!DOCTYPE %S>",bstrVal); 
            SysFreeString(bstrVal);

        }
        */
        //
        // Dump the root element and all children of the XML object
        //
        
        DumpElement(pElem, 0); // -1 to counteract nameless outer container
    }
    else
    {
        //
        // Failed to parse stream, output error information
        //
        IXMLError *pXMLError = NULL ;
        XML_ERROR xmle;
    
        hr = pDoc->QueryInterface(IID_IXMLError, (void **)&pXMLError);
        CHECK_ERROR(SUCCEEDED(hr), "Couldn't get IXMLError");
    
        ASSERT(pXMLError);
    
        hr = pXMLError->GetErrorInfo(&xmle);
        SAFERELEASE(pXMLError);
        CHECK_ERROR(SUCCEEDED(hr), "GetErrorInfo Failed");
    
        printf("%s: Error on line %d. Found %S while expecting %S\r\n",
                argv[0],
                xmle._nLine,
                xmle._pszFound,
                xmle._pszExpected);
    
        SysFreeString(xmle._pszFound);
        SysFreeString(xmle._pszExpected);
        SysFreeString(xmle._pchBuf);
    }
                         
done: // Clean up.
    //
    // Release any used interfaces
    //
    SAFERELEASE(pPSI);
    SAFERELEASE(pStm);
    SAFERELEASE(pElem);
    SAFERELEASE(pDoc);
    SysFreeString(pBURL);
    if (pszErr)
        fprintf (stderr, "%s, last error %d\n", pszErr, GetLastError());
    return 0;
}

Sample 3: Using IPersistMoniker::Load to load a document


//****************************************
// MSXML OM test code
//
// adapted for multithreaded urlmon testing
//****************************************

#include <windows.h>
#include <windowsx.h>
#include <stdlib.h>
#include <stdio.h>
#include <io.h>
#include <urlmon.h>
#include <hlink.h>
#include <dispex.h>
#include "mshtml.h"
#include "msxml.h"

int MyStrToOleStrN(LPOLESTR pwsz, int cchWideChar, LPCTSTR psz);
#define ASSERT(x)  if(!(x)) DebugBreak()
#define CHECK_ERROR(cond, err) if (!(cond)) {pszErr=(err); goto done;}
#define SAFERELEASE(p) if (p) {(p)->Release(); p = NULL;} else ;
//
// SAFECAST(obj, type)
//
// This macro is extremely useful for enforcing strong typechecking on other
// macros.  It generates no code.
//
// Simply insert this macro at the beginning of an expression list for
// each parameter that must be typechecked.  For example, for the
// definition of MYMAX(x, y), where x and y absolutely must be integers,
// use:
//
//   #define MYMAX(x, y)    (SAFECAST(x, int), SAFECAST(y, int), ((x) > (y) ? (x) : (y)))
//
//
#define SAFECAST(_obj, _type) (((_type)(_obj)==(_obj)?0:0), (_type)(_obj))

#define WALK_ELEMENT_COLLECTION(pCollection, pDispItem) \
    {\
        long length;\
        \
        if (SUCCEEDED(pCollection->get_length(&length)) && length > 0)\
        {\
            VARIANT vIndex, vEmpty;\
            vIndex.vt = VT_I4;\
            vEmpty.vt = VT_EMPTY;\
                                 \
            for (long i=0; i<length; i++)\
            {\
                vIndex.lVal = i;\
                IDispatch *pDispItem = NULL;\
                if (SUCCEEDED(pCollection->item(vIndex, vEmpty, &pDispItem)))\
                {

#define END_WALK_ELEMENT_COLLECTION(pDispItem) \
                    pDispItem->Release();\
                }\
            }\
        }\
    }


//=============================================================================


 class CTestBindStatusCallback : public IBindStatusCallback
                              , public IAuthenticate
                              , public IHttpSecurity

    {
    public:
        // *** IUnknown methods ***
        STDMETHOD(QueryInterface)(REFIID riid, LPVOID * ppvObj);
        STDMETHOD_(ULONG, AddRef)(void) ;
        STDMETHOD_(ULONG, Release)(void);

        // *** IAuthenticate ***
        STDMETHOD(Authenticate)(
            HWND *phwnd,
            LPWSTR *pszUsername,
            LPWSTR *pszPassword);

        // *** IBindStatusCallback ***
        STDMETHOD(OnStartBinding)(
            /* [in] */ DWORD grfBSCOption,
            /* [in] */ IBinding *pib);

        STDMETHOD(GetPriority)(
            /* [out] */ LONG *pnPriority);

        STDMETHOD(OnLowResource)(
            /* [in] */ DWORD reserved);

        STDMETHOD(OnProgress)(
            /* [in] */ ULONG ulProgress,
            /* [in] */ ULONG ulProgressMax,
            /* [in] */ ULONG ulStatusCode,
            /* [in] */ LPCWSTR szStatusText);

        STDMETHOD(OnStopBinding)(
            /* [in] */ HRESULT hresult,
            /* [in] */ LPCWSTR szError);

        STDMETHOD(GetBindInfo)(
            /* [out] */ DWORD *grfBINDF,
            /* [unique][out][in] */ BINDINFO *pbindinfo);

        STDMETHOD(OnDataAvailable)(
            /* [in] */ DWORD grfBSCF,
            /* [in] */ DWORD dwSize,
            /* [in] */ FORMATETC *pformatetc,
            /* [in] */ STGMEDIUM *pstgmed);

        STDMETHOD(OnObjectAvailable)(
            /* [in] */ REFIID riid,
            /* [iid_is][in] */ IUnknown *punk);



        /* *** IHttpSecurity ***  */
        STDMETHOD(GetWindow)(REFGUID rguidReason, HWND* phwnd);

        STDMETHOD(OnSecurityProblem)(DWORD dwProblem);

        // Worker routines

        
        IBinding*       m_pib;
        IMoniker*        m_pmk;
        IBindCtx*       m_pbc;
        HANDLE          m_hEvent;

        ULONG m_cRef;

        
        VOID Abort(); // Aborts the binding 
        ~CTestBindStatusCallback();
        CTestBindStatusCallback(IBindCtx *pbc, IMoniker *pmkIn, HANDLE hEvent);
    };




void Indent(int indent, FILE *fileID)
{
    while (indent-->0)
    {
        fprintf(fileID, "    ");
    }
}

//
// Dump an element attribute member if present
//
void DumpAttrib(IXMLElement *pElem, BSTR bstrAttribName, FILE *fileID)
{
    VARIANT vProp;
    
    VariantInit(&vProp);

    if (SUCCEEDED(pElem->getAttribute(bstrAttribName, &vProp)))
    {
        if (vProp.vt == VT_BSTR)
        {
            fprintf(fileID, "%S=\"%S\" ", bstrAttribName, vProp.bstrVal);
        }
        VariantClear(&vProp);
    }
}

HRESULT DumpElement(IXMLElement *pElem, int indent, FILE *fileID)
{
    BSTR bstr = NULL;
    IXMLElementCollection * pChildren;

    Indent(indent, fileID);

    //
    // Dump the name of the NODE
    //
    pElem->get_tagName(&bstr);
    fprintf(fileID, "<%S ", bstr?bstr:L"XML");

    //
    // Dump the attribs if present
    //
    DumpAttrib(pElem, L"VALUE", fileID);
    DumpAttrib(pElem, L"HREF", fileID);
    DumpAttrib(pElem, L"HOUR", fileID);
    DumpAttrib(pElem, L"DAY", fileID);
    DumpAttrib(pElem, L"IsClonable", fileID);
    DumpAttrib(pElem, L"Type", fileID);

    //
    // Find the children if they exist
    //
    if (SUCCEEDED(pElem->get_children(&pChildren)) && pChildren)
    {
        fprintf(fileID, ">\n");

        WALK_ELEMENT_COLLECTION(pChildren, pDisp)
        {
            //
            // pDisp will iterate over an IDispatch for each item in the collection
            //
            IXMLElement * pChild;
            if (SUCCEEDED(pDisp->QueryInterface(IID_IXMLElement, (void **)&pChild)))
            {
                DumpElement(pChild, indent+1, fileID);
                pChild->Release();
            }
        }
        END_WALK_ELEMENT_COLLECTION(pDisp);
        pChildren->Release();
        //
        // Having output children display closing tag
        //
        Indent(indent, fileID);
        fprintf(fileID, "</%S>\n", bstr?bstr:L"XML"); 
    }
    else
    {
        //
        // No children so terminate tag on same line
        //
        fprintf(fileID, "/>\n");
    }

    if (bstr)
        SysFreeString(bstr);

    return S_OK;
}

HANDLE g_hMutex = NULL;
ULONG g_ulNextThreadId = 0;

DWORD WINAPI LoadFunc(CHAR *pszUrl)
{


    FILE *fileID = stdout;
    HRESULT hr;
    IXMLElement *pElem = NULL;
    IUnknown *pUnk = NULL;
    IXMLDocument *pDoc = NULL;
    IMoniker *pmk = NULL;
    HANDLE hEvent = NULL;
    IBindStatusCallback *pPreviousCallback = NULL;
    IPersistMoniker *pPersistmk = NULL;
    PSTR pszErr = NULL;
    IBindCtx *pbc = NULL;
    IBindStatusCallback *pbsc = NULL;
    DWORD dwRet;
    LONG len;
    WCHAR *pwszUrl;

    ASSERT(SUCCEEDED(hr));
    len = lstrlenA(pszUrl);
    
    pwszUrl = (WCHAR *)LocalAlloc(LMEM_FIXED, (len + 1)*sizeof(WCHAR));
    ASSERT(pwszUrl);
    MyStrToOleStrN(pwszUrl, len + 1, pszUrl);



    //
    // Dump the root element and all childred of the XML object
    //

    ASSERT(SUCCEEDED(hr));
    if (!pDoc)
    {
        hr = CoCreateInstance(CLSID_XMLDocument, NULL, CLSCTX_INPROC_SERVER,
                                IID_IUnknown, (void**)&pUnk);
    }

    CHECK_ERROR (pUnk, "CoCreateInstance");

    hr = pUnk->QueryInterface(IID_IXMLDocument, (void **)&pDoc);

    ASSERT(SUCCEEDED(hr));
    pUnk->Release();
    pUnk = NULL;
    CHECK_ERROR (pDoc, "QI for IXMLDocument");

    hr = pDoc->QueryInterface(IID_IPersistMoniker, (void **)&pPersistmk);
    ASSERT(SUCCEEDED(hr));

    CHECK_ERROR(pPersistmk, "QI for IPersistMoniker");


    hr = CreateURLMoniker(NULL, pwszUrl, &pmk);
    ASSERT(SUCCEEDED(hr));
    CHECK_ERROR(pmk, "CreateURLMoniker");

    hr = CreateBindCtx(0, &pbc);
    if((!pbc) || FAILED(hr))
        goto done;

    pbc->AddRef();

    hEvent = CreateEventA(0, 0, 0, 0);
    pbsc = new CTestBindStatusCallback(pbc, pmk, hEvent);
    ASSERT(pbsc);
    if(!pbsc)
    {
        fprintf (stderr, "Out of memory ");
        goto done;
    }
    pbsc->AddRef();

    hr = RegisterBindStatusCallback(pbc, pbsc, &pPreviousCallback, 0);
    if(FAILED(hr))
    {
        fprintf (stderr, "RegisterBindStatusCallback : Failed");
        goto done;
    }
    
    
    hr = pPersistmk->Load(FALSE, pmk, pbc, 0);


    ASSERT(SUCCEEDED(hr));

    if(pPersistmk)
    {
        pPersistmk->Release();
        pPersistmk = NULL;
    }

    while(1)
    {
        MSG msg;
        dwRet =  MsgWaitForMultipleObjects(1, &hEvent, FALSE, INFINITE, QS_ALLINPUT);
        if(dwRet == WAIT_OBJECT_0)
        {
            // We're done
            break;
         }
         else
         {
            GetMessage(&msg, NULL, 0, 0);
            DispatchMessage(&msg);
         }
    }
    // Ready to start dumping the document

    //
    // Now walk the OM and look at interesting things:
    //
    hr = pDoc->get_root(&pElem);
    CHECK_ERROR(pElem, "Thread Failed to get_root of XML object");

    DumpElement(pElem, 0, fileID);
    
done: // Clean up.
    fclose(fileID);
    pElem->Release();
    LocalFree(pwszUrl);
    pbsc->Release();
    return 0;    
}

int MyStrToOleStrN(LPOLESTR pwsz, int cchWideChar, LPCTSTR psz)
{
    int i;
    i=MultiByteToWideChar(CP_ACP, 0, psz, -1, pwsz, cchWideChar);
    if (!i)
    {
        //DBG_WARN("MyStrToOleStrN string too long; truncated");
        pwsz[cchWideChar-1]=0;
    }
    else
        ZeroMemory(pwsz+i, sizeof(OLECHAR)*(cchWideChar-i));

    return i;
}

#define NUM_THREADS 1
const WCHAR *szUrls[NUM_THREADS] = {  L"http://ie_tools/bharats/msnbc0.cdf",
                                      //L"http://ie_tools/bharats/msnbc1.cdf",
                                     // L"http://ie_tools/bharats/msnbc2.cdf",
                                      //L"http://ie_tools/bharats/msnbc3.cdf",
                                      //L"http://ie_tools/bharats/msnbc4.cdf"
                                    };

int _cdecl main (int argc, char **argv)
{
    PSTR pszErr = NULL;
    IStream                *pStm = NULL;
    IPersistStreamInit     *pPSI = NULL;
    IXMLElement            *pElem = NULL;
    char                  buf[MAX_PATH];
    char                  *pszURL;
    UINT uNumThreads = NUM_THREADS; // Atleast three threads
    UINT ui;// iterator
    DWORD dwThreadId;
    HANDLE hThd;
    WCHAR *pwszStrUrlCopy = NULL;
    LONG len;

    HRESULT hr;

    //
    // Check usage.
    //
    if (argc < 2)
    {
        fprintf (stderr, "Usage:   %s URL\n",argv[0]);
        fprintf (stderr, "Eg %s c:\\nt\\private\\inet\\xml\\test\\channel.uni\n", argv[0]);
        fprintf (stderr, "or %s http://ohserv/users/julianj/channel.xml\n", argv[0]);
        exit (1);
    }
    
   
    // HACK if passed in a file name expand it if it doesn't look like an URL
    //
    
    if (CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, argv[1], 7, "http://", 7) == CSTR_EQUAL)
    {
        pszURL = argv[1];
    }
    else
    {
        pszURL = buf;
        GetFullPathName(argv[1], MAX_PATH, pszURL, NULL);
    }


    hr = CoInitialize(NULL);
    ASSERT(SUCCEEDED(hr));

    //
    // Create an empty XML document
    //
    LoadFunc(pszURL);

    len = lstrlen(pszURL) + 1;
    

    //
    // Dump the root element and all childred of the XML object onto screen


                         
done: // Clean up.
    //
    // Release any used interfaces
    //
    SAFERELEASE(pPSI);
    SAFERELEASE(pStm);
    SAFERELEASE(pElem);

    if (pszErr)
        fprintf (stderr, "%s, last error %d\n", pszErr, GetLastError());

    return 0;
}


STDMETHODIMP CTestBindStatusCallback::QueryInterface(
                                REFIID riid, 
                                LPVOID * ppvObj)
{
    if (IsEqualIID(riid, IID_IBindStatusCallback) || 
        IsEqualIID(riid, IID_IUnknown))
    {
        *ppvObj = SAFECAST(this, IBindStatusCallback*);
    }
    else if (IsEqualIID(riid, IID_IAuthenticate))
    {
        *ppvObj = SAFECAST(this, IAuthenticate*);
    }
    else if (IsEqualIID(riid, IID_IHttpSecurity))
    {
        *ppvObj = SAFECAST(this, IHttpSecurity*);
    }
    else
    {
        *ppvObj = NULL;
        return E_NOINTERFACE;
    }

    ((LPUNKNOWN)*ppvObj)->AddRef();
    return NOERROR;
}

ULONG CTestBindStatusCallback::AddRef(void)
{
    return ++m_cRef;
}

ULONG CTestBindStatusCallback::Release(void)
{
    if( 0L != --m_cRef )
        return m_cRef;

    delete this;
    return 0L;
}

VOID CTestBindStatusCallback::Abort()
{
    if(m_pib)
    {
        m_pib->Abort();
    }
}

CTestBindStatusCallback::CTestBindStatusCallback(IBindCtx *pbc, IMoniker *pmkIn, HANDLE hEvent)
{

    
    // Initialize object
    m_cRef=0L;

    m_pib = NULL;
    m_pbc = pbc;
    if(m_pbc)
        m_pbc->AddRef();
 

    m_pmk = pmkIn;
    if(m_pmk)
        m_pmk->AddRef();

    m_hEvent = hEvent;




}

CTestBindStatusCallback::~CTestBindStatusCallback()
{    
    SAFERELEASE(m_pib);
    SAFERELEASE(m_pbc);
 
    SAFERELEASE(m_pmk);
    if(m_hEvent)
        CloseHandle(m_hEvent);


}

 // IAuthenticate Methods
STDMETHODIMP CTestBindStatusCallback::Authenticate(
    HWND *phwnd,
    LPWSTR *pszUsername,
    LPWSTR *pszPassword)
{
    return E_NOTIMPL;
}



// IBindStatusCallback Methods
STDMETHODIMP CTestBindStatusCallback::OnStartBinding(DWORD grfBSCOption, 
IBinding *pibIn)
{

    return S_OK;

}

STDMETHODIMP CTestBindStatusCallback::GetPriority(LONG *pnPriority)
{
    return S_OK;
}

STDMETHODIMP CTestBindStatusCallback::OnLowResource(DWORD reserved)
{
    return S_OK; //BUGBUG
}

STDMETHODIMP CTestBindStatusCallback::OnProgress(
    ULONG ulProgress,
    ULONG ulProgressMax,
    ULONG ulStatusCode,
    LPCWSTR szStatusText)
{
    return S_OK;
}

STDMETHODIMP CTestBindStatusCallback::OnStopBinding(
    HRESULT hresult,
    LPCWSTR szError)
{
    HRESULT hr = S_OK;
    if(SUCCEEDED(hresult))
    {
        OutputDebugString(TEXT("Done Loading succesfully"));    
    }
    // Signal Event to wake up thread to navigate children
    SetEvent(m_hEvent);

    SAFERELEASE(m_pib);
    return hr;
}

STDMETHODIMP CTestBindStatusCallback::GetBindInfo(
    DWORD *grfBINDF, /* [out] */
    BINDINFO *pbindinfo /* [unique][out][in] */ )
{
    return E_NOTIMPL;

}

STDMETHODIMP CTestBindStatusCallback::OnDataAvailable(
    /* [in] */ DWORD grfBSCF,
    /* [in] */ DWORD dwSize,
    /* [in] */ FORMATETC *pformatetc,
    /* [in] */ STGMEDIUM *pstgmed)
{
    return S_OK;
}


STDMETHODIMP CTestBindStatusCallback::OnObjectAvailable(
    /* [in] */ REFIID riid,
    /* [iid_is][in] */ IUnknown *punk)
{
    return S_OK;
}



        /* *** IHttpSecurity ***  */
        // BUGBUG - Do we need to implement this interface
STDMETHODIMP CTestBindStatusCallback::GetWindow(REFGUID rguidReason, HWND* 
phwnd)
{
    if (!phwnd)
        return E_POINTER;

    *phwnd = NULL; // BUGBUG
    return S_OK;
    
}

STDMETHODIMP CTestBindStatusCallback::OnSecurityProblem(DWORD dwProblem)
{
    // force UI - return S_FALSE for all problems
    return S_FALSE;
}


Sample 4: Parsing an XML file using JScript

<HTML>
<HEAD>
<TITLE>JScript XML Sample</TITLE>
</HEAD>
<BR>
<H1><B><Center>JScript XML Sample</B></H1>             
<P>This page demonstrates using XML in JScript. Enter a valid XML or CDF file in the 
text box below and it will be parsed using MSXML and JScript. CDF is based on XML.
Information about each element in the file will be displayed in another window. For more
information on CDF please see the <A HREF="http://www.microsoft.com/ie/authors/">Internet
Explorer 4.0 authors and developers </A> site.

<P>Enter a XML or CDF file name:<BR>
<INPUT TYPE=TEXT NAME="cdfURL" VALUE="" size=50><BR>

<OBJECT
  classid="clsid:CFC399AF-D876-11D0-9C10-00C04FC99C8E"
  id="MSXML"
  Name="xmlDoc">
</OBJECT>

<BR><BR>

<INPUT TYPE = "BUTTON"
       NAME = "ParseCDF"     
       VALUE = "Parse The XML/CDF File"
       onClick = "WalkXMLMain(cdfURL.value)">

</CENTER>

<SCRIPT>
var win;                                // status goes here
var doc;                                // document to write on
var knownAttrs = new Object;            // element attribute variable

//
// Display the attribute 
//
function dumpAttrs(elem)
{
    var i, val, result = "";
  
    for (i in knownAttrs)
	{
	val = elem.getAttribute(knownAttrs[i]);
		if ((val != "") && (val != null))
			result = result + knownAttrs[i] + "=" + val + " ";
	}

    return result;
}

//
// Displays the element information for each element in the file. 
//
function dumpElement(elem)
{
	var children, cChildren, child
	var i, length;

	if (elem == null)
	{
		alert("elem == null");
		window = document;              //      Force a runtime error to bail
	}
	
	children = elem.children;
	if (children != null)
	  length = children.length;
	else
	  length = 0;
	
	// Display the element information in the table
        doc.write("<TR><TD>" + GetTagName(elem) + "</TD>");
	doc.write("<TD>" + GetType(elem.type) + "</TD>");
	if (elem.parent != null) // skip text for root node
	  doc.write("<TD>" + elem.text + "</TD>");
	else
	  doc.write("<TD>" + " " + "</TD>");
	doc.write("<TD>" + dumpAttrs(elem) + "</TD>");
	doc.write("<TD>" + GetParentTag(elem) + "</TD>");        
	doc.write("<TD>" + length + "</TD></TR>");

	// If the element has children, call dumpElement again
        if (children != null)
	{

		cChildren = children.length;

		for (i = 0; i < cChildren; i++)
		{
			child = children.item(i);
			dumpElement(child);
		}
	}
}

//
// Initialize the knownAttrs variable
//
function loadKnownAttrs()
{
	// Incomplete list, just for example.
	knownAttrs[0] = "HREF";
	knownAttrs[1] = "STYLE";
	knownAttrs[2] = "HOUR";
	knownAttrs[3] = "DAY";
	knownAttrs[4] = "MIN";
	knownAttrs[5] = "IsClonable";
	knownAttrs[6] = "Type";
	knownAttrs[7] = "BASE";
	knownAttrs[8] = "SELF";
}

//
// Writes information to the browser status bar
//
function setStatus(s)
{
	win.status = s;
}

//
// Main function, calls dumpElement
//
function WalkXMLMain(cdfName)
{
	var startTime = new Date();
	var CDFFile, xmlDoc;

	win = window.open("", "XMLOutput");
	doc = win.document;

	doc.writeln("<HTML>");
	doc.writeln("<TITLE>XML Output</TITLE>");
	doc.writeln("<BODY>");
	doc.writeln("<PRE>");

	setStatus("Loading known attributes...");
	loadKnownAttrs();

	setStatus("Creating XML object...");
	xmlDoc = MSXML;

	setStatus("Loading XML document...");
        xmlDoc.URL = cdfName;
	setStatus("File has been assigned to xmlDoc.URL");

	// Show the ducument information
        ShowDocInfo(xmlDoc);

	// Initialize the table for the element information
        MakeNodeHeader(win);
        // Traverse the XML tree showing the element information
	dumpElement(xmlDoc.root);

	doc.writeln("</TABLE>");
	var stopTime = new Date();
	doc.writeln("<FONT SIZE=&quot+1&quot><P>Start: " + startTime + "   Stop: " + stopTime + "</FONT>");
	doc.writeln("</PRE>");
	doc.writeln("</BODY>");
	doc.writeln("</HTML>");
	
	xmlDoc = null;
	setStatus("Done!");
}

//
// Initializes the table for the element information
//
function MakeNodeHeader(win)
{
  doc.writeln("<BR><BR><H2>Element Information</H2></CENTER>");
  doc.write("<FONT SIZE=&quot+1&quot><P>The properties of each node in the XML file is listed in the following table...<BR><BR></FONT>");

  doc.write("<TABLE BORDER CELLSPACING=2 CELLPADDING=2>");
  doc.write("<TR><TD ALIGN=LEFT><B>Tag Name</B></TD>");
  doc.write("<TD ALIGN=LEFT><B>Type</B></TD>");
  doc.write("<TD ALIGN=LEFT><B>Text (if any)</B></TD>");
  doc.write("<TD ALIGN=LEFT><B>Attribute</B></TD>");
  doc.write("<TD ALIGN=LEFT><B>Parent</B></TD>");
  doc.write("<TD ALIGN=LEFT><B># of Children</B></TD></TR>");
}

//
// Returns the element type 
//
function GetType(type)
{

  if (type == 0)
    return "ELEMENT";

  if (type == 1)
    return "TEXT";

  if (type == 2)
    return "COMMENT";

  if (type == 3)
    return "DOCUMENT";

  if (type == 4)
    return "DTD";
  else
    return "OTHER";
}

//
// Returns the tag name
//
function GetTagName(elem)
{
  if (elem.type == 0)
    return elem.tagName;
  else
    return " ";
}

//
// Returns the parent tag name
//
function GetParentTag(elem)
{
  if (elem.parent != null)
    return GetTagName(elem.parent);
  else
    return " ";
}

//
// Displays document information about the XML file
//
function ShowDocInfo(xmlDoc)
{
  doc.writeln("<FONT FACE=&quottimes&quot SIZE=&quot+1&quot><CENTER><H2>Document Information</H2>");
  if (xmlDoc.URL != null)
    doc.writeln("<P>URL is: " + xmlDoc.URL);
  doc.writeln("<P>Version is: " + xmlDoc.version);
  if (xmlDoc.doctype != null)
    doc.writeln("<P>Doc Type is: " + xmlDoc.doctype);
  if (xmlDoc.readyState != null)
    doc.writeln("<P>Ready State is: " + GetReadyState(xmlDoc.readyState));
  if (xmlDoc.charset != null)
    doc.writeln("<P>Character Set is: " + xmlDoc.charset);
}

//
// Returns the ready state of the document
//
function GetReadyState(state)
{
  if (state == 0)
    return "READYSTATE_UNINTIALIZED";
  if (state == 1)
    return "READYSTATE_LOADING";
  if (state == 2)
    return "READYSTATE_LOADED";
  if (state == 3)
    return "READYSTATE_INTERACTIVE";
  if (state == 4)
    return "READYSTATE_COMPLETE";
  else
    return "undefined";
}

</SCRIPT>
</BODY>
</HTML>

© 1997 Microsoft Corporation. All rights reserved. Terms of Use.