09 May 2012

WinForms OutOfMemoryException when disabling a menu item

One of my customers sent me an error report pretty strange. Mostly because the same application is used by hundreds of users and none of them have experienced this problem.

This is the full stack trace:

    System.OutOfMemoryException: Out of memory.
        at System.Drawing.Graphics.CheckErrorStatus(Int32 status)
        at System.Drawing.Graphics.DrawImage(Image image, Rectangle destRect, Int32 srcX, Int32 srcY, Int32 srcWidth, Int32 srcHeight, GraphicsUnit srcUnit, ImageAttributes imageAttrs, DrawImageAbort callback, IntPtr callbackData)
        at System.Drawing.Graphics.DrawImage(Image image, Rectangle destRect, Int32 srcX, Int32 srcY, Int32 srcWidth, Int32 srcHeight, GraphicsUnit srcUnit, ImageAttributes imageAttr, DrawImageAbort callback)
        at System.Drawing.Graphics.DrawImage(Image image, Rectangle destRect, Int32 srcX, Int32 srcY, Int32 srcWidth, Int32 srcHeight, GraphicsUnit srcUnit, ImageAttributes imageAttr)
        at System.Windows.Forms.ToolStripRenderer.CreateDisabledImage(Image normalImage)
        at System.Windows.Forms.ToolStripRenderer.OnRenderItemImage(ToolStripItemImageRenderEventArgs e)
        at System.Windows.Forms.ToolStripProfessionalRenderer.OnRenderItemImage(ToolStripItemImageRenderEventArgs e)
        at System.Windows.Forms.ToolStripRenderer.DrawItemImage(ToolStripItemImageRenderEventArgs e)
        at System.Windows.Forms.ToolStripMenuItem.OnPaint(PaintEventArgs e)
    at System.Windows.Forms.ToolStripItem.FireEvent(EventArgs e, ToolStripItemEventType met)
    at System.Windows.Forms.ToolStrip.OnPaint(PaintEventArgs e)
        at System.Windows.Forms.Control.PaintWithErrorHandling(PaintEventArgs e, Int16 layer, Boolean disposeEventArgs)
        at System.Windows.Forms.Control.WmPaint(Message& m)
        at System.Windows.Forms.Control.WndProc(Message& m)
        at System.Windows.Forms.ScrollableControl.WndProc(Message& m)
        at System.Windows.Forms.ToolStrip.WndProc(Message& m)
    at System.Windows.Forms.ToolStripDropDown.WndProc(Message& m)
        at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
        at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
        at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

  

This exception is thrown when trying to open a context menu with disabled items.

As usual, getting a repro was not easy. This was happening only on some computers of this client. They were Windows XP SP2 computers, so I launched a VM and finally got a repro.

After some debugging and searching on the interwebs, I discovered that, for some reason (a bug in a .NET library!?), if you assign an image to a menu item that comes from a assembly resource and then you disable that menu, the exception is thrown. I’ve been unable to reproduce the problem in Windows 7 or Windows Vista, but it happens on Windows XP.

Anyway, some post in a forum point out the following solution, which consists in making a copy of the image and assign that copy instead.

    Bitmap bmp = new Bitmap(imageFromAssemblyResource);
    mnuItem = new ToolStripMenuItem("My menu", bmp);

It didn’t work for me. I also tried to clone the image:

    mnuItem = new ToolStripMenuItem("My menu", (Image)imageFromAssemblyResource.Clone());

No luck. Finally I was able to workaround the bug using this code:

    Bitmap bmp = new Bitmap(imageFromAssemblyResource.Width, imageFromAssemblyResource.Height);
    using(Graphics g = Graphics.FromImage(bmp))
    {
        g.DrawImage(imageFromAssemblyResource, 0, 0);
    }

    mnuItem = new ToolStripMenuItem("My menu", bmp);

It’s ugly but it worked. If you know other workaround or fix, please let me know.