【问题标题】:Tell if IntPtr points to managed or unmanaged memory判断 IntPtr 是否指向托管或非托管内存
【发布时间】:2014-09-08 11:34:40
【问题描述】:

我在 C# 中使用封装的 C 库,需要将该库中的图像转换为位图并返回,但无需复制像素缓冲区。

转换为位图很简单:

Bitmap WrapAsBitmap(CImage image)
{
    return new Bitmap(image.Width, image.Height, image.BytesPerLine, PixelFormat.Format24bppRgb, image.Data);
}

只需将原始像素缓冲区 (image.Data) 传递给 Bitmap 构造函数,就不需要复制。相反,我需要调用LockBitsUnlockBits 来访问原始像素缓冲区的IntPtr

CImage WrapAsCImage(Bitmap bitmap)
{
    BitmapData data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
    var image = new CImage(bitmap.Width, bitmap.Height, data.Stride * 3, data.Scan0);
    bitmap.UnlockBits(data);
    return image;
}

现在,如您所见,如果data.Scan0 在托管内存中,这实际上是一种危险的方法,因为在使用新构造的image 之前,这些位已“解锁”。如果bitmap 是从WrapAsBitmap 方法构造的,那么没有问题,因为data.Scan0 指向不会被.NET 移动的非托管内存。因此,我想构建一个安全检查来检查data.Scan0 指针的位置(托管/非托管)。

我想到的另一个解决方案是从Bitmap 类继承并跟踪原始指针image.Data 以及Bitmap,但是Bitmap 类是密封的,所以我出局了运气。

简而言之:任何想法如何检测IntPtr 的位置(托管/非托管)?

【问题讨论】:

  • 这里的CImage 是什么?
  • 一个包装类,用于包装来自 C 库的图像。此类的唯一相关成员显示在 WrapAsBitmap 方法中
  • 有可能(我不知道),你这里还有一个问题:CImage -> WrapAsBitmap -> WrapAsCImage -> CImage,现在你有两个 CImage 实例指向同一个内存。也许您需要在 WrapAsCImage 中复制内存?
  • @AlexFarber 我知道,但这不是问题
  • 愚蠢的建议:使用 Image.Tag 属性怎么样?不是通用的,但如果使用得当,可能会起作用。

标签: c# .net interop intptr


【解决方案1】:

不存在 IntPtr 永远指向托管内存的情况。当然不是当图像数据是由 C 代码生成的时候。也不是当它由位图生成时,GDI+ 是非托管代码。你可以自己推理,你只能通过固定它来获得托管内存的 IntPtr。 GCHandle.AddrOfPinnedObject()。防止垃圾收集器在压缩堆时使指针无效的硬性要求。您在任何地方都没有看到有记录的需要固定。

实施测试没有意义。

确实有严格的要求来保持 IntPtr 在 Bitmap 包装器的生命周期内有效。直到您调用它的 Dispose() 方法为止。除了将 Bitmap 对象封装在一个实现 IDisposable 本身的类中之外,您不能使这个自动化。不这样做会导致非常丑陋的错误行为,只有在幸运时才会收到 AccessViolationException。带有随机像素内容的损坏位图是正常的故障模式。

如果您无法实现该保证,那么您应该这样做并改为复制位图数据。实现起来不难,就是不好看。

【讨论】:

    【解决方案2】:

    Bitmap 类实际上只是 GDI+ 位图对象的托管包装器。由于该对象不会对暴露给用户代码的固定缓冲区进行操作,而且该缓冲区也不能保证采用特定格式,因此您将无法执行您所描述的操作。

    换句话说,在不限制问题空间的情况下,肯定会有需要缓冲区复制的情况。

    【讨论】:

    • 嗯,我不这么认为,请参阅constructor 以及备注:调用者负责分配和释放由 scan0 参数指定的内存块。但是,在释放相关的Bitmap之前,不应释放内存。
    • @WouterHuysentruit 它使用的底层 GDI+ 方法的文档建议复制缓冲区:msdn.microsoft.com/en-us/library/ms536315.aspx
    猜你喜欢
    • 1970-01-01
    • 2014-04-22
    • 1970-01-01
    • 2013-03-14
    • 1970-01-01
    • 1970-01-01
    • 2011-05-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多