【问题标题】:C# compile error: "Invoke or BeginInvoke cannot be called on a control until the window handle has been created."C# 编译错误:“在创建窗口句柄之前,无法在控件上调用 Invoke 或 BeginInvoke。”
【发布时间】:2009-02-04 20:24:21
【问题描述】:

我刚刚发布了一个关于如何让代表更新另一个表单上的文本框的问题。就在我认为我使用 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 项上创建句柄...但是没有用。问题是我不知道自己在做什么,我在 Google 上广泛地进行了搜索,却只找到了模糊的答案。

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

谢谢!!!

编辑:

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

Log 是我的 RichTextBox1 (Log.Items.Add(message)) 我将其重命名为 Log,以便重新输入。

我正在从不同的表单调用 updateLog(message)...让我在这里更新它(尽管我从它调用 updateLog(message) 并没有什么区别仍然给我这个错误)

你们将不得不让我的事情变得更简单......并提供示例。我不明白你们在这里所说的一切......我不知道如何使用方法和句柄的调用。我也研究过它的废话......

第二次编辑:

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

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

静态 Main mainClass = new Main();

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

当我调用 updateLog(message) 时,我相信我正在尝试更新 Main() 的第二个实体(也称为 mainClass)上的日志(richtextbox)。当然,这样做会抛出这个异常,因为我什至没有看到我正在使用的当前 Main 的副本。

这就是我的目标,感谢其中一位给出答案的人:

Main mainClass = Application.OpenForms.OfType<Main>().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);
        }
    }
}

【问题讨论】:

  • 您确定这是编译器错误吗?对我来说,这听起来更像是执行时间异常......
  • 您确定 mainClass 与您正在查看的 Main 实例相同吗?您是否有可能有两个实例 - 一个已创建,一个未创建?
  • 伙计们......我不确定自己......这部分让我很困惑,我几乎不知道发生了什么哈哈
  • “他的问题是我不知道我在做什么”...是的,这是个问题... :)
  • 我让一件事开始工作,然后 5 分钟后又发生了其他事情。如果我能找到一个很好的例子,我会确切地知道某人在说什么。

标签: c# delegates invoke handle runtime-error


【解决方案1】:

好吧,我要重新开始了。

为了了解正在发生的事情,您需要了解 .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);
    }
}

【讨论】:

  • 好吧,你告诉我:在我的 Logging 类中,我创建 Main 的 NEW 对象的事实与它无关?我只是说,我相信我正在尝试编辑驻留在 Main 上的 TextBox mainClass = new Main();我没有看到新的 Main,所以它上面的 TextBox 永远不会加载。
  • 不,您的新 Main 与它有关。请参阅我刚刚添加的代码示例,了解如何解决此问题。
  • 我终于做到了。非常感谢你,你是我的英雄。
  • 没问题。很抱歉之前把你埋在行话里——很难在适当的技术水平上找到答案,当我无法传达我的意思时(我的错,不是你的错),这让我感到沮丧。我很高兴能帮上忙。继续前进,祝你好运。
  • +1 提示添加断点,这帮助我解决了问题。有时我会忘记最明显的事情......
【解决方案2】:

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

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

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

【讨论】:

  • 我会看看我是否可以将其转换为我的代码......我不完全确定所有这些都做了什么以及为什么我应该这样做。到目前为止,我还不知道如何使用它。
  • 这很聪明。它可以让逻辑测试在没有表单的情况下运行。
  • 这是处理 Invoke 的标准习语。是最正确的答案。
【解决方案3】:

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

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

在这种情况下,大多数错误可以通过在表单的加载事件结束时设置一个标志来消除,并且仅在设置该标志后才尝试操作表单的控件。

【讨论】:

  • 但是我的文本框已经创建了!我可以在单击按钮之前看到它。如果它没有被创建......我如何“创建它”!因为我不知道......
  • 您不是在 TextBox 上调用 Invoke,而是在 Form 上调用它。如果在创建表单之前或在销毁表单之后调用它,您将收到此错误。
  • 我正在尝试向存在于 main 的第二个版本的 TextBox 添加一些内容,因为我执行“Main myClass = new Main()”我需要弄清楚如何创建 myClass 而不必创建 Main() 的新蓝图。
  • @OneShot:请参阅我的其他答案以获得可能的解决方案。
【解决方案4】:

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

在您可以调用 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 本身负责确定它是否需要异步调用该方法。

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

【讨论】:

  • 谢谢!是的,我知道它不会解决这个问题......一个简单的 RichTextBox1(日志)怎么可能不是有效的或加载的表单呢???
  • 不是文本框无效。是表格。表单必须在您对其进行任何调用之前创建其句柄。只有在显示表单时才会创建句柄。尝试一些 Console.WriteLines,看看您是否可以确定呼叫是否/何时发生以及以什么顺序发生。
  • 我发现在我的 Logging 类中我正在创建我的 MainClass 的一个新实例......这就是它尚未创建的原因。现在我必须弄清楚如何在我的日志记录类中创建一个新的 MainClass 实例,而不是创建一个相同类的新实例。
【解决方案5】:

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

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

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

在您的日志函数中,而不是您当前拥有的。不同之处在于对 Application.OpenForms.OfType().First 的调用将进入您的应用程序,并检索您所看到的 ACTUAL 主表单(从技术上讲,它将检索它的第一个实例)并对其进行调用形式,直接。

希望这会有所帮助。

【讨论】:

  • 天哪,我认为这可能会做到,但我还没有让它工作。我似乎找不到 Application.OpenForms...那在哪里??
  • System.Windows.Forms.Application.OpenForms
【解决方案6】:

这是为了以防万一其他人遇到此问题。 我的问题: VB.net:“在创建窗口句柄之前,不能在控件上调用 Invoke 或 BeginInvoke。” 我关闭了一个表单,其中包含正在调用的事件处理程序以更新委托,但没有删除事件的处理程序。

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

【讨论】:

    【解决方案7】:

    logAddDelegate(消息);

    我认为您是在 Form_Load 事件引发之前调用它的。修复您的代码以在调用 logAddDelegate(...) 之前等待表单加载,即在调用 Logging.updateLog() 之前

    【讨论】:

    • 怎么会加载不出来...在我点击按钮之前,我正在直视TextBox。
    【解决方案8】:

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

    【讨论】:

      【解决方案9】:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-05-14
        • 2010-10-22
        • 2023-01-10
        • 1970-01-01
        • 1970-01-01
        • 2014-10-27
        • 1970-01-01
        相关资源
        最近更新 更多