none
代码中的 IntPtr 需要手动释放吗,如何释放 RRS feed

  • 问题

  • 我有一段代码,运行正常,代码内调用了 gdiplus.dll 的函数,看到 MSDN 上关于此函数的介绍,有一段 Remark 说明不用时要释放掉某个指针对象。在 C# 里实验,Marshal.FreeCoTaskMem 或是 Marshal.FreeHGlobal 均会报错,网上查找资料也有人说不用手动释放,不明就里,来论坛请教高手,希望不吝赐教。

    先贴出代码,并用注释说明

            public static Bitmap CreateNewWithEffect(this Bitmap image, ref Rectangle Rect, float Radius = 10, bool ExpandEdge = false)
            {
                // 新图像
                Bitmap newImage = new Bitmap(image);
                
                int Result;
                IntPtr BlurEffect;
                BlurParameters BlurPara;
                if ((Radius < 0) || (Radius > 255))
                {
                    throw new ArgumentOutOfRangeException("Radius 参数错误,半径必须在 [0,255] 范围内");
                }
                BlurPara.Radius = Radius;
                BlurPara.ExpandEdges = ExpandEdge;
                Result = GdipCreateEffect(BlurEffectGuid, out BlurEffect);
    
                if (Result == 0)
                {
                    IntPtr Handle = Marshal.AllocHGlobal(Marshal.SizeOf(BlurPara));
                    Marshal.StructureToPtr(BlurPara, Handle, true);
                    GdipSetEffectParameters(BlurEffect, Handle, (uint)Marshal.SizeOf(BlurPara));
    
                    // 准备参数
                    IntPtr scrImagePointer = image.NativeHandle();  // 原图像的句柄
                    Rectangle newImageRect = new Rectangle(Rect.Location, Rect.Size); // 创建一个和要处理的范围同样尺寸的 Rectangle
                    // 此变量需不需要手动进行释放
                    IntPtr newImagePointer = IntPtr.Zero;
    
                    //GdipBitmapApplyEffect(image.NativeHandle(), BlurEffect, ref Rect, false, IntPtr.Zero, 0);
                    // 使用GdipBitmapCreateApplyEffect函数可以不改变原始的图像,而把模糊的结果写入到一个新的图像中
                    int ok = GdipBitmapCreateApplyEffect(ref scrImagePointer, 1, BlurEffect, ref Rect,ref newImageRect,out newImagePointer, false, IntPtr.Zero, 0);
    
                    if (ok == 0) // 成功
                    {
                        // 执行后,newImagePointer 应不为 IntPtr.Zero
                        if (newImagePointer != IntPtr.Zero)
                        {
                            newImage = newImagePointer.NativeBitmapPtrToBitmap();
                        }
                    }
    
                    // newImagePointer 和 scrImagePointer 两个 IntPtr 需不需要手动释放?
                    GdipDeleteEffect(BlurEffect);
                    Marshal.FreeHGlobal(Handle);
                }
                else
                {
                    throw new ExternalException("不支持的GDI+版本,必须为GDI+1.1及以上版本,且操作系统要求为Win Vista及之后版本.");
                }
                return newImage;
            }

    问题:

    1. 代码中两个 IntPtr newImagePointer 和 scrImagePointer 需不需要显式手动的释放?

    2. 如何释放?

    3. 释放后是否如同 Object.Dispose 一样,此对象就无法继续调用了?

    谢谢


    不重要的其实最重要

    2014年4月25日 5:31

