RCW issue when returning a BSTR

[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];
    *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();
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* 🙂


9 thoughts on “RCW issue when returning a BSTR

  1. 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?

  2. 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.

  3. 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)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s