将一个字符缓冲区从.net传递到COM,并将其更新
本文关键字:COM 更新 一个 缓冲区 字符 net | 更新日期: 2023-09-27 18:15:17
我有以下COM方法要从c#调用,它返回一个字符串在提供的缓冲区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
有任何影响。
嗯,这当然是一个很难正确调用的函数。您的声明大致是可以的,您只需要应用[PreserveSig]属性并使返回值类型为int,这样您就可以发现S_FALSE返回值,该返回值表明没有下一个元素。
困难在于必须预先猜测要传递的StringBuilder有多大。本机代码获得一个指向GC堆的原始指针,该指针指向构建器缓冲区,因此意外是非常致命的。您必须预先猜测构建器的适当容量,并将其作为初始pcch参数传递。
编组程序确实注意函数返回后的SizeParamIndex。它只复制ppch所指示的字符。如果由于任何原因,它写的内容超过了缓冲区的容量,那么程序将立即终止并抛出ExecutionEngineException,因为这表明GC堆已损坏。
请注意,如果您猜测容量过低,那么您不一定能发现这一点。当函数只复制尽可能多的合适的字符并且不返回错误代码时,您可能会得到一个截断的字符串。找出这个问题的最好方法是测试它并有意地传递一个小构建器。注意返回值
值得指出的一个怪癖是,函数签名命中了一个在COM早期很常见的hack,实际上通过OLECHAR*返回二进制数据而不是文本。这是一个强烈的提示,因为字符串不能保证以零结尾。这在。net中不会有好的结果,当字符串规范化时数据会被损坏。当数据恰好匹配其中一个utf-16代理字符时,使程序崩溃。如果是这种情况,那么您需要一个简短的[]而不是StringBuilder。