Tuesday, June 26, 2007

Like most developers, I have tons of small code samples and snippets lying around.  I'm starting to go through them in order to post what might be useful to others.

Here's one that I wrote about 5 years ago when I had a customer complaining about the speed of parsing an XML document using MSXML.  Of course this test simply confirmed that yes, MSXML is molasses-in-January slow and that if raw speed is what you want, then something like Expat is a better route to go.  However the test is a reasonably good sample of how you might use the DOMDocument from a C++ application.

As always, the code is an as-is sample.  uSe it at your own risk.

#include <windows.h>
#include "objbase.h"
#include "oleauto.h"
#include "MSXML.h"
#include "ATLBASE.h"
#import "msxml.dll" named_guids raw_interfaces_only

TCHAR *xmlfile = _T("\\test.xml");
using namespace MSXML;

void GetCurrentDirectory(TCHAR *szDirectory);


int WINAPI WinMain(    HINSTANCE hInstance,
                    HINSTANCE hPrevInstance,
                    LPTSTR lpCmdLine,
                    int nCmdShow)
{
    MSXML::IXMLDOMDocument      *iXMLDoc          = NULL;
    HRESULT                     hr;
    VARIANT_BOOL                b;
    int                         et                = 0;
    HANDLE                      hFile             = NULL;
    DWORD                       wsize, dwhigh     = 0;
    TCHAR                       filename[MAX_PATH];
    MEMORYSTATUS                ms;
    BYTE                        *buffer           = NULL;
    TCHAR                       *wbuffer          = NULL;
    DWORD                       dwRead            = 0;
    DWORD                       toalloc           = 0;
    int                         i;
    CComBSTR                    *bstr;

    _tprintf(_T("Starting XML Benchmark...\n"));
    _tprintf(_T("Creating DOMDocument..."));

    // start COM
    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);

    // create a DOMDocument
    hr = CoCreateInstance (MSXML::CLSID_DOMDocument, NULL,
                 CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER,
                 MSXML::IID_IXMLDOMDocument,(LPVOID *)&iXMLDoc);

    // check for success
    if(!iXMLDoc)
    {
        _tprintf(_T("failed (hr = %i)\n"), hr);
        goto exit;
    }

    _tprintf(_T("ok\n"));

    // synchronous operation
    iXMLDoc->put_async(VARIANT_FALSE);

    // get the data file - pull from the same directory as our app
    GetCurrentDirectory(filename);
    _tcscat(filename, xmlfile);
    _tprintf(_T("Opening XML file (%s)..."), filename);

    hFile = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);

    if(hFile == INVALID_HANDLE_VALUE)
    {
        _tprintf(_T("failed (err = %i)\n"), GetLastError());
        goto exit;
    }

    // determine the file size
    dwsize = GetFileSize(hFile, &dwhigh);

    _tprintf(_T("ok\n"));

    // check available memory
    ms.dwLength = sizeof(MEMORYSTATUS);
    GlobalMemoryStatus(&ms);

    // either 1/3 available memory or file size, whichever is less
    toalloc = (dwsize > (ms.dwAvailPhys / 3)) ? (ms.dwAvailPhys / 3) : dwsize;

    _tprintf(_T("Allocating %i bytes..."), toalloc);
    
    //alocate buffers. data is ANSI, loadXML requires Unicode
    buffer = (BYTE *)LocalAlloc(LPTR, toalloc);
    wbuffer = (TCHAR *)LocalAlloc(LPTR, toalloc * sizeof(TCHAR) + 1);

    // check for success
    if((buffer == NULL) || (wbuffer == NULL))
    {
        _tprintf(_T("failed (err = %i)\n"), GetLastError());
        goto exit;
    }

    _tprintf(_T("ok\nReading %i bytes..."), toalloc);
    
    // fill the RAM buffer with data
    ReadFile(hFile, &buffer[0], toalloc, &dwRead, NULL);

    // lazy man's ANSI to Unicode conversion
    // this is NOT recommended for production code, don't cut and paste!!
    for(i = 0 ; i < (int)toalloc ; i++)
    {
        wbuffer[i] = buffer[i];
    }
    wbuffer[i] = '\0';
    bstr = new CComBSTR(wbuffer);

    _tprintf(_T("ok\nLoading %i bytes into DOMDocument..."), toalloc);

    // get a start time
    et = GetTickCount();

    // load the xml
    hr = iXMLDoc->loadXML(*bstr, &b);

    // determine ET
    et = GetTickCount() - et;

    // check for load failure
    if(!b)
    {
        _tprintf(_T("failed (hr = %i)\n"), hr);
        _tprintf(_T("GetLastError = %i\n"), GetLastError());
        goto exit;
    }

    _tprintf(_T("done.\n\nET = %i ms\n\n"), et);

exit:
    // release the DOMDocument
    iXMLDoc->Release();

    // release the file
    CloseHandle(hFile);

    // release COM
    CoUninitialize();

    _tprintf(_T("Exiting XML Benchmark in 5 seconds...\n"));
    
    // free buffers
    if(buffer)
        LocalFree(buffer);
    
    if(wbuffer)
        LocalFree(wbuffer);

    // wait to allow display to persist for a while
    Sleep(5000);

    return 0;
}

void GetCurrentDirectory(TCHAR *szDirectory)
{
    TCHAR *p, *p2;

    // get current directory
    GetModuleFileName(NULL, szDirectory, MAX_PATH);

    // trim off the exe name
    p = _tcsstr(szDirectory, _T("\\"));

    if(p == NULL)
    {
        // no slash found - return root?
        _tcscpy(szDirectory, _T("\\"));
        
        return;
    }

    while( p != NULL)
    {
        p2 = p + 1;
        p = _tcsstr(p2, _T("\\"));
    }

    // null terminate to crop
    p2--;
    *p2 = '\0';

    return;
}

6/26/2007 4:16:45 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0]  | 
Name
E-mail
Home page

Comment (HTML not allowed)  

Enter the code shown (prevents robots):