我确信有一个简单的答案,但经过一些研究后我找不到它。我阅读并证明(除非我写的是错误的)自动编组通过托管内存中分配的引用(或类)传递的结构,由本机代码正确读取和写入,但是一旦代码执行返回到托管层,不保留本机代码中更改的值。这是一个例子:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public class DirtyWordsCheckResult
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1024)]
public string replace_string;
public EnumDirtyWordsType dirty_type;
public DirtyWordsCheckResult()
{
replace_string = new string(' ', 1024);
}
}
public enum EnumDirtyWordsType
{
kDirtyWordsTypeNormalAllowWords = 0, // normal allow words
kDirtyWordsTypeEvil = 1, // illegal,can not be displayed
kDirtyWordsTypeSensitive = 2, // legal, but contain sensitive
}
public override EResult DirtyWordsFilter(string words, bool replace_sensitive, out DirtyWordsCheckResult check_result)
{
check_result = new DirtyWordsCheckResult();
var result = Utils.DirtyWordsFilter(utils_, words, replace_sensitive, check_result);
return result;
}
本机函数DirtyWordsFilter确实正确地获取了已分配的对象,并且可以毫无问题地写入,但是不会保留这些值。
现在我知道我可以使用Marshal.AllocHGlobal来传递预分配的IntPTR,因此我不是在寻找解决方案,我只是很想知道为什么原来的机制不起作用。
答案 0 :(得分:5)
您使用的结构不是 blittable 。一个昂贵的单词意味着本机布局与托管布局不同。字符串导致它。不仅仅因为CharSet,.NET字符串看起来不像char []。这需要pinvoke编组程序在传递本机代码可以使用的指针之前创建正确大小的副本。
但它默认不将修改后的结构复制回来。您必须在参数上添加[Out]
以使其改变主意。我们无法看到[DllImport]声明,但它应该类似于:
[DllImport(...)]
private static extern EResult DirtyWordsFilter(..., [Out] DirtyWordsCheckResult check_result);
pinvoke marshaller对数据流没有具体的了解,即使你在声明中使用ref
或out
(通常用于结构声明)也没有。它只是看到参数通过引用传递,并假设[In]
为默认值。通常是正确和最佳的猜测,而不是这里。 Fwiw,请注意Pack = 1几乎永远不会正确,它需要匹配本机代码中使用的打包,它的默认值也是8。在这个特定情况下发生无关紧要。