3

问题:在循环中使用此代码 3322 次(使用底部方法 1246 次)后,在 GetHIcon() 处引发通用 GDI+ 异常。

示例项目: http ://dl.dropbox.com/u/18919663/TestGDICursorDrawing.zip

我正在尝试做的事情:从循环中的位图中绘制一个新光标来做一个简单的聚焦动画。

我已经检查过的内容:我确保正在处理所有位图和图形并监控内存泄漏以确保。还要确保没有其他进程有可见的泄漏。尝试了其他方法和方法来确保正确使用位图。

谷歌告诉我的: GDI+ 中似乎有一个错误,没有人提供解决方案。一个人尝试创建自己的位图到图标转换器,但它不够灵活,无法处理非通用图像尺寸。

public static Cursor CreateCursor(Bitmap bmp, int xHotSpot, int yHotSpot)
{
    //Shows me exactly when the error occurs.
    counter++;
    Console.WriteLine(counter + " GetHicon() calls");

    //GetHicon() is the trouble maker. 
    var newCur = new Cursor(bmp.GetHicon());
    bmp.Dispose();
    bmp = null;

    return newCur;
}

我试过的其他方法:

public static Cursor CreateCursor(Bitmap bmp, int xHotSpot, int yHotSpot)
{
    //Tried this method too, but this method results in an error with even fewer loops.
    Bitmap newBitmap = new Bitmap(bmp);
    // was told to try to make a new bitmap and dispose of the last to ensure that it wasn't locked or being used somewhere. 
    bmp.Dispose();
    bmp = null;
    //error occurs here. 
    IntPtr ptr = newBitmap.GetHicon();
    ICONINFO tmp = new ICONINFO();
    GetIconInfo(ptr, ref tmp);
    tmp.xHotspot = xHotSpot;
    tmp.yHotspot = yHotSpot;
    tmp.fIcon = false;
    ptr = CreateIconIndirect(ref tmp);

    newBitmap.Dispose();
    newBitmap = null;

    return new Cursor(ptr);
}


[DllImport("user32.dll", EntryPoint = "GetIconInfo")]
public static extern bool GetIconInfo(IntPtr hIcon, ref ICONINFO piconinfo);

[DllImport("user32.dll")]
public static extern IntPtr CreateIconIndirect(ref ICONINFO icon);

[StructLayout(LayoutKind.Sequential)]
public struct ICONINFO
{
    public bool fIcon;         // Specifies whether this structure defines an icon or a cursor. A value of TRUE specifies 
    public Int32 xHotspot;     // Specifies the x-coordinate of a cursor's hot spot. If this structure defines an icon, the hot 
    public Int32 yHotspot;     // Specifies the y-coordinate of the cursor's hot spot. If this structure defines an icon, the hot 
    public IntPtr hbmMask;     // (HBITMAP) Specifies the icon bitmask bitmap. If this structure defines a black and white icon, 
    public IntPtr hbmColor;    // (HBITMAP) Handle to the icon color bitmap. This member can be optional if this 
}
4

2 回答 2

6

考虑到它的症状,这个问题在我看来肯定是内存泄漏。它运行了一段时间,然后爆炸。

事实证明,您尝试的第二种方法是严重泄漏 GDI 对象。当您调用GetIconInfo填充ICONINFO结构时,它实际上会创建两个对应于图标/光标的位图,hbmMask以及hbmColor. 使用完后必须调用删除它们,否则会泄漏它们DeleteObject根据文档的备注部分:

GetIconInfohbmMask为 的和的hbmColor成员创建位图ICONINFO。调用应用程序必须管理这些位图并在不再需要时将其删除。

这也不是您在这里遇到的唯一泄漏。无论使用哪种方法,我都会看到至少两个额外的泄漏:

  • Bitmap.GetHicon方法要求您DestroyIcon在使用完图标后调用。您也没有这样做,因此您每次都在泄漏该图标。

  • 直到最后,您都没有在紧循环中处理您在紧密循环中创建的BitmapGraphicsGraphicsPathCursor对象,这意味着为每次迭代创建的所有临时对象都被泄露。(我建议将 GDI+ 对象的创建包装在一个语句中,而不是试图记住调用它们的方法。)whileDrawRingAroundCursorusingDispose

当你修复所有这些漏洞时,执行量增加了一倍以上,以至于我什至看不到同心圆出现了。但是我仍然无法让它无限期地运行而不会崩溃,所以肯定会有更多的漏洞我还没有发现。

像这样的东西Thread.Sleep也会引发危险信号,并让我脑海中响起响亮的警钟。

也许现在是说我强烈建议您尝试不同设计的好时机?创建所有这些对象,即使您正确地管理它们的生命周期,也将相对昂贵并且似乎相当不必要。此外,只要用户将光标移出应用程序窗口并移到其他对象上,Windows 就会向WM_SETCURSOR新悬停的窗口发送一条新消息,并将光标完全更改为其他内容。用最小的努力使这种效果“消失”太容易了。

于 2012-05-11T19:56:59.033 回答
2

请在 GetHicon 之后使用DestroyIcon,以防止内存泄漏

[DllImport("user32.dll", CharSet = CharSet.Auto)]
extern static bool DestroyIcon(IntPtr handle);

MSDN:https ://msdn.microsoft.com/en-us/library/system.drawing.bitmap.gethicon%28v=vs.110%29.aspx

我的代码示例:

 [DllImport("user32.dll", CharSet = CharSet.Auto)]
 extern static bool DestroyIcon(IntPtr handle);
 public static Icon ConvertoToIcon(Bitmap bmp)
 {
     System.IntPtr icH = bmp.GetHicon();
     var toReturn = (Icon)Icon.FromHandle(icH).Clone();
     DestroyIcon(icH);
     return toReturn;
 }
于 2015-07-21T10:47:04.440 回答