c# - 绘制光标时出现通用 GDI+ 异常 ~3322 次

标签 c# .net bitmap cursor gdi+

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

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

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

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

Google 告诉我的内容: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 
}

最佳答案

鉴于其症状,这个问题绝对对我来说看起来像是内存泄漏。它运行了一段时间很好,然后爆炸了。

事实证明,您尝试的第二种方法严重泄漏了 GDI 对象。当你调用 GetIconInfo 填充一个 ICONINFO 结构时,它实际上创建了两个对应于图标/光标的位图,hbmMaskhbmColor。您必须在使用完它们后调用DeleteObject 来删除它们,否则会泄露它们。根据the documentation的备注部分:

GetIconInfo creates bitmaps for the hbmMask and hbmColor members of ICONINFO. The calling application must manage these bitmaps and delete them when they are no longer necessary.

这也不是您在这里遇到的唯一漏洞。使用任一种方法,我至少看到了两个额外的泄漏:

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

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

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

Thread.Sleep 之类的东西也会发出危险信号,并在我的脑海中敲响响亮的警钟。

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

关于c# - 绘制光标时出现通用 GDI+ 异常 ~3322 次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10557474/

相关文章:

.net - ASP.NET 中的 REST WCF 服务和 session

c# - 使用流利的 NHibernate 映射一个只读的 sql View

c# - 安装 .net Framework 4.7.2 时,4.5.1 应用程序中的 TLS1.2 协商失败

android - 从 Android 相机 Surface 获取 RGB 值

javascript - 有没有办法使用 Javascript 访问图像的位图数据?

c# - 在后面的代码中获取和设置绑定(bind)转换器

c# - C++ API 自动转换为 C#?

c# - 英雄卡图像附件未加载

image - 如何在Firemonkey中拉伸(stretch)图像?

c# - 如何将 Datetime 中英文文本中的月份名称转换为 C# 中的阿拉伯文本?