[Edit – Jan 26 2005]Unfortunately, the information in this entry is incorrect. See this later entry for details.[/Edit]
I was trying out some RCW stuff (between VC++ 7.1 unmanaged and C#) and faced this little problem of a BSTR that could not be freed at all. I’ll explain the issue to you :-
Let’s say I have a COM method as follows :-
HRESULT GetHerName([in] LONG id, [out,retval] BSTR* namestr);
And say, I have the following implementation :-
STDMETHODIMP CNoomata::GetHerName(LONG id, BSTR* namestr)
{
wchar_t str[100];
swprintf(str,L"Sheila_%d",id);
*namestr = SysAllocString(str);
return S_OK;
}
Now C# will see this method as :-
string GetHerName(int id);
And I can use the class easily as follows :-
NoomataClass addr = new NoomataClass(); Console.WriteLine(addr.GetHerName(10)); string s2 = addr.GetHerName(22);
All that is very fine, but there is one *major* problem here. Every time I call GetHerName from my C# program I leak a BSTR! That’s bad, really bad!
RCW marshalling magic will create a new System.String object for me from the BSTR that is returned by the COM method, but then it ignores the BSTR – and eventually leaks it. Now had this been a COM client, the COM client would have been responsible for freeing the BSTR using SysFreeString, but the C# program does not even see the returned BSTR.
Heath Stewart (C# MVP and Fellow CPian) has confirmed that the BSTR will indeed leak in the above situation and he has offered an alternate solution that will avoid the leak. It’s not a very straightforward solution though – Heath suggests that instead of having the COM method return a BSTR*, we should have it return an IntPtr; now we can get a System.String object by using Marshal.PtrToStringBSTR (this will be a copy though), and once we do that we can use Marshal.FreeBSTR to free the BSTR pointed to by the IntPtr. I don’t usually say “ouch”, but I think this time I think I can say *ouch* and in fact *double ouch* π
I’m baffled, that seems to be one of the basic things when you are expected to do any interop, :sigh:
I need to add a :sigh: emoticon for the blog π
Hello – I was just pointed to your blog by one of your readers.
I am fairly sure that the marshaler does the right thing here…could you package up and send me a repro so that I can investigate this?
How did you verify it leaked? BSTR memory is cached by windows and so depending on how you checked this it may appear to have leaked when in fact it did not. See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/automat/htm/chap7_2xgz.asp for more details.
In a discussion that arose from this here in Microsoft – yes, much much later – it turns out that this is not the case unless you stumbled across a bug. The behavior is that the shim – mscoree.dll in most cases – is to follow COM rules and free memory itself, but differs from native C++ memory management rules.
See Memory Management in the interop marshaling section of the .NET Framework SDK for more information.
Thanks guys
I don’t have a repro now – happened so long ago. Someone reported in some forum about a leaking app and since he was using RCW, I made this guess as to what might be happening in his case. Later when Heath and some others discussed it on a thread I started on one of the CP forums, everyone concluded that this leak was possible.
But on thinking about it now, chances are low that this bug exists – as it’d have got noticed several times already by other people. I guess I’ll make another post explaining how this post was incorrect and also edit this post (don’t wanna delete it – I’ll let it remain here to remind me not to make conclusions too soon in future)
what abt the vc++6.0 components returning BSTR*?
Any inputs on passing bstr string from VC++ to managed C# dll?