C#程序窗口关闭时,会报错:“System.InvalidOperationException”类型的未经处理的异常在 System.Windows.Forms.dll 中发生 其他信息: 在创建窗口句柄之前,不能在控件上调用 Invoke 或 BeginInvoke。
为了让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();
}
在程序关闭时即会报错:
参考其他类似的问题,用控件的IsHandleCreated方法来做判断,有句柄时才执行Invoke:
private void Notify_Set(string text)
{
if(lbNotify.IsHandleCreated) //这里判断句柄是否有创建
{
lbNotify.Invoke((MethodInvoker)delegate
{
lbNotify.Text = text;
});
}
}
运行结果依然是会报错:
这种情况应该怎么避免该报错?是程序上哪里写不对还是这个IsHandleCreated方法在某种情况不适用?
这个错误是因为在窗口句柄创建之前,代码试图在控件上调用 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();
}
原因是窗体对象已经释放,但线程还在执行,导致句柄为空
解决办法
1.关闭窗口时在closing事件里杀死线程
2.线程里可以先判断this.Disposed
3.无脑加个try