我确信有一个简单的答案,但经过一些研究后我找不到它。我阅读并证明(除非我写的是错误的)通过在托管内存中分配的引用(或类)传递的自动编码结构由 native 代码正确读取和写入,但是一旦代码执行返回到托管层, native 代码中更改的值不会保留。这里有一个例子:
[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;
}
native 函数 DirtyWordsFilter 确实正确获取分配的对象并且可以毫无问题地写入其中,但是不会保留值。
现在我知道我可以使用 Marshal.AllocHGlobal 来传递预分配的 IntPTR,因此我不是在寻找解决方案,我只是想了解为什么原始机制不起作用。
最佳答案
您使用的结构不是blittable。一个昂贵的词,意味着 native 布局与托管布局不同。字符串导致它。不仅仅是因为 CharSet,.NET 字符串看起来一点也不像 char[]。这需要 pinvoke 编码器在将指针传递给 native 代码可以使用的指针之前创建正确大小的副本。
但默认情况下它不会将修改后的结构复制回去。您必须将 [Out]
放在参数上才能使其改变主意。我们看不到 [DllImport] 声明,但它应该类似于:
[DllImport(...)]
private static extern EResult DirtyWordsFilter(..., [Out] DirtyWordsCheckResult check_result);
pinvoke 编码器没有数据流的具体知识,即使您在声明中使用 ref
或 out
(通常出现在结构声明中)也是如此。它仅看到参数通过引用传递,并假定 [In]
为默认值。通常是正确且最佳的猜测,此处不适用。 Fwiw,请注意 Pack=1 几乎永远不会正确,它需要匹配 native 代码中使用的包装,它的默认值也是 8。在这种特定情况下碰巧无关紧要。
关于c# - 为什么编码对象不保留在 native 代码中修改的值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43762241/