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

9

我正在用C++编写一个COM API,并编写一个使用该API的C#程序。我的问题是在将BSTR传递到COM函数时,如何管理BSTR内存。假设我的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接口进行传递,还是我需要在函数内部释放它?谢谢!
2个回答

15
《为BSTR分配和释放内存》中指出,当你调用一个期望BSTR参数的函数时,你必须在调用前为BSTR分配内存并在调用后释放它。如果BSTR是一个输入参数,那么就不要释放它。C#(以及任何其他使用COM对象的运行时)必须遵循管理通过COM对象传入和传出内存的COM约定,因此如果BSTR是输入参数,则必须管理字符串的内存。否则,COM对象如何知道它是从C#还是其他语言运行时调用的呢?
另外,通过《托管代码和非托管代码之间的互操作性》可以了解到:关于所有权问题,CLR遵循COM风格的约定:
  • 作为[in]传递的内存属于调用方,并且应由调用方分配和释放。被调用方不应尝试释放或修改该内存。
  • 由被调用方分配并作为[out]传递或返回的内存属于调用方,并且应由调用方释放。
  • 被调用方可以从调用方处作为[in, out]传递的内存中释放内存,为其分配新内存,并覆盖旧的指针值,以便将其传递出去。新内存属于调用方。这需要双重间接引用,例如char **。
在互操作的世界中,调用方/被调用方变成了CLR/本地代码。上述规则意味着,在未固定(unpinned)情况下,如果在本地代码中接收到一个作为[out]从CLR传递给你的内存块的指针,则你必须负责释放该内存。CLR遵循COM的内存所有权规则。如果CLR接收到从本机代码作为[out]传递的指针,CLR需要释放它。另一方面,如果CLR分配了内存,则需要释放它。显然,在第一种情况下,本机代码需要进行解分配,在第二种情况下,托管代码需要进行解分配。因此,CLR遵循COM的内存所有权规则。证毕。

1
这个答案与CLR自动执行的互操作无关,而这正是OP所询问的。 - ildjarn
1
@Alf:实际情况是,关于手动COM内存管理的文档对于关于CLR“可能或不可能代表您做什么”的问题是无关紧要的。也就是说,这个问题是关于CLR COM封送的,而这个答案不是。 - ildjarn
1
@ildjarn,问题是在COM接口中是否应该释放参数。答案是否定的,因为CLR使用COM惯例来管理参数的内存所有权。实际上,这是真正的问题:“也就是说,C#是否释放了生成用于调用COM接口的BSTR,还是我应该在我的函数中释放它?谢谢!” - MSN
1
CLR通过COM+管理提供的内存,对吗?这个问题是关于在C#中需要做什么的。在C#中,你需要释放一个输出参数吗?CLR会为你做到这一点,对吗?如果答案是是、否、是——关于CLR和COM+内存管理的评论是无用的,因为它们没有回答关于C#的问题。QED - Kyle W
1
好的,从C#开发人员的角度来看,我读到了这篇文章,“我应该在我的函数中释放它吗?” - 不需要,CLR会为您完成。再次阅读后,我相信他是在询问无论被C#或C++调用,他是否应该在自己的C++函数内部释放它。如果他的意思确实如此,那么你的回答就很有道理了。 - Kyle W
显示剩余13条评论

0

你的意思是从C#开发者的角度还是从C++开发者的角度来看?

C#开发者在处理COM+时不必担心任何内存管理问题。

如果用C++创建COM+组件,你不需要知道是谁在调用你,内存语义相同。如果是传入参数,无论是C++还是C#,调用方都负责管理内存。在C#中,CLR会为他们处理它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接