将字符缓冲区从.NET传递给COM并将其更新回来

我有从C#调用以下COM方法,它在提供的缓冲区pchText (不一定是零终止)中返回一个字符串,并在pcch复制字符数:

 HRESULT Next([in, out] long* pcch, [out, size_is(*pcch)] OLECHAR* pchText); 

如何为互操作定义C#签名?

到目前为止,我试过这个:

 void Next(ref int pcch, [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)] System.Text.StringBuilder pchText); 

它似乎工作,但我不确定SizeParamIndex是否对StringBuilder有任何影响。

嗯,正确调用肯定是一项困难的function。 您的声明大致正常,您只需要应用[PreserveSig]属性并使返回值类型为int,这样您就可以发现一个S_FALSE返回值,表明没有下一个元素。

困难在于必须在前面猜测StringBuilder要传递多大。 本机代码获取一个指向GC堆的原始指针,指向构建器缓冲区,因此事故非常致命。 您必须在构建器的适当容量前预测并将其作为初始pcch参数传递。

函数返回后,marshaller 注意SizeParamIndex。 它只会复制与ppch指示的字符数一样多的字符。 如果由于任何原因它写入的内容超出缓冲区的范围,则程序将立即中止ExecutionEngineException,因为这表明GC堆已损坏。

请注意,如果您猜测容量太低,那么您无法发现这一点。 当函数只复制适合且不返回错误代码的字符时,您可能只会得到一个截断的字符串。 找出问题的最佳方法是通过测试这个并故意传递一个小型构建器。 注意返回值。

有一个怪癖是值得指出的,函数签名在COM的早期常见的黑客攻击,实际上通过OLECHAR *返回二进制数据而不是文本。 强烈提示是这种情况,因为字符串不能保证为零终止。 这在.NET中不会有好结果,当字符串规范化时,数据将被破坏。 当数据恰好与utf-16代理字符之一匹配时崩溃您的程序。 如果是这种情况那么你需要一个short []而不是StringBuilder。