判断IntPtr是指向托管内存还是非托管内存

时间:2014-09-08 11:34:41

标签: c# .net interop intptr

我在C#中使用了一个包装的C库,需要将该库中的图像转换为Bitmap并返回,但是没有复制像素缓冲区。

转换为位图非常简单:

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.DataBitmap,但Bitmap类是密封的,所以我运气不好。

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

2 个答案:

答案 0 :(得分:5)

没有任何情况,IntPtr 指向托管内存。当然,当图像数据由C代码生成时。不是由Bitmap生成的,GDI +也是非托管代码。您可以自己解释这一点,您只能通过固定它来获取托管内存的IntPtr。 GCHandle.AddrOfPinnedObject()。很难要求在压缩堆时防止垃圾收集器使指针无效。你没有看到记录在案的需要。

实施测试毫无意义。

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

如果您无法实现该保证,则执行此操作并复制位图数据。它并不难实现,只是不漂亮。

答案 1 :(得分:0)

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

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