Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

System.Drawing Out of Memory Exception On Main() Method - C#

Tags:

c#

winforms

My program is a CRM, I used Rad Ribbon Bar, so Many Buttons with images, RadGridView (which some columns contain images) and so many other controls which contain images. It's a mdi parent/child program.

Just an Example of RibbonBar

In so many cases while loading a mdi child or working with some grid views the program will hang and give me this error:

OutOfMemoryException occurred in System.Drawing.dll

I tried GC.Collect() on certain parts but no success. For setting images there is no code! for example for setting an image for a button I used its properties in visual studio. I have set All other control images in this way using the properties panel in visual mode.

enter image description here

and These are some designer codes related to drawing:

    btnCustomerList.Image = global::MyApp.Properties.Resources.CustomerList32;

    gridViewCommandColumn1.Image = global::MyApp.Properties.Resources.ViewShop32;

and When The error comes after a while working with the app, it will appear in Program.cs and in the line Application.Run(new MainForm());:

    static void Main()
    {
        AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", AppDomain.CurrentDomain.BaseDirectory + "\\Settings.config");
        bool ok;
        Mutex m = new Mutex(true, WindowsIdentity.GetCurrent().Name.ToString().Split('\\')[1] + "MyApp", out  ok);
        if (ok)
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            // The Error will cause HERE
            Application.Run(new MainForm());

            GC.KeepAlive(m);
        }
        else
            Application.Exit();
    }

MainForm is the mdi parent which contains Ribbon Bar. and this is the Full stack trace:

at System.Drawing.Image.FromHbitmap(IntPtr hbitmap, IntPtr hpalette)
at System.Drawing.Image.FromHbitmap(IntPtr hbitmap)
at System.Drawing.Icon.ToBitmap()
at System.Windows.Forms.ThreadExceptionDialog..ctor(Exception t)
at System.Windows.Forms.Application.ThreadContext.OnThreadException(Exception t)
at System.Windows.Forms.Control.WndProcException(Exception e)
at System.Windows.Forms.Control.ControlNativeWindow.OnThreadException(Exception e)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
at System.Windows.Forms.Application.Run(Form mainForm)
at MyApp.Program.Main() in d:\\MyApp\\Application\\MyApp\\Program.cs:line 36"

UPADTED:

the code for calling mdi-children by clicking on ribbon bar buttons is here:

private void btnCustomerList_Click(object sender, EventArgs e)
{
    OpenForm(new FormCustomerList(), "Customer List");
}

private void btnCustomerRelated_Click(object sender, EventArgs e)
{
    OpenForm(new FormCustomerRelated(), "Customer Related");
}

and Here is OpenForm method:

private void OpenForm(Form formType, string Caption)
{
    foreach (Form nform in Application.OpenForms)
    {
        if (nform.GetType() == formType.GetType())
        {
            nform.Activate();
            return;
        }
    }
    this.MdiChildren.OfType<Form>().ToList().ForEach(x => x.Dispose());
    GC.Collect();

    Form form = formType;
    form.MdiParent = this;
    form.Dock = DockStyle.Fill;
    form.Show();
    this.Text = Caption;
}

and in every mdi child's form constructor, after InitializeComponent(); I wrote GC.Collect(); also. But as told in comments, the GDI objects in task manager will increase and increase till 10000 objects and then application will crash.

UPADTED: THE MOST ISSUE

It seems I have found the part which cause the most GDI objects. In every form there are some controls like textboxes, drop down list etc. I have set some rules for them, for example if user enter a textbox, its back color should be yellow and after leave it should be white again. So there is a main method which I call in form load to literate through all controls and find the target ones and add for example enter and leave events with the defined rules. something Like This:

private void FormCustomerList_Load(object sender, EventArgs e)
{
    ClassCRMControls.AddEventHandler(this);
}  

and inside ClassCRMControls class:

public static void AddEventHandler(Control parent)
{
    foreach (Control c in parent.Controls)
    {
        if (c.GetType() == typeof(RadTextBox))
        {
            c.Enter += new EventHandler(ClassCRMControls.EnterEvent);
            c.Leave += new EventHandler(ClassCRMControls.LeaveEvent);
        }
        else
            AddEventHandler(c);
    }
}

private static void EnterEvent(object sender, EventArgs e)
{
    (sender as RadTextBox).TextBoxElement.TextBoxItem.BackColor = Color.FromArgb(255, 251, 147);
}

private static void LeaveEvent(object sender, EventArgs e)
{
      (sender as RadTextBox).TextBoxElement.TextBoxItem.ResetValue(LightVisualElement.BackColorProperty, ValueResetFlags.Local);
}
like image 303
Inside Man Avatar asked Nov 02 '17 06:11

Inside Man


2 Answers

I have found the source of the problem and it was the custom animated cursor I used for grids and other controls too. I initialize it like this:

this.Cursor = ClassObjects.CreateAnimatedCursor("C:\\aniCur.ani"));

Since I loaded this cursor from the file every time I used it in any way, more and more GDI Objects got created.

So I declared a public static cursor the main() of the respective form like this:

public static Cursor animCur = ClassObjects.CreateAnimatedCursor("C:\\aniCur.ani"));

and then whenever I need to use this cursor I just reference this object public static cursor from the form.

this.Cursor = MainForm.animCur;

That's It :)

How did I find it? I just try to remove (commenting) some codes which I suspected them, then I checked GDI objects in task manager. After some testing it became apparent that the endless loading of new cursor objects was causing the problem.

like image 125
Inside Man Avatar answered Nov 14 '22 17:11

Inside Man


There can be multiple reasons for OutOfMemoryExceptions. I have discussed 6 of them in another question.

In this case, after the comments and the edit, it became clear that GDI issues occur as well. You can detect these issues by showing an additional column in task manager:

GDI objects shown in Task Manager

GDIView is a far better application for GDI Leak analysis, because it also tells you the type of the GDI handle that got lost. It also has absolute and relative counters, so you can see how many of them get lost during a particular action.

GDIView details

The number of GDI handles can be configured in Registry. Do not use that as a permanent solution. Instead, with the additional info from GDIView, find the piece of code that leaks the GDI object.

When you run into the limit of GDI handles, the application typically starts looking bad: things are not painted any more and you get black rectangles in some places. However, this behavior is not necessary. In OP's case, black rectangles were not part of the description.

like image 23
Thomas Weller Avatar answered Nov 14 '22 17:11

Thomas Weller