Chrobo 2023-01-31 14:05 采纳率: 75%
浏览 78
已结题

C#关闭窗口错误:在创建窗口句柄之前,不能在控件上调用 Invoke 或 BeginInvoke。

问题

C#程序窗口关闭时,会报错:“System.InvalidOperationException”类型的未经处理的异常在 System.Windows.Forms.dll 中发生 其他信息: 在创建窗口句柄之前,不能在控件上调用 Invoke 或 BeginInvoke。

img

程序代码

为了让lable的文字内容方便修改,将其封装成可调用的函数:

private void Notify_Set(string text)
{
    lbNotify.Invoke((MethodInvoker)delegate
    {
        lbNotify.Text = text;
    });
}

线程函数如下:

private void Thread_Test()
{
   Thread.Sleep(1000); 

    while (g_ifToolRun)
    {
       Notify_Set("Have not device connect");   
    }
}

在窗口初始化完成后启动线程:

public TestProgram()
 {
    InitializeComponent();

    Thread mThread = new Thread(() => Thread_Test());
    mThread.Priority = ThreadPriority.Highest;
    mThread.Start();
}

在程序关闭时即会报错:

img

尝试解决

参考其他类似的问题,用控件的IsHandleCreated方法来做判断,有句柄时才执行Invoke:

private void Notify_Set(string text)
{
    if(lbNotify.IsHandleCreated)    //这里判断句柄是否有创建
    {
        lbNotify.Invoke((MethodInvoker)delegate
        {
           lbNotify.Text = text;
       });
    }
}

运行结果依然是会报错:

img

提问

这种情况应该怎么避免该报错?是程序上哪里写不对还是这个IsHandleCreated方法在某种情况不适用?

  • 写回答

3条回答 默认 最新

  • m0_54204465 2023-01-31 14:20
    关注

    这个错误是因为在窗口句柄创建之前,代码试图在控件上调用 Invoke 或 BeginInvoke 方法。在窗口句柄创建后,再进行这种操作即可。可以在窗口的 Load 事件中启动线程,以确保窗口句柄已创建,代码如下:

    private void TestProgram_Load(object sender, EventArgs e)
    {
    Thread mThread = new Thread(() => Thread_Test());
    mThread.Priority = ThreadPriority.Highest;
    mThread.Start();
    }
    
    

    如果在程序退出时依旧存在问题,可以考虑使用其他方法如加锁、使用线程安全的控件等来解决。可以使用 lock 关键字加锁以保护数据的访问,并确保只有一个线程可以同时访问该数据。您也可以使用线程安全的控件,例如 System.Windows.Forms.ListView,而不是不安全的控件,例如 System.Windows.Forms.ListBox。

    private object _lock = new object();
    private List<string> _data = new List<string>();
    
    private void ThreadProc()
    {
        lock (_lock)
        {
            _data.Add("Thread data");
        }
    }
    
    private void UpdateListView()
    {
        lock (_lock)
        {
            listView1.Items.Clear();
            foreach (var item in _data)
            {
                listView1.Items.Add(item);
            }
        }
    }
    
    

    你可以在线程结束前调用Control.Invoke来执行一些代码,以保证线程在退出前调用的代码完成。你也可以使用同步的方式访问控件,例如使用 Control.BeginInvoke 和 Control.EndInvoke。

    private void Button1_Click(object sender, EventArgs e)
    {
        new Thread(() => 
        {
            // Perform a long-running task.
    
            // Call BeginInvoke to safely update the UI from a different thread.
            BeginInvoke(new Action(() => 
            {
                // Update the UI.
            }));
        }).Start();
    }
    
    

    或者:

    private void Button1_Click(object sender, EventArgs e)
    {
        new Thread(() => 
        {
            // Perform a long-running task.
    
            // Call Control.Invoke to safely update the UI from a different thread.
            Control.Invoke(new Action(() => 
            {
                // Update the UI.
            }));
        }).Start();
    }
    
    

    在退出线程前,请确保已调用 Control.Invoke 或 Control.BeginInvoke。
    你还可以在程序终止时显式地等待所有线程退出。可以使用Thread.Join()方法,让主线程等待其他线程完成后再退出。例如:

    List<Thread> threads = new List<Thread>();
    
    // 创建并启动多个线程
    for (int i = 0; i < 10; i++)
    {
        Thread t = new Thread(new ThreadStart(DoWork));
        t.Start();
        threads.Add(t);
    }
    
    // 等待所有线程完成
    foreach (Thread t in threads)
    {
        t.Join();
    }
    
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论 编辑记录
查看更多回答(2条)

报告相同问题?

问题事件

  • 系统已结题 2月9日
  • 已采纳回答 2月1日
  • 创建了问题 1月31日

悬赏问题

  • ¥55 AD844 howland电流源如何驱动大额负载
  • ¥15 C++ /QT 内存权限的判断函数列举
  • ¥15 深度学习GFnet理解问题
  • ¥15 单细胞小提琴堆叠图代码
  • ¥50 升级strust2版本到2.3.15.1后使用ognl3.0.6.jar windows环境中没有问题,但部署到linux环境报错
  • ¥15 vue页面,node封装接口
  • ¥15 求TMS320F280039C工程模板!
  • ¥15 delphi+fastreport实现分组补空打印问题
  • ¥15 使用python把两台mysql数据库服务器数据导出和导入
  • ¥15 NodeBB论坛配置Apache Solr中文搜索引擎的详细教程