Generic GDI+ Exception while drawing Cursor ~3322 times
.net, bitmap, c#, cursor, gdi+
Solution
This problem definitely looks like a memory leak to me, given its symptoms. It runs fine for a while, then blows up.
And as it turns out, the second method you tried is leaking GDI objects badly. When you call `GetIconInfo` to fill an `ICONINFO` structure, it actually creates two bitmaps corresponding to the icon/cursor, `hbmMask` and `hbmColor`. You must call `DeleteObject` to delete these when you are finished using them, otherwise you'll leak them. According to the Remarks section of 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.
That's not the only leak you've got here, either. With either method, I see at least two additional leaks:
The `Bitmap.GetHicon` method requires you to call `DestroyIcon` when you get finished using the icon. You haven't done that either, so you're leaking that icon each time.
You're not disposing the `Bitmap`, `Graphics`, `GraphicsPath`, and `Cursor` objects that you're creating inside the tight `while` loop in the `DrawRingAroundCursor` until the very end, which means all those temporary objects created for each iteration are leaked. (I recommend wrapping the creation of GDI+ objects in a `using` statement, rather than trying to remember to call their `Dispose` methods.)
When you fix all of those leaks, the execution more than doubles, making it so that I can't even see the concentric circles show up anymore. But I still can't get it to run indefinitely without crashing, so there's bound to be some more leaks in there that I just haven't found.
Stuff like `Thread.Sleep` also raises red flags and makes loud warning bells go off in my head.
Perhaps here is a good time to say that I strongly urge you to try a different design? Creating all of these objects, even if you're properly managing their lifetimes, is going to be relatively expensive and seems rather unnecessary. Moreover, as soon as the user moves the cursor outside of your application's window and over some other object, Windows will send a new `WM_SETCURSOR` message to the new hovered window and it will change the cursor to something else entirely. It's all too easy to make this effect "go away" with minimal effort.
Problem
The Problem: After using this code in a loop 3322 times (1246 times using the bottom method), a generic GDI+ exception is thrown at GetHIcon(). Sample Project: http://dl.dropbox.com/u/18919663/TestGDICursorDrawing.zip What I'm trying to do: Draw a new cursor from a bitmap in a loop to do a simple focusing animation. What I've already checked: I made sure that all bitmaps and graphics are being disposed and monitored memory leaks to make sure. Also made sure no other process had a visible leak. Tried alternative methods and ways to ensure Bitmaps were being used correctly. What Google has told me: There seems to be a bug in GDI+ and nobody has offered a solution. One person tried to create his own Bitmap to Icon converter, but it's not flexible enough to do non-generic image sizes. ``` 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; } ``` Other Method I tried: ``` 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 } ```