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

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日

悬赏问题

  • ¥15 PointNet++的onnx模型只能使用一次
  • ¥20 西南科技大学数字信号处理
  • ¥15 有两个非常“自以为是”烦人的问题急期待大家解决!
  • ¥30 STM32 INMP441无法读取数据
  • ¥15 R语言绘制密度图,一个密度曲线内fill不同颜色如何实现
  • ¥100 求汇川机器人IRCB300控制器和示教器同版本升级固件文件升级包
  • ¥15 用visualstudio2022创建vue项目后无法启动
  • ¥15 x趋于0时tanx-sinx极限可以拆开算吗
  • ¥500 把面具戴到人脸上,请大家贡献智慧,别用大模型回答,大模型的答案没啥用
  • ¥15 任意一个散点图自己下载其js脚本文件并做成独立的案例页面,不要作在线的,要离线状态。