2

I'm building an XML DOM document in C++. My problem is this: I execute an XPATH query from an Element in my Document, which I know will return another Element. The elementPtr->selectSingleNode call returns an IXMLDOMNode. How can I gain access to the attributes of this node?

Part of me wants to downcast the Node to an Element, but I couldn't get the cast to work.

I tried

MSXML2::IXMLDOMElementPtr pParentElement;
pParentNode->QueryInterface(__uuidof(MSXML2::IXMLDOMElement), 
                            (void**) &pParentElement);

Which results in the following runtime error:

0x0057cc58 _com_error::`scalar deleting destructor'(unsigned int)

The other route I tried was to just use nodes:

MSXML2::IXMLDOMNodePtr pParentNode = 
    pParameterElement->selectSingleNode("parent");
MSXML2::IXMLDOMNamedNodeMap* pParentAttributes;
pParentNode->get_attributes(&pParentAttributes);

MSXML2::IXMLDOMNodePtr pCategoryNameNode = 
    pParentAttributes->getNamedItem("Category");
VARIANT value;
pCategoryNameNode->get_nodeValue(&value);
CString categoryName = value;

This fails at "parentNode->get_attributes()".

It seems like I'm missing something; the API should not be this hard to use.

--edit--

What I was missing was that the selectSingleNode call was failing, leaving me with a NULL pointer. You can't call QueryInterface on that, neither can you call get_attributes on it :P

I've selected the answer that fits the question that I asked, not the answer that helped me to realise that I asked the wrong question.

Symmetric
  • 4,495
  • 5
  • 32
  • 50

3 Answers3

6

I don't see anything wrong with what you have written.

The smart com pointers will help you convert if they can, you don't have to write the query interface yourself.

MSXML2::IXMLDOMNodePtr pParentNode = pParameterElement->selectSingleNode("parent");
MSXML2::IXMLDOMElementPtr pParentElement( pParentNode );

Using the Ptr types is a bit painfull in my opinion, though the MSXML interface favours them. Here is an equivelant example using ATL

CComPtr<IXMLDOMNode> node = ...;
CComQIPtr<IXMLDOMElement> elementNode( node );

if( elementNode ) { 
// it was an element!
} else { 
// it's something else try again? 
}

The other attempt would look like...

CComPtr<IXMLDOMNamedNodeMap> attributes;
node->get_attributes( &attributes );
if( attributes ) {
  _bstr_t name( L"category" );
  attributes->getNamedItem(name);
}

And it's COM, it's always hard to use in C++ :(

Greg Domjan
  • 13,943
  • 6
  • 43
  • 59
  • It turns out that my problem was not the question that I asked ;) What would I gain by using ATL here? (I'm not being difficult, I just don't know about ATL). The syntax looks pretty similar. – Symmetric Nov 14 '08 at 21:53
  • 1
    I seem to remember that the ATL smart pointers are guaranteed not to throw an exception whereas the Compiler COM Support Class might unless you use raw_interfaces only. So you are meant to use the ATL ones inside other COM objects (or I suppose supply all the relevant catches), and only the compiler ones in client code. – Peter Nimmo Apr 08 '13 at 12:13
  • I'd use ATL to give some assistance in managing the lifetime of the objects and makes sure you release them, adding some additional exception safety. It also clears up your code by not having all the 'raw' COM calls littered around. – Greg Domjan Apr 23 '13 at 19:50
1

How did you try to do the downcast from IXMLDOMNode to IXMLDOMElement? You can't just use a C++ cast for that, as it's a COM object: you've got to use QueryInterface().


Looking at your QueryInterface() code, some thoughts:

  • Is pParentNode definitely not null? I don't think that this is the problem, given what you get, but it's worth checking.
  • The QueryInterface() call isn't quite right, I think: you've got to call AddRef() one way or another on the returned interface, and your code won't. As another poster noted, you can get _com_ptr_t<> to do this for you:

    MSXML2::IXMLDOMElementPtr pParentElement(pParentNode);
    

Doing this will, I hope, stop that "scalar deleting destructor" error that's probably caused by an AddRef()/Release() mis-match.

Anyway, try the above and see if pParentElement is null or not. If it is, the next thing I'd suggest is calling get_nodeType() on pParentNode to see what sort of node it really is. This might give a clue as to whether the XPath is not returning what you expect.

DavidK
  • 3,929
  • 1
  • 19
  • 26
  • Thanks for the suggestion; I looked into QueryInterface, but couldn't get it to work. I've added my latest attempt to the original question; can you see anything wrong with what I'm doing? Thanks – Symmetric Nov 14 '08 at 01:31
  • You've pointed me to the source of my problem; the QueryInterface call was correct, but my pParentNode was null, as the selectSingleNode call failed. The pParentElement(pParentNode) call is a much nicer way of performing the downcast though. – Symmetric Nov 14 '08 at 21:51
0

CComPtr is necessary for IXMLDOMNamedNodeMap, otherwise there would be a exception:

object of abstract class type IXMLDOMNamedNodeMap is not allowed

Alex Metsai
  • 1,837
  • 5
  • 12
  • 24