我有从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
产生任何影响。
答案 0 :(得分:4)
嗯,正确调用肯定是一个很难的功能。您的声明大致正常,您只需要应用[PreserveSig]属性并使返回值类型为 int ,这样您就可以发现一个S_FALSE返回值,表明没有下一个元素。
困难在于必须先猜测StringBuilder传递的大小。本机代码获取一个指向GC堆的原始指针,指向构建器缓冲区,因此事故非常致命。您必须在构建器的适当容量前预测并将其作为初始 pcch 参数传递。
marshaller 在函数返回后注意SizeParamIndex。它只会复制 ppch 指示的字符数。如果由于任何原因它写入的内容超出缓冲区的范围,则程序将立即中止ExecutionEngineException,因为这表明GC堆已损坏。
请注意,如果您猜测容量太低,那么您无法发现这一点。当函数只复制适合的字符并且不返回错误代码时,您可能只会获得截断的字符串。找出这是一个问题的最佳方法是通过测试这个并故意通过一个小建筑商。注意返回值。
一个怪癖值得指出,函数签名在COM的早期常见的黑客攻击,实际上通过OLECHAR *返回二进制数据而不是文本。强烈暗示是这种情况,因为字符串不能保证为零终止。这在.NET中不会有好结果,当字符串规范化时,数据将被破坏。当数据恰好与utf-16代理字符之一匹配时崩溃您的程序。如果是这种情况,那么你需要一个简短的[]而不是一个StringBuilder。