C++ leak with VARIANT / bstrVal code
Asked Answered
S

2

6

A leak checker tells me that I have a memory leak on memory that is allocated in the following code:

// Get the value from the object as a variant.
VARIANT vVal;
VariantInit ( &vVal );
hres = clsObj->Get ( fieldName.c_str(), 0, &vVal, 0, 0 );
if ( FAILED ( hres ) )
{
    (... various cleanup / throw stuff ...)
}

// And get it as a wstring.
wstring val ( vVal.bstrVal );

(... do some standard, non-memory leaking stuff with the wstring ...)    

// Clean up.
VariantClear ( &vVal );

The "clsObj" in there is a IWbemClassObject, which is a Microsoft interface for WMI stuff.

The specific line that allocates the leaked memory is the "clsObj->Get" line. The leak checker then reports more specific code for the leak itself (i.e. further in the stack trace at the time of the allocation of the leaked memory) for which I don't have the source code:

(ole32): (filename not available): CoRevokeMallocSpy    
(OLEAUT32): (filename not available): GetErrorInfo  
(OLEAUT32): (filename not available): SysAllocStringLen 
(OLEAUT32): (filename not available): SysAllocString    
(wbemcomn): (filename not available): CVar::SetBSTR 
(wbemcomn): (filename not available): CVar::FillVariant 
(fastprox): (filename not available): CWbemObject::Get  

So the underlying BSTR in the VARIANT vVal is being leaked, it seems. But I'm doing a VariantClear... do I have to do something else?

Perhaps I'm leaking it in the wstring constructor? But if so, I don't understand. I thought bstrVal essentially just boils down to a char pointer (or wchar or whatever); the wstring constructor should just copy from that address as if it were any other pointer, right?

It's not like the wstring constructor takes over responsibility for clearing up the memory that's originally pointed to by vVal.bstrVal, as if it were doing a Detach() on some reference counted COM object, is it?

In case it matters, this is in Visual C++ 6.

Smail answered 9/1, 2013 at 19:56 Comment(0)
P
2

There may be no leak! See this article by Microsoft's Larry Osterman on the subject which describes something similar to what you are seeing:

I found a bunch of the leaks, and fixed them, but one of the leaks I just couldn't figure out showed up every time we allocated a BSTR object. [...]

Basically, OLE caches all BSTR objects allocated in a process to allow it to pool together strings. As a result, these strings are effectively leaked "on purpose". [...]

Fortunately, there's a way of disabling the BSTR caching, simply set the OANOCACHE environment variable to 1 before launching your application. If your application is a service, then you need to set OANOCACHE as a system environment variable (the bottom set of environment variables) and reboot.
Piotrowski answered 10/1, 2013 at 5:32 Comment(2)
Hmmm... thanks for pointing this out! Unfortunately, OANOCACHE seems to cause my app to crash when it comes time to exit (and before the leak checker produces its output). It's an interesting possibility, though - I'll play around with it some moreSmail
That could indicate that the real bug is somewhere else in the code, and the BSTR pooling/caching was masking it, perhaps...Piotrowski
J
0

A BSTR is two data elements. It is an int that has the count of the length of the basic string, and the basic string as well. So even if you have an emtpy BSTR holding "", you have to empty it properly before assigning a new BSTR value to it. Otherwise, even if you free the 'string' part, you leak away the int data member.

I suspect you are leaking the fieldname.c_str(). It is probably creating a BSTR on the stack and that is leaking away.

Alter your code to do this instead:

CComBSTR tempBSTR = fieldName.c_str();
hres = clsObj->Get ( tempBSTR, 0, &vVal, 0, 0 );

This will at a minimum let you make sure it isn't an auto built BSTR leaking away on your call to Get().

By the way, if you have to work with BSTR, look into using CComBSTR in its place. It can help prevent basic memory leaks.

Josettejosey answered 9/1, 2013 at 20:18 Comment(4)
I thought of this possibility after posting my question, and tried a similar thing: CComBSTR, then .Attach with W2BSTR(fieldName.c_str()). It still reports the leak, and it's still in the clsObj->Get line, not in the lines where I'm making that CComBSTR.Smail
And just to be sure: Now I've tried it your way, rather than my "similar" way. Still shows the same leak.Smail
The next step is to make sure you are passing exactly what your clsObj->Get() needs. If so, and if your vVal is pristine (empty/inited), then it would seem the Get() is leaking internally, or you are leaking on its return. Since you don't have the symbols for the Get() call, you can try to step through the disassembly code and see if anything does leak, but that won't be easy.Josettejosey
wstring.c_str() is part of the standard C++ library, and just returns an internal pointer into the string itself, it doesn't need any special clean-up. You can use it as-is if Get() takes a LPCWSTR (which is basically a const wchar_t*) as a param; but if Get() takes a BSTR, then you should be creating a temp BSTR to pass as a param rather than a wchar_t*.Piotrowski

© 2022 - 2024 — McMap. All rights reserved.