2013-12-10 42 views
0

如果我理解正确,当使用非可移位值的结构时,来自非托管内存的结构数据被复制到托管内存中(基本上具有相同的结构两次)。C#Pinvoke IntPtr结构和内存

此外,如果我没有错,IntPtr变量存储在托管内存中,但它们指向的数据位于非托管内存中。

现在可以说,我有一个委托方法,它是由一个C++函数调用并接收结构为ref,处理程序会是这样的:

private void _handler(ref MyStruct p){} 

现在阿比说我不应该保持对结构的引用,因为内存可能被回收并用于进一步的调用,但是由于非托管结构被复制到托管内存中,并且在将其分配给局部变量后,它将被再次复制(因为它是结构体!)如果非托管内存被释放或重写,我应该不会有任何问题。然而,如果结构包含一个IntPtr变量,我可能会保存一个指针的副本,但不保存它所指向的数据,所以如果我尝试从我保存的结构中访问IntPtr数据,我可能会遇到一些内存故障,这是正确?

最后一个问题是,对托管内存中的结构进行更改也会影响非托管内存结构,因为它通过ref传递,并且隐含地表示IN/OUT,所以在事件处理函数调用结束后,更改在托管内存中也会在非托管内存中创建?

回答

0

你的理解基本上是正确的。拿掉blittabiltiy的细节,根本问题是传递给你的回调内存的所有权之一。通常,回调的调用者与回调本身之间的契约是内存由调用者拥有,并且仅在回调本身的持续时间内有效。 (其他合同也是可能的 - 例如,调用者可以将内存所有权转移给回调函数,在这种情况下,回调函数负责在完成时释放内存)。

在您参考的文档中,API告诉您不要保留对内存的引用,这意味着您很可能在调用者拥有该结构的标准情况下。这意味着结构的内容和该结构指向的所有内容在调用回调期间应该是有效的,但在回调完成后不能依赖。

将结构复制到本地不会有问题,因为在回调完成之前不应该释放本机内存,因此在执行回调期间保留尽可能多的副本应该没问题。如果将结构的副本保存到某个位置(如静态字段),则会产生问题,该位置在您的回调返回后将保持活动状态。

由于该结构隐式地进出,因此您对其进行的修改也会在调用的本机端反映出来。无论结构中是否包含可粘连数据,情况都是如此。如果结构包含非blittable数据,那么CLR会在您的代码完成时将对结构的更改整理回本机副本。

请注意,仅仅因为您可以修改本地结构并不意味着API合同期望您这样做。通常,API签名包含指向C++结构的指针,以避免在进行调用时复制整个结构,而不是修改结构。

+0

关于输入/输出的东西,在整个程序执行过程中,一侧是否反映在另一侧?还是只在本地电话/退货? 此外,如果我将结构保存到一个静态变量,并在回调完成后保留它,会有什么问题?如果我实际上保留从本地代码发送给我的结构的COPY(不包括那些IntPtr变量) – user1777914