C# 在加载窗口之前创建窗口句柄时出错

C# 在加载窗口之前创建窗口句柄时出错,c#,winforms,C#,Winforms,我有一个带有很多控件的大型windows窗体程序。我运行它时看到的第一个窗口是登录屏幕,它只有一个组合框、按钮、文本框和一些标签。当我按下按钮时,它加载Form1。Form1,on load自动创建一个游戏实例,它使用以下代码最小化并将游戏嵌入Form1: Game.Location = new Point(146, 6); Game.TopLevel = false; Game.Visible = true; Game.BringToFront(); Game.FormBorderStyle

我有一个带有很多控件的大型windows窗体程序。我运行它时看到的第一个窗口是登录屏幕,它只有一个组合框、按钮、文本框和一些标签。当我按下按钮时,它加载Form1。Form1,on load自动创建一个游戏实例,它使用以下代码最小化并将游戏嵌入Form1:

Game.Location = new Point(146, 6);
Game.TopLevel = false;
Game.Visible = true;
Game.BringToFront();
Game.FormBorderStyle = FormBorderStyle.None;
Controls.Add(Game);
我明白了

“创建窗口句柄时出错”

此行出错(由于某些原因,它不在窗口创建行):


请帮助,其他人说这是因为有太多的窗口,但我尝试在project中删除窗口,但我得到了相同的错误。

这个问题似乎已经得到了回答:

您收到错误是因为您的程序创建了太多错误 把手。应用程序的windows句柄限制为10000 把手。您需要找到内存泄漏

确保在关闭表单并完成后对表单调用Dispose(),或在表单中使用子句声明表单:

using(Form mainForm = new Form())
{
    mainForm.Show();
}
Process Explorer或Windows任务管理器允许您查看GDI对象、句柄、线程和用户对象。您必须对“详细信息”选项卡执行操作,然后选择要查看的列(右键单击列->选择列)。寻找大量GDI对象以确认windows句柄限制确实是问题所在

您正在处理登录表单吗?你提到你有很多控件,你可以用任何方式来限制它们,比如用一个标签和一个文本数组来显示一些东西,而不是用一堆标签或一个文本框网格等等

可以使用的内存分析器的一个示例是.Net内存分析器

(2016年3月30日更新)

修改您提供给我的信息后,我想到了三件事:

  • 注销事件

    如果要在窗体上动态创建并删除控件,则必须确保从以前使用此控件注册的每个事件中注销任何事件处理程序。如果您不这样做,事件处理程序将保留对控件的引用,并且即使您调用Dispose,它也将被阻止进行垃圾收集(以及GDI/用户对象)!在您处理父窗体之前,它们将一直保留

  • 计时器(以及其他非UI)事件

    计时器的tick事件处理程序方法(及其任何调用)中的代码将在一个单独的线程上运行,该线程不是UI线程。如果您需要访问任何UI属性、成员或方法,则必须将线程上下文切换回UI线程,否则会引发各种奇怪的异常。此外,如果您从非UI线程创建UI对象(控件),它将创建更多的GUI/用户句柄,而在其他情况下可能没有。您可以通过调用表单或控件的Invoke方法来完成此操作,并在匿名方法中执行UI操作,或者将其作为委托传递给UI操作代码。如果还直接从任何UI对象调用该方法,则可能需要检查Control.invokererequired,以避免使用锁的代价高昂的Invoke方法

  • AppDomain.FirstChanceException

    如果所有其他方法都失败了,而您仍然不知道为什么会出现异常,下面是一个聪明的小技巧,我用来查找非常模糊或难以找到的bug:向AppDomain.FirstChanceException事件注册一个事件处理程序,并在处理程序的方法体中放置一个断点。这样,下一次抛出异常时,您的代码将获得处理它的“第一次机会”,并且您将在我刚才提到的FirstChanceException处理程序方法中命中该断点。从这里,您可以打开调用堆栈窗口,查看抛出异常的堆栈跟踪。这包括源自本机或.net API的异常。通常,这些信息对您是隐藏的,因为调用堆栈在将异常传递给代码之前会被清除,您可以在代码中捕获异常


  • 这个问题似乎已经得到了回答:

    您收到错误是因为您的程序创建了太多错误 把手。应用程序的windows句柄限制为10000 把手。您需要找到内存泄漏

    确保在关闭表单并完成后对表单调用Dispose(),或在表单中使用子句声明表单:

    using(Form mainForm = new Form())
    {
        mainForm.Show();
    }
    
    Process Explorer或Windows任务管理器允许您查看GDI对象、句柄、线程和用户对象。您必须对“详细信息”选项卡执行操作,然后选择要查看的列(右键单击列->选择列)。寻找大量GDI对象以确认windows句柄限制确实是问题所在

    您正在处理登录表单吗?你提到你有很多控件,你可以用任何方式来限制它们,比如用一个标签和一个文本数组来显示一些东西,而不是用一堆标签或一个文本框网格等等

    可以使用的内存分析器的一个示例是.Net内存分析器

    (2016年3月30日更新)

    修改您提供给我的信息后,我想到了三件事:

  • 注销事件

    如果要在窗体上动态创建并删除控件,则必须确保从以前使用此控件注册的每个事件中注销任何事件处理程序。如果您不这样做,事件处理程序将保留对控件的引用,并且即使您调用Dispose,它也将被阻止进行垃圾收集(以及GDI/用户对象)!在您处理父窗体之前,它们将一直保留

  • 计时器(以及其他非UI)事件

    计时器的tick事件处理程序方法(及其任何调用)中的代码将在一个单独的线程上运行,该线程不是UI线程。如果您需要访问任何UI p