答案

  • 不需要释放,因为scrImagePointer 和newImagePointer 分别是image和newImage对象里面的指针(就是说image和newImage对象的内容实际上就是这两个指针指向的内存的内容),在image对象和newImage对象dispose的时候会自动释放的。

    如果你手动释放了反而会出错。

    如果要释放也不是用Marshal.FreeCoTaskMem 或是 Marshal.FreeHGlobal ,而是要用gdiplus.dll 里面的GdipDisposeImage函数来释放 

    2014年4月25日 6:37
  • 非常感谢 lapheal 的帮助

    追问一句,是不是可以大体理解成,在 CLR 管辖范围内的对象,即使对某个对象声明了个 IntPtr 使用,也不必手动去释放,只要这个对象 Dispose 了,基本就不会产生问题。我的代码里的“Marshal.FreeHGlobal(Handle)”中的 handle 需要手动释放,因为是由 BlurPara 而来,而 BlurPara 是 PInvoke 调用 CLR 管辖范围以外的 API 产生的对象,不属于 CLR 管的,所以需要主动释放。


    不重要的其实最重要

    这个问题得具体情况具体分析,在这个例子里面,scrImagePointer 和newImagePointer并不是对image声明了IntPtr,而是从image.NativeHandle()方法里面获取的,返回的就是image对象内部的图像指针,而这个图像指针是会在image对象dispose的时候释放的。“对某个对象声明了个 IntPtr 使用”这个情况很多,得看这个IntPtr是否这个对象内部管理的IntPtr,如果是的话就不需要手动释放。代码里面的handle需要释放的原因是其所指向的内存是你用Marshal手动申请的(用AllocHGlobal),对于这种调用AllocHGlobal申请的内存,如果不再使用了就要释放。简单来说,这个东西是你创建出来的你就得负责回收,如果是你从别处那里借来用的,一般情况下你不需要负责回收。
    2014年4月27日 3:46

全部回复

  • 补充:

    MSDN 原文地址是:http://msdn.microsoft.com/en-us/library/windows/desktop/ms536320(v=vs.85).aspx


    不重要的其实最重要

    2014年4月25日 5:32
  • 不需要释放,因为scrImagePointer 和newImagePointer 分别是image和newImage对象里面的指针(就是说image和newImage对象的内容实际上就是这两个指针指向的内存的内容),在image对象和newImage对象dispose的时候会自动释放的。

    如果你手动释放了反而会出错。

    如果要释放也不是用Marshal.FreeCoTaskMem 或是 Marshal.FreeHGlobal ,而是要用gdiplus.dll 里面的GdipDisposeImage函数来释放 

    2014年4月25日 6:37
  • 非常感谢 lapheal 的帮助

    追问一句,是不是可以大体理解成,在 CLR 管辖范围内的对象,即使对某个对象声明了个 IntPtr 使用,也不必手动去释放,只要这个对象 Dispose 了,基本就不会产生问题。我的代码里的“Marshal.FreeHGlobal(Handle)”中的 handle 需要手动释放,因为是由 BlurPara 而来,而 BlurPara 是 PInvoke 调用 CLR 管辖范围以外的 API 产生的对象,不属于 CLR 管的,所以需要主动释放。


    不重要的其实最重要

    2014年4月26日 17:09
  • 非常感谢 lapheal 的帮助

    追问一句,是不是可以大体理解成,在 CLR 管辖范围内的对象,即使对某个对象声明了个 IntPtr 使用,也不必手动去释放,只要这个对象 Dispose 了,基本就不会产生问题。我的代码里的“Marshal.FreeHGlobal(Handle)”中的 handle 需要手动释放,因为是由 BlurPara 而来,而 BlurPara 是 PInvoke 调用 CLR 管辖范围以外的 API 产生的对象,不属于 CLR 管的,所以需要主动释放。


    不重要的其实最重要

    这个问题得具体情况具体分析,在这个例子里面,scrImagePointer 和newImagePointer并不是对image声明了IntPtr,而是从image.NativeHandle()方法里面获取的,返回的就是image对象内部的图像指针,而这个图像指针是会在image对象dispose的时候释放的。“对某个对象声明了个 IntPtr 使用”这个情况很多,得看这个IntPtr是否这个对象内部管理的IntPtr,如果是的话就不需要手动释放。代码里面的handle需要释放的原因是其所指向的内存是你用Marshal手动申请的(用AllocHGlobal),对于这种调用AllocHGlobal申请的内存,如果不再使用了就要释放。简单来说,这个东西是你创建出来的你就得负责回收,如果是你从别处那里借来用的,一般情况下你不需要负责回收。
    2014年4月27日 3:46
  • 感谢 lapheal

    不重要的其实最重要

    2014年4月27日 13:38