views:

64

answers:

2

sI have an XML file that looks like the following...

<a>
  <b>
    <version>1.0</version>
    <c>
       <Module>foo.EXE</Module>
    </c>
    <c>
       <Module>bar.DLL</Module>
    </c>
  </b>
</a>

I have a COM DLL that uses MSXML2:IXMLDOMNode objects that call "selectNodes" something like...

    CComPtr<MSXML2::IXMLDOMNodeList> oRes = NULL ;
    HRESULT hResult = m_StartNode->selectNodes(sQuery, &oRes) ;

When sQuery is //a/b/c[Module[contains(.,'EXE')]], then hResult is E_FAIL and ::GetLastError() returns 0.

Admittedly, I am new to XPATH, but why wouldn't this return all the 'c' element that have a Module element containing 'EXE'.

((edit)) Other simpler XPATH expressions work. //a/b/c for example returns all elements as expected. It appears to be when I use 'contains()' or 'ends-with()' that the XPATH fails.

Here is a complete console app that demonstrates the problem.

// XMLTest.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#import <msxml3.dll> raw_interfaces_only rename("value", "xmlvalue")

int _tmain(int argc, _TCHAR* argv[])
{
    CoInitialize(NULL);
    CComPtr<MSXML2::IXMLDOMDocument> thedoc;
    thedoc.CoCreateInstance(__uuidof(MSXML2::DOMDocument));
    _variant_t filename(L"c:\\shared\\test\\BlackListSmall.xml");
    VARIANT_BOOL success;
    HRESULT res = thedoc->load(filename, &success);
    _bstr_t sQuery = L"//a/b/c[Module[contains(.,'EXE')]]";
    CComPtr<MSXML2::IXMLDOMNodeList> oRes;
    thedoc->selectNodes(sQuery, &oRes);
    thedoc = NULL;
    oRes = NULL;
    CoUninitialize();
    return 0;
}

and this is the contents of stdafx.h

#pragma once
#include "targetver.h"
#include <stdio.h>
#include <tchar.h>
#ifndef STRICT
#define STRICT
#endif
#define _ATL_APARTMENT_THREADED
#define _ATL_NO_AUTOMATIC_NAMESPACE
#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS  // some CString constructors will be explicit
#include <atlbase.h>
#include <atlcom.h>
#include <atlctl.h>
using namespace ATL;

BTW, when I run this and get to the selectNodes() call, I get three messages in the debugger output window...

First-chance exception at 0x7564fbae in XMLTest.exe: 0xE0000001: 0xe0000001.
First-chance exception at 0x7564fbae in XMLTest.exe: 0xE0000001: 0xe0000001.
First-chance exception at 0x7564fbae in XMLTest.exe: 0xE0000001: 0xe0000001.

... and the callstack provides no real info when I break on all exceptions.

((Final Edit)) I awarded the answer to Dimitre, see below. Here are the changes I made to my example program according to his answer...

#import <msxml4.dll> raw_interfaces_only rename("value", "xmlvalue")
...
CComPtr<MSXML2::IXMLDOMDocument2> thedoc; //changed from IXMLDOMDocument
...
HRESULT res = thedoc->load(filename, &success);    // unchanged
_bstr_t lang = L"SelectionLanguage";  // inserted 
_variant_t xpathlang = L"XPath";      // inserted
thedoc->setProperty(lang,xpathlang);  // inserted
_bstr_t sQuery = L"//a/b/c[Module[contains(.,'EXE')]]";  //unchanged
...

Thanks again Dimitre

A: 

you want:

"//a/b/c[contains(./Module/text(),'EXE')]"

Assuming you want the c elements that have a Module child element having text that contains the string 'EXE'

Jonathan Fingland
@Jonathan - I've tried your XPATH and I still get E_FAIL (oRes == null) and the last error is still 0.
Les
A: 

You have to issue this on the document object:

setProperty("SelectionLanguage", "XPath");

before calling the SelectNodes() methd with XPath expressions.

The default value is not XPath but some earlier selection language.

Dimitre Novatchev
You nailed it. I searched all over and could not find this simple answer. I also had to switch to IXMLDOMDocument2 in order to have the setProperty method available, but setting that value gets me past the road block. Thx.
Les