绘制光标〜3322次时的通用GDI +异常

时间:2012-05-11 19:29:26

标签: c# .net bitmap cursor gdi+

问题:在循环中使用此代码3322次(使用底部方法1246次)后,GetHIcon()会抛出一个通用的GDI +异常。

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

我正在尝试做什么:从循环中的位图绘制新光标以执行简单的对焦动画。

我已经检查了什么:我确保所有位图和图形都被处理并监视内存泄漏以确保。还确保没有其他过程有明显的泄漏。试图确保正确使用位图的替代方法和方法。

谷歌告诉我的事情: GDI +似乎有一个错误,没有人提供解决方案。一个人试图创建自己的Bitmap to Icon转换器,但它不够灵活,不能做非通用的图像尺寸。

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 
}

2 个答案:

答案 0 :(得分:6)

这个问题肯定看起来像是一个内存泄漏给我,因为它的症状。它运行良好一段时间,然后爆炸。

事实证明,你尝试的第二种方法是严重泄漏GDI对象。当您致电GetIconInfo填充ICONINFO结构时,它实际上会创建与图标/光标hbmMaskhbmColor对应的两个位图。当您使用完毕后,必须调用DeleteObject才能删除它们,否则您将泄露它们。根据{{​​3}}的备注部分:

  

GetIconInfohbmMask的{​​{1}}和hbmColor成员创建位图。调用应用程序必须管理这些位图,并在不再需要时删除它们。

这不是你在这里唯一的泄漏。使用 方法,我发现至少还有两个漏洞:

  • ICONINFO方法要求您在使用完图标后调用Bitmap.GetHicon。你还没有这样做,所以你每次都会泄漏那个图标。

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

当你修复所有这些泄漏时,执行的次数会增加一倍以上,这使得我甚至无法看到同心圆出现了。但是我仍然无法让它在没有崩溃的情况下无限期地运行,所以在那里肯定会有一些我没有找到的更多泄漏。

Dispose这样的东西也会引起红旗,让我脑子里响起警告。

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

答案 1 :(得分: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;
 }