Description
Occasionally, Windows Forms application user will get an error message as shown in below snapshot.

Analysis
If you have done win32 development, you will immediately suspect GDI handles. I would like to point out the steps which you can take to troubleshoot and confirm if there is a GDI Leak in a process.
When a user reports this issue which is not reproducible at will, in that case you can ask user to launch task manager and go to view->select columns and check Handles and GDI Handles. Even better, if you can use process explorer.
Things to remember
1. 32 bit Windows OS(XP and above) uses DWORD which is 4 bytes for either user handle or gdi handle. Most significant 16 bits are used for GDI handles so 2^16 = 65536 is a theoretical limit on number of GDI or USER handles a Win32 Process can have.
2. Default value of number of handles configured in a 32 bit operating system are 10k which is configurable in the registry
3. “HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\GDIProcessHandleQuota” has the default value of GDI handles
5. GDI handles are not just bitmaps, but it can be any device context object for example, Font, Pen, DC etc

Exception object: 1a2b14ec
Exception type: System.OutOfMemoryException
Message: Screen-compatible bitmap cannot be created. The screen bitmap format cannot be determined.
SP IP Function
System_Drawing_ni!System.Drawing.BufferedGraphicsContext.bFillBitmapInfo(IntPtr, IntPtr, BITMAPINFO_FLAT ByRef)
…………………………………..
MethodDesc: 7adff018
Method Name: System.Drawing.BufferedGraphicsContext.bFillBitmapInfo(IntPtr, IntPtr, BITMAPINFO_FLAT ByRef)
0:000> !dumpil 7adff018
ilAddr = 7aeb0f64
IL_0000: ldsfld System.IntPtr::Zero
IL_0005: stloc.0
IL_0006: ldc.i4.0
IL_0007: stloc.1
.try
{
IL_0008: ldnull
IL_0009: ldarg.1
IL_000a: newobj System.Runtime.InteropServices.HandleRef::.ctor
IL_000f: ldc.i4.1
IL_0010: ldc.i4.1
IL_0011: call System.Drawing.SafeNativeMethods::CreateCompatibleBitmap
IL_0016: stloc.0
IL_0017: ldloc.0
IL_0018: ldsfld System.IntPtr::Zero
IL_001d: call System.IntPtr::op_Equality
IL_0022: brfalse.s IL_0034
IL_0024: ldstr “GraphicsBufferQueryFail”
IL_0029: call System.Drawing.SR::GetString
IL_002e: newobj System.OutOfMemoryException::.ctor
IL_0033: throw
Below is the finalizequeue stats
0:000> !finalizequeue
SyncBlocks to be cleaned up: 0
MTA Interfaces to be released: 0
STA Interfaces to be released: 0
———————————-
generation 0 has 34 finalizable objects (19826184->1982620c)
generation 1 has 6 finalizable objects (1982616c->19826184)
generation 2 has 84919 finalizable objects (197d3290->1982616c)
Ready for finalization 0 objects (1982620c->1982620c)
………………………………………………………………………………………………………………….
7b225228 9811 156976 System.Windows.Forms.Control+FontHandleWrapper
7b21e930 9816 1649088 System.Windows.Forms.TextBox
……………………………………………………………………………………………………………………
Total 84959 objects
and the call stack for HFONT gdi handle leak is belowBelow is the call stack to set a new Font object for every TextBox Control
System.Windows.Forms.Control.get_FontHandle()
System.Windows.Forms.Control.get_FontHandle()
System.Windows.Forms.Control.SetWindowFont()
System.Windows.Forms.Control.OnHandleCreated
And the root cause for Control not getting disposed is
022d28f8(System.Windows.Forms.ToolStrip)->
022d2644(MyApp.Controls.UserControl)->
022d6ed4(MyApp.MyPanel)->
022d84d0(System.Windows.Forms.ContextMenuStrip)->
021546f4(System.Windows.Forms.TextBox)->
02154608(System.Windows.Forms.PropertyStore)->
a. Form contains ContextMenuStrip member
b. Each of the created Control has a reference to ContextMenuStrip(Control.ContextMenuStrip = Form.ContextMenuStrip)
c. Even after a Control is removed, it still holds reference to ContextMenuStrip which is a member of Main Form
d. Unless Main Form is disposed, Controls will survive GC although Dispose is called on each of the Control on OnControlRemoved Event
e. This issue can be easily resolved by setting Control.ContextMenuStrip = null when a control is removed
