IntPtr - 内存和 GDI 泄漏 [C#]

IntPtr - Memory & GDI leak [C#]

问题: 在截取屏幕截图(循环)时,出现 RAM 和 GDI 泄漏。

private Bitmap GetSS(int ScreenWidth, int ScreenHeight, int ScreenWidthCut, int ScreenHeightCut)
    {
        
        int ScreenLocWidth = Screen.PrimaryScreen.Bounds.Width - ScreenWidth;
        int ScreenLocHeight = Screen.PrimaryScreen.Bounds.Height - ScreenHeight;

        IntPtr dc1 = CreateDC("DISPLAY", null, null, (IntPtr)null);
        //Create the DC of the display
        Graphics g1 = Graphics.FromHdc(dc1);
        //Create a new Graphics object from the handle of a specified device
        Bitmap MyImage = new Bitmap(ScreenWidthCut, ScreenHeightCut, g1);
        //Create a Bitmap object of the same size according to the screen size
        Graphics g2 = Graphics.FromImage(MyImage);

        //Get the handle of the screen
        IntPtr dc3 = g1.GetHdc();
        //Get the handle of the bitmap
        IntPtr dc2 = g2.GetHdc();
        BitBlt(dc2, 0, 0, ScreenWidth, ScreenHeight, dc3, ScreenLocWidth, ScreenLocHeight,
            (int)CopyPixelOperation.SourceCopy | (int)CopyPixelOperation.CaptureBlt);
        g1.ReleaseHdc(dc3);
        //Release the screen handle
        g2.ReleaseHdc(dc2);
        //Release the bitmap handle
        DeleteObject(dc1);
        DeleteObject(dc2);
        DeleteObject(dc3);
        
        return MyImage;
    }

调试给了我这些可能导致泄漏的行。

//Get the handle of the screen
        IntPtr dc3 = g1.GetHdc();
        //Get the handle of the bitmap
        IntPtr dc2 = g2.GetHdc();

我试图通过以下方式释放和删除创建的 objects,但没有任何效果。

g1.ReleaseHdc(dc3);
    //Release the screen handle
    g2.ReleaseHdc(dc2);
    //Release the bitmap handle
    DeleteObject(dc1);
    DeleteObject(dc2);
    DeleteObject(dc3);

我找到了使用 GarbageCollector 的解决方案。这样可行!不再有内存或 GDI 泄漏。我只是打电话

GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);

在我调用“GetSS”之后。 但我想了解为什么手动释放和删除 objects 不起作用,我想尽可能避免使用 GarbageCollector。

编辑:这就是我调用 GetSS 的方式

while (startLoc.x == 0) 
        {
            using (Bitmap imgScene = GetSS(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, Screen.PrimaryScreen.Bounds.Width, (int)(Screen.PrimaryScreen.Bounds.Height * 0.20)))
            {
                //the stuff I do with the image is commented out for testing purposes, this is not causing th leak
            }
            Thread.Sleep(10);
            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
        }

这是为了删除 Object:

[System.Runtime.InteropServices.DllImport("gdi32.dll")]
    public static extern bool DeleteObject(IntPtr hObject);

大家身体健康。

如果强制 GC 解决了问题,则可能是由于某些终结器启动并释放了内存。这暗示它可能是一些没有被处理的一次性物品。 Graphics class 是 IDisposable,因此它们应该在 using 语句中以确保处置。位图似乎在函数外正确处理。

建议CreateDC对应的函数是DeleteDC.

我可能还建议释放 finally-statements 中的所有资源,以确保即使发生某些异常也能释放它们。

您缺少 using 个块,而且 DeleteObject 应该是 DeleteDC,它也应该在 finally.

此外,dc3 不是必需的,因为您在 dc1 中已经有了它。

 private Bitmap GetSS(int ScreenWidth, int ScreenHeight, int ScreenWidthCut, int ScreenHeightCut)
 {
     int ScreenLocWidth = Screen.PrimaryScreen.Bounds.Width - ScreenWidth;
     int ScreenLocHeight = Screen.PrimaryScreen.Bounds.Height - ScreenHeight;

     Bitmap MyImage;
     IntPtr dc1 = IntPtr.Zero;
     IntPtr dc2 = IntPtr.Zero;
     try
     {
         dc1 = CreateDC("DISPLAY", null, null, (IntPtr)null);
         //Create the DC of the display
         //Create a Bitmap object of the same size according to the screen size
         using (Graphics g1 = Graphics.FromHdc(dc1))
         {
             MyImage = new Bitmap(ScreenWidthCut, ScreenHeightCut, g1);
             using (Graphics g2 = Graphics.FromImage(MyImage))
             {
                 //Get the handle of the bitmap
                 dc2 = g2.GetHdc();
                 BitBlt(dc2, 0, 0, ScreenWidth, ScreenHeight, dc1, ScreenLocWidth, ScreenLocHeight,
                     (int)CopyPixelOperation.SourceCopy | (int)CopyPixelOperation.CaptureBlt);
             }
         }
     }
     catch
     {
         MyImage?.Dispose();
         throw;
     }
     finally
     {
         //Release the bitmap handle
         if (dc1 != IntPtr.Zero)
             DeleteObject(dc1);
         if (dc2 != IntPtr.Zero)
             g2.ReleaseHdc(dc2);
     }
     
     return MyImage;
 }

请不要忘记您 return 的图像也必须在某个时候处理掉。