C#编译错误:“在创建窗口句柄之前,无法在控件上调用Invoke或BeginInvoke。”

我刚刚发布了一个关于如何让代理人在另一个表单上更新文本框的问题。 正当我以为我有使用Invoke的答案时…这种情况发生了。 这是我的代码:

主要表格代码:

using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; using System.IO; using System.Data.OleDb; using System.Collections.Specialized; using System.Text; using System.Threading; delegate void logAdd(string message); namespace LCR_ShepherdStaffupdater_1._0 { public partial class Main : Form { public Main() { InitializeComponent(); } public void add(string message) { this.Log.Items.Add(message); } public void logAdd(string message) { /////////////////////////// COMPILER ERROR BELOW /////////// this.Invoke(new logAdd(add), new object[] { message }); // Compile error occurs here }////////////////////////////// COMPILER ERROR ABOVE /////////// private void exitProgramToolStripMenuItem_Click(object sender, EventArgs e) { Application.Exit(); } private void aboutToolStripMenuItem1_Click(object sender, EventArgs e) { Form aboutBox = new AboutBox1(); aboutBox.ShowDialog(); } private void settingsToolStripMenuItem_Click(object sender, EventArgs e) { } private void settingsToolStripMenuItem1_Click(object sender, EventArgs e) { settingsForm.settings.ShowDialog(); } private void synchronize_Click(object sender, EventArgs e) { string message = "Here my message is"; // changed this ErrorLogging.updateLog(message); // changed this } } public class settingsForm { public static Form settings = new Settings(); } } 

记录类代码:

 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace LCR_ShepherdStaffupdater_1._0 { public class Logging { static Main mainClass = new Main(); static logAdd logAddDelegate; public static void updateLog(string message) { logAddDelegate = mainClass.logAdd; logAddDelegate(message); } } } 
  • 编译错误:

    InvalidOperationException未处理 – 在创建窗口句柄之前,无法在控件上调用Invoke或BeginInvoke。

我已经尝试在Log项目上创建一个句柄……但是这不起作用。 问题是我没有CLUE我正在做什么,我已经广泛搜索谷歌只是为了找到模糊的答案。

请在调用此委托之前告诉我如何创建句柄。 当你在这里,给我一些方法,我可以使这个代码更简单。 例如,我不想要两个Add函数……我必须这样做,因为我无法从Logging类中找到要调用的项。 有没有更好的方法来完成我需要做的事情?

谢谢!!!

编辑:

我的项目相当大,但这些是导致此特定问题的唯一项目。

Log是我的RichTextBox1(Log.Items.Add(message))我将它重命名为Log,因此更容易重新输入。

我从另一种forms调用updateLog(消息)…让我在这里更新(虽然它从我调用updateLog(消息)没有区别仍然给我这个错误)

你们不得不让事情变得更简单……并提供一些例子。 我不明白你们在这里说的所有内容的HALF ……我不知道如何使用Invoking of methods和Handles。 我也研究了它的废话……

第二次编辑:

我相信我找到了问题,但不知道如何解决它。

在我的日志类中,我使用此代码创建mainClass:

static Main mainClass = new Main();

我正在为Main()创建一个全新的蓝图副本,包括Log (我正在尝试更新的richtextbox)

当我调用updateLog(消息)时,我相信我正在尝试更新Main()的第二个实体(也称为mainClass)上的Log(richtextbox)。 当然,这样做会让我这个例外,因为我甚至没有看到我正在使用的当前Main的副本。

这是我正在拍摄的,感谢其中一位给出答案的人:

 Main mainClass = Application.OpenForms.OfType().First(); logAddDelegate = mainClass.logAdd; logAddDelegate(message); 

我需要使用new()运算符创建mainClass,因为我不想创建我希望能够编辑当前表单的表单的新蓝图。

上面的代码不起作用,我甚至找不到Application。 这甚至是C#语法吗?

如果我能够使上述代码工作,我想我可以解决我的问题,并在经过几个小时的寻求答案后最终解决这个问题。

最终编辑:

我想通过以下用户之一了解它。 这是我更新的代码:

主要表格代码:

 using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; using System.IO; using System.Data.OleDb; using System.Collections.Specialized; using System.Text; using System.Threading; delegate void logAdd(string message); namespace LCR_ShepherdStaffupdater_1._0 { public partial class Main : Form { private static Main mainFormForLogging; public static Main MainFormForLogging { get { return mainFormForLogging; } } public Main() { InitializeComponent(); if (mainFormForLogging == null) { mainFormForLogging = this; } } public void add(string message) { this.Log.Items.Add(message); } public void logAdd(string message) { this.Log.BeginInvoke(new logAdd(add), new object[] { message }); } private void exitProgramToolStripMenuItem_Click(object sender, EventArgs e) { Application.Exit(); } private void aboutToolStripMenuItem1_Click(object sender, EventArgs e) { Form aboutBox = new AboutBox1(); aboutBox.ShowDialog(); } private void settingsToolStripMenuItem_Click(object sender, EventArgs e) { } private void settingsToolStripMenuItem1_Click(object sender, EventArgs e) { settingsForm.settings.ShowDialog(); } private void synchronize_Click(object sender, EventArgs e) { add("test"); Logging.updateLog("testthisone"); //DatabaseHandling.createDataSet(); } } public class settingsForm { public static Form settings = new Settings(); } } 

记录类代码:

 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace LCR_ShepherdStaffupdater_1._0 { public class Logging { static Main mainClass = Main.MainFormForLogging; static logAdd logAddDelegate; public static void updateLog(string message) { logAddDelegate = mainClass.logAdd; logAddDelegate(message); } } } 

对,我要重新开始。

为了理解正在发生的事情,您需要了解.NET和Windows如何相互关联。 .NET在Windows上运行并包含许多本机Win32概念,如窗口,列表视图,编辑框(标准文本框的Win32名称)。 这意味着您可以拥有TextBox或Form的有效.NET实例,但还没有该项目的基础Windows版本(EditBox或Window)。 当HandleCreated为true时,将创建项目的Windows版本。

您的问题正在发生,因为有些东西导致在创建表单窗口之前调用logAdd方法。 这意味着在启动Form实例之后的某个地方,但是在创建Window句柄之前,有些东西试图调用logAdd。 如果向logAdd添加断点,您应该能够看到正在进行该调用的内容。 你会发现,调用是在你在logger类中创建的Main实例上进行的,而不是实际运行的Main实例。 由于记录器实例永远不会显示,因此不会创建窗口句柄,因此您会收到错误消息。

应用程序运行的一般方法是在启动方法中调用Application.Run(new Main()),该方法通常位于Program类中并称为Main。 您需要记录器指向main的这个实例。

有几种方法可以获取表单的实例,每种方法都有自己的注意事项,但为了简单起见,您可以将实例从Main类本身公开。 例如:

 public partial class Main : Form { private static Main mainFormForLogging; public static Main MainFormForLogging { get { return mainFormForLogging; } } public Main() { InitializeComponent(); if (mainFormForLogging == null) { mainFormForLogging = this; } } protected void Dispose(bool disposing) { if (disposing) { if (this == mainFormForLogging) { mainFormForLogging = null; } } base.Dispose(disposing); } } 

我过去使用以下方法解决了这个问题:

 private void invokeOnFormThread(MethodInvoker method) { if (IsHandleCreated) Invoke(new EventHandler(delegate { method(); })); else method(); } 

调用invokeOnFormThread而不是Invoke。 如果已经创建了句柄,它将仅使用表单的线程,否则它将使用调用者的线程。

当您收到此错误时,它几乎总是意味着您在实际创建控件或表单之前尝试对其进行操作。

在WinForms中,GUI元素有两个半独立的生命:作为内存中的类和操作系统中的实体。 因此,可以在.net中引用尚未实际创建的控件。 “正在创建的句柄”指的是具有由OS分配给控件的编号以允许程序操纵其属性。

在这种情况下,大多数错误可以通过在窗体的加载事件的末尾设置一个标志来消除,并且只在设置了该标志之后尝试操作窗体的控件。

这是运行时错误,而不是编译器错误。

在您可以调用BeginInvoke或Invoke之前,必须显示您的表单“Main”(因此创建了一个窗口句柄)。

我通常在这些情况下做的是将它留给表单来确定它是否需要使用对BeginInvoke或Invoke的调用。 您可以通过调用InvokeRequired来测试它(检查MSDN)。

所以对于初学者,我将在Loggin类的updateLog方法中删除logAddDelegate调用。 只需直接调用表单即可添加日志。 像这样:

 public partial class Main : Form { public Main() { InitializeComponent(); } private delegate void AddNewLogMessageEventHandler(string message); public void AddLogMessage(string message) { object[] args = new object[1]; args[0] = message; if (InvokeRequired) BeginInvoke(new AddNewLogMessageEventHandler(AddLog), args); else Invoke(new AddNewLogMessageEventHandler(AddLog), args); } private void AddLog(string message) { this.Log.Items.Add(message); } } 

}

所以你可以看到,Form本身负责确定是否需要异步调用该方法。

但是,这仍然无法修复运行时错误,因为您在显示之前调用了表单。 您可以检查表单的句柄是否为空,这至少可以让您validation您是否正在处理有效的表单。

如果您在尚未“显示”的窗口上调用,则会发生该错误。 你确定你没有使用Logging类中的代码(特别是第一行)创建主类的第二个实例吗? 可能是您正在调用登录的主要表单不是您正在查看的主要表单。 如果要检查,请在日志记录调用内添加对“MainClass.Show()”的调用。 如果您弹出主表单的第二个副本,则问题是您的日志记录类没有引用表单的正确“实例”。

将课程视为“蓝图”。 该类的每个实例(使用“new”一词创建)是另一个从蓝图创建的对象。 仅仅因为两个对象(在这种情况下,您的两个主要forms)共享相同的蓝图,并不意味着您可以互换使用它们。 在这种情况下,您已经拥有一个主表单,并且您想要“重用”它。 你可以试试:

 MainClass myMainForm = Application.OpenForms.OfType().First(); logAddDelegate = myMainForm.logAdd; logAddDelegate(message); 

在你的日志function中,而不是你现在拥有的。 不同之处在于对Application.OpenForms.OfType()的调用。首先将进入您的应用程序,并检索您正在看到的ACTUAL主表单(从技术上讲,它将检索它的第一个实例)并对其进行调用forms,直接。

希望这可以帮助。

这是为了帮助以防任何其他人陷入困境。 我的问题:VB.net:“在创建窗口句柄之前,无法在控件上调用Invoke或BeginInvoke。”我关闭了一个表单,该表单具有调用更新委托的事件的处理程序,而不删除处理程序事件。

我做了什么:当我关闭表单时,我删除了所有处理程序,并在打开表单时将其分配回来。 它解决了这个问题。

logAddDelegate(消息);

我想你在Form_Load事件被引发之前就是在调用它。 在调用logAddDelegate(…)之前,即在调用Logging.updateLog()之前修复代码以等待表单加载

这是您的确切代码吗? 你正在调用this.Log.Items.Add(message); 在您的add(string)方法中,但您的日志记录类称为Logging,而不是Log。 你有另一种叫做Log的forms吗? 如果在调用add方法时尚未创建该表单,则会出现此exception。

我发现InvokeRequired不可靠,所以我只是使用

 if (!this.IsHandleCreated) { this.CreateHandle(); }