Friday, November 12, 2010

GDI+ Leak on .Net Framework

I encountered the following error on many machines for our clients using Windows XP with our .Net application:

System.OutOfMemoryException: Screen-compatible bitmap cannot be created. the screen bitmap format cannot be determined.

After long search and investigation, I figured out that this error is related to GDI+ leak with .Net Framework.
Background:
The GDI (Graphic Device Interface) limits the number of object per processor by 10000 object, if your application reach this number you will encounter such situation.
you can find the limit value in the registry:

\HKEY_LOCAL_MACHINE
\SOFTWARE
\Microsoft
\NT
\CurrentVersion
\Windows
ProcessHandleQuota:REG_DWORD
Sure it is not a good idea to increase this limit, but to solve the problem and the leak.
Finding and solving Memory leak in .net is very easy, actually you don't need so, and you can use the GC, but the GDI leak is another story, you can't COLLECT the GDI leak.
Example of Leaky! code with NotifyIcon in sysTray:
In this code you have to change your NotifyIcon based on your application status (error, checking, online ...)

NotifyIcon1.Icon = System.Drawing.Icon.FromHandle(New Bitmap((ImageList1.Images(0))).GetHicon())
... Code 1
... Test 2
NotifyIcon1.Icon = System.Drawing.Icon.FromHandle(New Bitmap((ImageList1.Images(1))).GetHicon())
...
...
NotifyIcon1.Icon = System.Drawing.Icon.FromHandle(New Bitmap((ImageList1.Images(2))).GetHicon())
...
Every time you assign an icon to your notifyIcon the GDI+ object increased till reach the limit and Voila!
Setting the objects to Nothing or dispose the object dose not solve the problem, the best and optimal solution is the following:
Read all your icons on application startup or form load into List Of Icons, then assign it to your NotifyIcon1,

Dim ListIcons As New List(Of Icon)
Sub Form_Load()

ListIcons.Add(System.Drawing.Icon.FromHandle(New Bitmap((ImgListLogos.Images(0))).GetHicon()))

ListIcons.Add(System.Drawing.Icon.FromHandle(New Bitmap((ImgListLogos.Images(1))).GetHicon()))

ListIcons.Add(System.Drawing.Icon.FromHandle(New Bitmap((ImgListLogos.Images(2))).GetHicon()))

ListIcons.Add(System.Drawing.Icon.FromHandle(New Bitmap((ImgListLogos.Images(3))).GetHicon()))

End

Now change your code to:

NotifyIcon1.Icon = ListIcons(0)
... Code1
... Test
... Foo
NotifyIcon1.Icon = ListIcons(3)
...
This workaround will solve your GDI leak in your application.
To monitor the GDI Leak, go to Task Manager and select View>Cols>GDI Objects

2 comments:

Unknown said...

Thankyou Sameh!
I was going crazy trying to understand this error (which you CAN capture in debug mode).
I have a treeview with just over 10 000 nodes and when trying to change the background color of the tree I get this error. In executable mode the error can be trapped in the Application_UnhandledException event where I can cancel the shutdown and continue without any trouble.

Unknown said...

Welcome T Borg