0

I have to deal with a legacy application that uses MSXML to write measurement data to a simple XML file. Basicly, this is what's happening:

MSXML2::IXMLDOMDocument2Ptr pXmlDocument;
HRESULT comResult = CXMLUtil::createXMLDocument(pXmlDocument);
// ... check HRESULT ...
MSXML2::IXMLDOMNodePtr pXmlMainNode = pXmlDocument.GetInterfacePtr();
MSXML2::IXMLDOMNodePtr pXmlSubNode = CXMLUtil::AppendNewElement(pXmlDocument, pXmlMainNode, RootTag, "");
// ... create further nodes ...
MSXML2::IXMLDOMNodePtr pXmlTmpNode = CXMLUtil::AppendNewElement(
pXmlDocument,
pXmlDataSetNode,
measDataTag,
measdata,
numberOfDataItems );
// ... append further items ...

After each item, pXmlTmpNode.Release() is called. At the end, all other nodes as well as pXmlDocument are Released.

createXMLDocument is defined as follows:

HRESULT CXMLUtil::createXMLDocument(MSXML2::IXMLDOMDocument2Ptr &pXmlDocument) {
    MSXML2::IXMLDOMDocument2 *xmlDocument = 0;
    HRESULT comError = CoCreateInstance(__uuidof(MSXML2::DOMDocument),NULL,CLSCTX_INPROC_SERVER,IID_PPV_ARGS(&xmlDocument));
    if (comError) return comError;
    xmlDocument->put_async(VARIANT_FALSE);
    xmlDocument->put_validateOnParse(VARIANT_FALSE);
    xmlDocument->put_resolveExternals(VARIANT_FALSE);
    xmlDocument->put_preserveWhiteSpace(VARIANT_TRUE);
    MSXML2::IXMLDOMDocument2Ptr ptr(xmlDocument);
    pXmlDocument = ptr;
    return comError;
}

For appending new elements (i.e. unsiged char arrays), this function is applied:

MSXML2::IXMLDOMNodePtr CXMLUtil::AppendNewElement(MSXML2::IXMLDOMDocument2Ptr pXMLDom, MSXML2::IXMLDOMNodePtr pParent, CComBSTR strElementName, unsigned char* pData, long nData) {
    MSXML2::IXMLDOMNodePtr pData1 = pXMLDom->createElement(BSTR(strElementName));
    pData1->put_dataType(CComBSTR(_T("bin.base64")));
    SAFEARRAY* psa = SafeArrayCreateVector( VT_UI1, 0L, nData);
    psa->pvData = pData;
    VARIANT var;
    VariantInit(&var);
    var.parray = psa;
    var.vt = (VT_ARRAY | VT_UI1 );
    pData1->nodeTypedValue = var;
    pParent->appendChild(pData1);
    SafeArrayDestroy(psa);
    return pData1;
}

Regardless of wether #import <msxml4.dll> or #import <msxml6.dll> is used, there seems to be a leak of memory. Since measdata etc. can be huge, that's a pretty big problem. Is there something I can do to make the code work? Thanks, Matthew

Matz
  • 583
  • 1
  • 6
  • 21
  • 1
    "there seems to be a leak of memory": how did you determine that? – Remus Rusanu Dec 03 '13 at 10:51
  • Calling the function several times and watching the process' memory consumption. – Matz Dec 03 '13 at 10:54
  • "process' memory consumption" and by that, what do you mean? Specifically, which counter(s). BTW, `if (comError)` should be [`if (FAILED(comError))`](http://msdn.microsoft.com/en-us/library/windows/desktop/ms693474(v=vs.85).aspx), in general. – Remus Rusanu Dec 03 '13 at 11:02
  • use this tool if you are on solaris:http://theunixshell.blogspot.com/2013/11/finding-memory-leaks-on-solaris-is-no.html – Vijay Dec 03 '13 at 11:09

1 Answers1

2
MSXML2::IXMLDOMDocument2 *xmlDocument = 0;
HRESULT comError = CoCreateInstance(__uuidof(MSXML2::DOMDocument),
    NULL,CLSCTX_INPROC_SERVER,IID_PPV_ARGS(&xmlDocument));
...
MSXML2::IXMLDOMDocument2Ptr ptr(xmlDocument);
pXmlDocument = ptr;

This code leaks xmlDocument. ptr(xmlDocument) does an extra AddRef, but there is no Release for xmlDocument going out of scope. You use smart ptrs everywhere, no reason not to use one here as well, maybe something like this:

MSXML2::IXMLDOMDocument2Ptr xmlDocument;
HRESULT comError = CoCreateInstance(__uuidof(MSXML2::DOMDocument),
    NULL,CLSCTX_INPROC_SERVER,IID_PPV_ARGS(&xmlDocument));
...
pXmlDocument = xmlDocument;
Remus Rusanu
  • 288,378
  • 40
  • 442
  • 569
  • `MSXML2::IXMLDOMDocument2Ptr xmlDocument` can't be done; it's telling me it's an abstract class. However, adding an extra `Release` after creating the `ptr` pointer did the trick. Thank you! – Matz Dec 03 '13 at 11:23