C# · 12月 26, 2021

从C#(COM互操作)传递BSTR到COM函数的公约

我正在C编写一个API,并编写一个在C#中使用这个API的程序.我的问题是关于BSTR内存管理语义,将BSTR传递到COM函数.说我的IDL看起来像: HRESULT SomeFunction([in] BSTR input);

目前这个功能是这样实现的:

HRESULT SomeFunction(BSTR input) { // Do stuff …,then: SysFreeString(input);}

当我从C#中用类似SomeFunction(myString)的方法调用它时,C#会生成这样的东西(伪代码):

myString = SysAllocString(“string”);SomeFunction(myString);

或者更像这样:

myString = SysAllocString(“string”);SomeFunction(myString);SysFreeString(myString);

也就是说,C#是否释放它产生的BSTR,以封送到COM接口,还是应该在我的函数内释放它?谢谢!

解决方法 从 Allocating and Releasing Memory for a BSTR:

When you call into a function that expects a BSTR argument,you
must allocate the memory for the BSTR before the call and
release it afterwards. …

所以如果它是一个输入参数,不要释放它. C#(以及使用COM对象的任何其他运行时)必须遵守用于管理内存传入和传出COM对象的COM约定,因此必须管理字符串的内存(如果它是输入参数).否则,COM对象如何知道它正在从C#或其他语言运行时调用?

额外的google-fu表示:Marshaling between Managed and Unmanaged Code

… Regarding ownership issues,the CLR follows COM-style
conventions:

Memory passed as [in] is owned by the caller and should be both
allocated by the caller and freed by the caller. The callee should
not try to free or modify that memory. Memory allocated by the callee and passed as [out] or returned
is owned by the caller and should be freed by the caller. The callee can free memory passed as [in,out] from the caller,
allocate new memory for it,and overwrite the old pointer value,
thereby passing it out. The new memory is owned by the caller. This
requires two levels of indirection,such as char **.

In the interop world,caller/callee becomes CLR/native code. The rules
above imply that in the unpinned case,if when in native code you
receive a pointer to a block of memory passed to you as [out] from
the CLR,you need to free it. On the other hand,if the CLR receives
a pointer that is passed as [out] from native code,the CLR needs to
free it. Clearly,in the first case,native code needs to do the
de-allocation and in the second case,managed code needs to do
de-allocation.

所以CLR遵循用于内存所有权的COM规则. QED.