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

时间:2017-11-02 06:46:00

标签: c# winforms

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

Just an Example of RibbonBar

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

OutOfMemoryException occurred in System.Drawing.dll

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

enter image description here

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

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

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

以及在使用应用程序一段时间后发生错误时,该错误会显示在Program.csApplication.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是包含功能区栏的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"

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");
}

以下是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;
}

并且在每个mdi child的表单构造函数中,InitializeComponent();之后我也写了GC.Collect();。但正如评论中所述,任务管理器中的GDI objects将增加并增加到10000个对象,然后应用程序将崩溃。

UPADTED:最重要的问题

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

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

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);
}

2 个答案:

答案 0 :(得分:5)

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

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

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

所以我宣布public static cursor相应表格的main()如下:

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

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

this.Cursor = MainForm.animCur;

那是它:))

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

答案 1 :(得分:3)

OutOfMemoryExceptions可能有多种原因。我已经讨论了其中的6个in another question

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

GDI objects shown in Task Manager

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

GDIView details

GDI处理的数量can be configured in Registry。不要将其用作永久解决方案。相反,使用GDIView的附加信息,找到泄漏GDI对象的代码段。

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