这是一个非常简单(完整)的程序,用于练习 GCHandle.FromIntPointer 的使用:
using System;
using System.Runtime.InteropServices;
namespace GCHandleBugTest
{
class Program
{
static void Main(string[] args)
{
int[] arr = new int[10];
GCHandle handle = GCHandle.Alloc(arr, GCHandleType.Pinned);
IntPtr pointer = handle.AddrOfPinnedObject();
GCHandle handle2 = GCHandle.FromIntPtr(pointer);
}
}
}
请注意,该程序本质上是英文描述的程序的音译通过 C# 的 CLR (4e) http://shop.oreilly.com/product/0790145353665.do第 547 页。但是,运行它会导致非托管异常,例如:
Additional Information: The runtime has encountered a fatal error. The address of the error was at 0x210bc39b, on thread 0x21bc. The error code is 0xc0000005. This error may be a bug in the CLR or in the unsafe or non-verifiable portions of user code. Common sources of this bug include user marshaling errors for COM-interop or PInvoke, which may corrupt the stack.
我认为这可能是 .NET 4.5 中的一个错误,并且由于我没有看到任何明显错误,因此我在 Linux 上的 Mono (v2.10.8.1) 中尝试了完全相同的程序。我得到了稍微多一点的信息,但仍然令人费解的异常GCHandle value belongs to a different domain.
据我所知,handle
确实与我调用的代码属于同一个 AppDomainGCHandle.FromIntPtr
。但事实上,我在两个实现中都看到了异常,这让我怀疑我遗漏了一些重要的细节。这里发生了什么?
你的思维模式是错误的。 FromIntPtr() 只能转换回从 ToIntPtr() 获得的值。它们是方便的方法,特别适合在非托管代码中存储对托管对象的引用(并使其保持活动状态)。这gcroot 模板类 http://msdn.microsoft.com/en-us/library/481fa11f.aspx依赖它,在C++项目中使用。这很方便,因为非托管代码只需存储指针。
底层值(实际指针)称为“句柄”,但它实际上是指向垃圾收集器维护的表的指针。表创建extra除了垃圾收集器找到的对象之外,还包括对对象的引用。实质上允许托管对象生存,即使程序不再拥有对该对象的有效引用。
GCHandle.AddrOfPinnedObject() 返回一个完全不同的指针,它指向实际的托管对象,而不是“句柄”。 “属于不同的域”异常消息是可以理解的,因为我提到的表与 AppDomain 关联。
.NET 4.5 中的崩溃看起来非常像一个错误。它确实使用名为 MarshalNative::GCHandleInternalCheckDomain() 的内部 CLR 函数执行测试。 CLR 的 v2 版本引发 ArgumentException 并显示消息文本“无法跨 AppDomains 传递 GCHandle。”。但 v4 版本在此方法内崩溃,进而生成 ExecutionEngineException。这确实not看起来是故意的。
反馈报告提交于连接.microsoft.com https://connect.microsoft.com/VisualStudio/feedback/details/791096/gchandle-fromintptr-argument-value-check-does-not-work
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)