C#中的结构修改会影响非托管内存吗?

11
我个人的看法是不行的,因为托管和非托管内存是不同的,但我不确定.NET Framework是否在后台使用了Marshaling。

我的理解是: 当从非托管DLL获取结构体时,调用返回一个IntPtr,然后使用Marshal类将结构体复制到托管内存中(在托管内存中对结构体所做的更改并不会传递到非托管DLL中)。

我似乎找不到这方面的MSDN文档,如果有链接请提供,谢谢。

以下是我的代码:

[DllImport("mydll.dll", BestFitMapping=false, CharSet=CharSet.Ansi)]
private static extern int GetStruct(ref MyStruct s);

[StructLayout(LayoutKind.Sequential, Pack=0)]
struct MyStruct
{
     public int    Field1;
     public IntPtr Field2;
}

public void DoSomething()
{
      MyStruct s = new MyStruct();
      GetStruct(ref s);

      s.Field1 = 100; //does unmanaged memory now have 100 in Field1 as well?
      s.Field2 = IntPtr.Zero; //does unmanaged memory now have a NULL pointer in field Field2 as well?
}
2个回答

11
不,P/Invoke marshaller会将非托管结构成员值复制到结构的托管版本中。通常情况下,托管结构的版本与其非托管版本不兼容。内存布局是不可发现的,这是CLR用来重新排序字段以使结构更小的东西。封送是必要的,你必须创建一个副本。
由于您让它填充传递给它的内存,因此使用给定的函数签名无法修改结构。该函数本身已经复制了结构。但是,您可以使用Field2值,因为它是原始指针。如果它指向一个结构,则使用Marshal.PtrToStructure()自己进行封送。修改它的托管副本,并使用Marshal.StructureToPtr()将其复制回非托管内存。或使用Marshal.ReadXxx()和WriteXxx()直接访问它。

这是我期望的,但我真的希望在MSDN或类似网站上得到确认。另外,Pack=0就可以了(它只是表示平台默认值,并且在使用相同代码进行x86和x64部署时非常重要)。请参见: http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.structlayoutattribute.pack.aspx - userx
1
这里真的不是找链接的好地方,我建议使用谷歌。 - Hans Passant
重申我在问题中所说的并不是一个真正的答案 :). 我希望有一些文档来解释微软实现的行为。 - userx
StructureToPtr 对我很有用。我不得不修改一个非托管结构来设置配置值,它从未返回到 API,所以我不能只使用 PtrToStructure 创建的托管副本。但是 PtrToStructure、修改托管结构数据、StructureToPtr 的顺序非常好。 - bobasaurus

0

CSharp语言规范.doc第26页

结构体构造函数是使用new运算符调用的,但这并不意味着正在分配内存。结构体构造函数不是动态分配对象并返回对其的引用,而是仅返回结构体值本身(通常在堆栈上的临时位置),然后根据需要复制此值。

由于“struct”后备存储没有特殊之处,因此不应该期望在成员赋值背后进行匿名编组操作。


我认为这是正确的答案。事实上,当结构体(或其字段)的更改不会有任何钩子来将更改返回到非托管内存时,似乎可以得出这个问题的确定性答案。 - userx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接