System.Draw Main()方法的内存异常 - C#

Ins*_*Man 13 c# winforms

我的程序是一个CRM,我使用Rad Ribbon Bar,所以有许多带图像的按钮,RadGridView(有些列包含图像)和许多其他包含图像的控件.这是一个mdi父/子计划.

只是RibbonBar的一个例子

在很多情况下,在加载mdi子项或使用某些网格视图时,程序将挂起并给我这个错误:

OutOfMemoryException occurred in System.Drawing.dll
Run Code Online (Sandbox Code Playgroud)

我试过GC.Collect()某些部分但没有成功.设置图像没有代码!例如,为按钮设置图像我在visual studio中使用了它的属性.我在可视模式下使用属性面板以这种方式设置了所有其他控制图像.

在此输入图像描述

这些是与绘图相关的一些设计师代码:

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

    gridViewCommandColumn1.Image = global::MyApp.Properties.Resources.ViewShop32;
Run Code Online (Sandbox Code Playgroud)

当使用应用程序一段时间后出现错误时,它将出现Program.cs在行中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();
    }
Run Code Online (Sandbox Code Playgroud)

MainForm是包含功能区栏的mdi父级.这是完整的堆栈跟踪:

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"
Run Code Online (Sandbox Code Playgroud)

UPADTED:

mdi-children单击功能区栏按钮进行调用的代码如下:

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");
}
Run Code Online (Sandbox Code Playgroud)

这是OpenForm方法:

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;
}
Run Code Online (Sandbox Code Playgroud)

InitializeComponent();我写完之后,在每个mdi孩子的形式构造函数中GC.Collect();.但正如在评论中所说,GDI objectsin任务管理器将增加并增加直到10000对象,然后应用程序将崩溃.

UPADTED:最重要的问题

我似乎找到了导致最多的部分GDI objects.在每种形式中都有一些控件,如文本框,下拉列表等.我已经为它们设置了一些规则,例如,如果用户输入文本框,其背面颜色应该是黄色,并且在离开后它应该再次为白色.所以有一个主要方法,我在表单加载中调用以通过所有控件进行识别并找到目标控件并添加例如使用定义的规则进入和离开事件.像这样的东西:

private void FormCustomerList_Load(object sender, EventArgs e)
{
    ClassCRMControls.AddEventHandler(this);
}  
Run Code Online (Sandbox Code Playgroud)

和内部ClassCRMControls课程:

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);
}
Run Code Online (Sandbox Code Playgroud)

Ins*_*Man 5

我找到了问题的根源,它也是我用于网格和其他控件的自定义动画光标.我像这样初始化它:

this.Cursor = ClassObjects.CreateAnimatedCursor("C:\\aniCur.ani"));
Run Code Online (Sandbox Code Playgroud)

因为每当我以任何方式使用它时都从文件中加载了这个光标,所以GDI Objects创建了越来越多的光标.

所以我声明了public static cursor相应形式的main(),如下所示:

public static Cursor animCur = ClassObjects.CreateAnimatedCursor("C:\\aniCur.ani"));
Run Code Online (Sandbox Code Playgroud)

然后每当我需要使用这个游标时,我只是public static cursor从表单中引用这个对象.

this.Cursor = MainForm.animCur;
Run Code Online (Sandbox Code Playgroud)

而已 :)

我是怎么找到的?我只是试图删除(评论)我怀疑它们的一些代码,然后我检查GDI objects了任务管理器.经过一些测试后,很明显无休止地加载新的游标对象导致了问题.

  • 这不是一个非常有用的答案.肯定未来这个Q + A的读者将不会有完全相同的问题,并想知道你是如何发现这个句柄泄漏的原因.并考虑正确执行此操作,以便GC*可以*正确地避免泄漏.示例代码[在这里](/sf/answers/301488911/),带有"ownHandle"字段的hack负责处理. (3认同)

Tho*_*ler 5

OutOfMemoryExceptions 可能有多种原因。我在另一个问题中讨论了其中的 6 个。

在这种情况下,经过评论和编辑,很明显 GDI 问题也发生了。您可以通过在任务管理器中显示附加列来检测这些问题:

任务管理器中显示的 GDI 对象

GDIView是一个更好的 GDI 泄漏分析应用程序,因为它还告诉您丢失的 GDI 句柄的类型。它还具有绝对和相对计数器,因此您可以查看在特定操作中丢失了多少个计数器。

GDI查看详情

可以在 Registry 中配置GDI 句柄的数量。不要将其用作永久解决方案。相反,使用来自 GDIView 的附加信息,找到泄漏 GDI 对象的代码段。

当您遇到 GDI 句柄的限制时,应用程序通常开始看起来很糟糕:不再绘制东西,并且在某些地方会出现黑色矩形。但是,这种行为不是必需的。在 OP 的情况下,黑色矩形不是描述的一部分。