c# - GCHandle.FromIntPointer 没有按预期工作

标签 c# mono clr .net-4.5

这是一个非常简单(完整)的程序,用于练习 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);
        }
    }
}

请注意,此程序本质上是对 CLR via C# (4e) 上用英语描述的程序的音译。在第 547 页。但是,运行它会导致非托管异常,例如:

附加信息:运行时遇到 fatal error 。错误地址位于线程 0x21bc 上的 0x210bc39b。错误代码是 0xc0000005。此错误可能是 CLR 中的错​​误,或者是用户代码的不安全或不可验证部分中的错误。此错误的常见来源包括 COM 互操作或 PInvoke 的用户编码错误,这可能会损坏堆栈。

认为这可能是 .NET 4.5 中的错误,并且由于我没有发现任何明显的错误,所以我在 Linux 上的 Mono (v2.10.8.1) 中尝试了完全相同的程序。我得到了稍微多一些但仍然令人费解的异常 GCHandle value belongs to a different domain.

据我所知,handle 确实与我调用 GCHandle.FromIntPtr 的代码属于同一个 AppDomain。但事实上我在这两个实现中都看到了异常,这让我怀疑我遗漏了一些重要的细节。这是怎么回事?

最佳答案

您的心智模型有误。 FromIntPtr() 只能转换回您从 ToIntPtr() 获得的值。它们是方便的方法,特别方便在非托管代码中存储对托管对象的引用(并使其保持事件状态)。 gcroot<> template class依赖于它,用于 C++ 项目。这很方便,因为非托管代码只需要存储指针。

底层值,实际指针,称为“句柄”,但它实际上是指向垃圾收集器维护的表的指针。除了垃圾收集器找到的对象之外,该表还创建了对对象的额外 引用。从本质上讲,即使程序不再具有对该对象的有效引用,也允许托管对象继续存在。

GCHandle.AddrOfPinnedObject() 返回一个完全不同的指针,它指向实际的托管对象,而不是“句柄”。 “属于不同的域”异常消息是可以理解的,因为我提到的表与 AppDomain 相关联。

.NET 4.5 中的崩溃很像是一个错误。它确实使用名为 MarshalNative::GCHandleInternalCheckDomain() 的内部 CLR 函数执行测试。 CLR 的 v2 版本引发 ArgumentException,消息文本为“无法跨 AppDomains 传递 GCHandle。”。但是 v4 版本在此方法中崩溃,进而生成 ExecutionEngineException。这看起来不是是故意的。

反馈报告归档于 connect.microsoft.com

关于c# - GCHandle.FromIntPointer 没有按预期工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17331987/

相关文章:

c# - 获取 FileInfo 本身?

c# - MonoDroid SurfaceView

c# - Mono - XDocument.Load 因 LoadOptions.PreserveWhitespace 而失败

c# - Xamarin Forms 应用入门

c# - 在 C# 中将 bool 表达式转换为 char

c# - 获取对象处置/销毁的通知

c# - WPF 从用户控件调用父方法

c# - 最快读取二进制文件读写

c++ - 如何制作托管 (clr) 多线程 C++ .dll?

c# - 是否建议为每个客户端请求创建线程?