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.

user1964027
  • 61
  • 1
  • 3

2 Answers2

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.
BrendanMcK
  • 14,252
  • 45
  • 54
  • 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 more – user1964027 Jan 10 '13 at 15:04
  • 1
    That could indicate that the real bug is somewhere else in the code, and the BSTR pooling/caching was masking it, perhaps... – BrendanMcK Jan 11 '13 at 05:23
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.

StarPilot
  • 2,246
  • 1
  • 16
  • 18
  • 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. – user1964027 Jan 09 '13 at 20:21
  • And just to be sure: Now I've tried it your way, rather than my "similar" way. Still shows the same leak. – user1964027 Jan 09 '13 at 20:26
  • 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. – StarPilot Jan 09 '13 at 20:33
  • 1
    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*. – BrendanMcK Jan 11 '13 at 05:26