Question on Memory Management using MSXML4

I have a question about when I am supposed to release pointers...

I am creating an xml document

MSXML2::IXMLDOMDocument *pDoc;
MSXML2::IXMLDOMElementPtr pNode;
MSXML2::IXMLDOMElementPtr pNode2
MSXML2::IXMLDOMElementPtr pNode3;

and constantly making calls like pNode = pDoc->createNode().

am I supposed to free some memory before I re-assign pNode?
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Yes. You need to call pNode->Release() when you've finished with it.

An easy way might be to use the CComPtr<> template wrapper, which does this for you as it acts as a smart pointer.

So, something like

CComPtr<IXMLDOMElement>  lNode;

lNode = SomeFunctionToGetYouANodeHere();

lNode = SomeOtherFunction();

and you get an automatic 'release' between the two.
jjacksnAuthor Commented:
thanks andrew, would the CComPt be transparent to my code?  
It's a standard wrapper defined in <atl\atlbase.h>

It's basically transparent, except for

a) If you put it into a container, you need to wrap the CComPtr in the CAdapt<> template

vector< CComPtr<IUnknown> >  MyVec;
IUnknown *a;
CComPtr<IUnknown> b;
MyVec.push_back( a ); or push_back(b)


vector< CAdapt < CComPtr< IUnknown > > > MyVec
a & b as before
then push_back(a) or b is OK

and b) you can't use the & operator if the CComPtr is NOT NULL - because that would be silly, because you'd be changing the pointer without dealing with reference counts.

For something like a QueryInterface call, you'd do

CComPtr<IMyInterface> x;

something->QueryInterface( IID_IMyInterface, (void**) &x );
which is OK, but you MUST ensure that 'x' is NULL before the call.

The constructor initialises it to NULL, but you couldn't, for example, do:

something->QueryInterface( IID_IMyInterface, (void**) &x );
something->QueryInterface( IID_IMyInterface, (void**) &x );

in succession, without an explicit x = NULL between the two.

(the & operator in the second call will assert )


Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
OWASP: Threats Fundamentals

Learn the top ten threats that are present in modern web-application development and how to protect your business from them.

jjacksnAuthor Commented:
I'm have a problem.  I'm not trying to use CComPtrs, and just calling release.  However, I'm getting memory errors all over the place:  here is my strucutre:

MSXML2::IXMLDOMDocument *pDoc;
MSXML2::IXMLDOMElementPtr pRoot;
MSXML2::IXMLDOMElementPtr pTop1;
MSXML2::IXMLDOMElementPtr pTop2
MSXML2::IXMLDOMElementPtr pNode;
MSXML2::IXMLDOMElementPtr pNode2
MSXML2::IXMLDOMElementPtr pNode3;

pRoot = pDoc->getRoot();

while(info to be parserd)
pNode = pDoc->CreateNode()
pNode->SetAttribute() //a bunch of times
PNode2 = pDoc->CreateNode("to_be_child_of_pNode);
//Set attributes



When I do the clenaup inside the loop, I get an error the second time through the loop.  If I don't free memory inside the loop, I get a memory error when the call returns and the caller tries to access and simple integer. When I don't make an Release calls, it completes with no errors (but I think I'm leaking memory).
pRoot = pDoc->getRoot();   - This will AddRef on pRoot

while(info to be parserd)
pNode = pDoc->CreateNode()     - This will AddRef on pNode
pNode->SetAttribute() //a bunch of times  - this won't AddRef
PNode2 = pDoc->CreateNode("to_be_child_of_pNode); - This will AddRef on pNode2
//Set attributes
pNode->appendChild(pNode2);  -- This won't AddRef
pNode2->Release()  -- OK, so now pNode2 is clear. You shouldn't use it anymore.

pTop1->appendChild(pNode);  -- don't know where pTop1 and 2 are from?
pNode->Release()  - ok, so you've release it, and shouldn't use it anymore

pRoot->appendChild(pTop1);  - no AddRef for you to worry about
pRoot->appendChild(pTop2);  - ditto
pTop1->Release(); - it depends where you got these from
pRoot->Release(); - that looks right
pDoc->release();  - it depends where this comes from

So, in summary, that's probably right, though I don't know about pTop1 and 2

What error do you get, and on what line?

(And I'm about to go home now, so won't be back until tomorrow morning!)

jjacksnAuthor Commented:

pNode, pNode2, are going to be used loop goes through, aren't they just pointers?  I get an error in SetAttriute(), it says its null is that ok?  
What does pNode equal after the CreateNode, then? Couldn't you just post a bit of code that's complete and does something, then I can compile it myself :-)
jjacksnAuthor Commented:
Adnrew, I just swtiched them all to CComPtrs, so hopefully that will take care of the problem.  I had a really weird bug however, and I was wondering if you may know the causse.

I was trying to extract first and last name from e-mail fields that were like "Firstname Lastname".  I had to account for middle names as well.  I forgot to examine the case when there was no space, so I was making calls like stl::string substring(0,-1) and they were just returning the entire string, I was then calling char* = string->c_str();  none of this ways giving erros, and all of the fields appeared valid in quickwatch, but I was getting a conversion error from the _bstr_t method that was being called inherently when passing char* args to node methods much later in the code.  

This code was running fine when the COM object was called from C++ code.  However, this was giving an error when run from C# code.  So, I very carefully made the exact same method calls between two test programs, and one returned fine, the C# code had an error.  Does the COM object have any idea what is calling it? (I thought not.)  If so, why?  If not, why would anything cause an error form one caller to another?
No, it doesn't know who's calling it.

The obvious problem is a memory management one.

What's your compiler here? Borland? the c_str() function for strings in Borland is dangerous. It returns you a char* pointer that is only valid for as long as you don't change the original string at all, and it stays in scope.

So, something like

AnsiString AFunction(); {...}

char *MyStr = AFunction().c_str()

isn't really valid. The result of AFunction() is a temporary AnsiString. You then c_str() it, and get a char* which immedaitely becomes invalid 'cos the temporary goes out of scope.

You'd have to do

AnsiString lTempResult = AFunction()
char *MyStr = lTempResult.c_str()

// use MyStr now, before lTempResult goes out of scope.

Could that be related?
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Fonts Typography

From novice to tech pro — start learning today.