调整窗口时Aforge PictureBox抛出System.InvalidOperationException异常求助
AForge PictureBox 多线程异常及程序关闭崩溃问题解决
问题现象
调整或移动应用程序窗口时,AForge的PictureBox控件抛出System.InvalidOperationException: 'The object is currently in use elsewhere.'异常,部分场景提示多线程使用问题;使用Invoke修复窗口操作异常后,程序关闭时会崩溃。
异常信息

原核心代码
public partial class MainForm : Form { //Global Var String[] serialLog = new String[300]; SerialLogDisplay sld_form; FilterInfoCollection filterInfoCollection; VideoCaptureDevice videoCaptureDevice; public MainForm() { InitializeComponent(); } //Generated Methods private void btnCCamera_Click(object sender, EventArgs e) { if(cmbCameraS.SelectedIndex > 0) { disconnectVideoStream(false); videoCaptureDevice = new VideoCaptureDevice(filterInfoCollection[cmbCameraS.SelectedIndex-1].MonikerString); videoCaptureDevice.NewFrame += VideoCaptureDevice_NewFrame; videoCaptureDevice.Start(); Add_Log("Camera \"" + filterInfoCollection[cmbCameraS.SelectedIndex-1].Name+ "\" Connected!"); } else { disconnectVideoStream(true); } } private void Form1_Load(object sender, EventArgs e) { //Camera System cmbCameraS.Items.Add("None"); filterInfoCollection = new FilterInfoCollection(FilterCategory.VideoInputDevice); foreach (FilterInfo filterInfo in filterInfoCollection) { cmbCameraS.Items.Add(filterInfo.Name); } cmbCameraS.SelectedIndex = 0; videoCaptureDevice = new VideoCaptureDevice(); //Log shortLog.Parent = cameraDisplay; shortLog.BackColor = Color.Transparent; shortLog.Text = serialLog[0] + "\n" + serialLog[1] + "\n" + serialLog[2]; //Loop Timer tmr = new Timer(); tmr.Interval = 1000; tmr.Tick += Tmr_Tick; tmr.Start(); //DisplayObjects MPpanel.Parent = cameraDisplay; } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { disconnectVideoStream(false); } private void SLbtn_Click(object sender, EventArgs e) { if (sld_form == null) { sld_form = new SerialLogDisplay(this); } sld_form.update_log(serialLog); sld_form.Show(); sld_form.Activate(); } //Created Methods //Serial Log private void Tmr_Tick(object sender, EventArgs e) { String lm = ""; for (int i = 4; i >= 0; i--) { if (serialLog[i] != null) { lm += serialLog[i] + "\n"; } } shortLog.Text = lm; } public void SerialLogDisplay_Close() { sld_form = null; } public void Add_Log(String text) { for (int i = serialLog.Length - 1; i > 0; i--) { serialLog[i] = serialLog[i - 1]; } serialLog[0] = text; if(sld_form != null) { sld_form.update_log(serialLog); } } public void Serial_Command(String command) { Add_Log(command); //Send command to serial Port } //Camera Output private void VideoCaptureDevice_NewFrame(object sender, NewFrameEventArgs eventArgs) { try { if (cameraDisplay.Image != null) { cameraDisplay.Image.Dispose(); } Bitmap bmp = (Bitmap)eventArgs.Frame.Clone(); cameraDisplay.Image = bmp; } catch (System.InvalidOperationException ex) { Add_Log("Error: " + ex); disconnectVideoStream(true); } //GC.Collect(); } private void disconnectVideoStream(bool showLog) { if (videoCaptureDevice.IsRunning == true) { if (showLog) { Add_Log("Disconnecting video stream..."); } videoCaptureDevice.SignalToStop(); videoCaptureDevice.WaitForStop(); videoCaptureDevice.Stop(); cameraDisplay.Image = null; } if (showLog) { Add_Log("No camera connected!"); } GC.Collect(); } }
修改后的Invoke代码(修复窗口操作异常但导致关闭崩溃)
private void VideoCaptureDevice_NewFrame(object sender, NewFrameEventArgs eventArgs) { Invoke(new Action(() => { try { if (cameraDisplay.Image != null) { cameraDisplay.Image.Dispose(); } Bitmap bmp = (Bitmap)eventArgs.Frame.Clone(); cameraDisplay.Image = bmp; } catch (System.InvalidOperationException ex) { Add_Log("Error: " + ex); disconnectVideoStream(true); } })); //GC.Collect(); }
解决方案
1. 解决多线程操作控件异常
AForge的NewFrame事件在非UI线程触发,直接操作PictureBox会引发跨线程访问异常。使用Invoke是正确方向,但需要先判断控件是否已被销毁,同时优化Bitmap的释放逻辑:
private void VideoCaptureDevice_NewFrame(object sender, NewFrameEventArgs eventArgs) { // 先检查控件状态,避免窗口关闭后触发无效操作 if (!cameraDisplay.IsHandleCreated || cameraDisplay.Disposing || cameraDisplay.IsDisposed) { return; } // 使用BeginInvoke替代Invoke,避免阻塞摄像头采集线程 BeginInvoke(new Action(() => { try { // 先保存旧图像,赋值新图像后再销毁旧图,避免控件正在使用旧图时被释放 Bitmap oldBitmap = cameraDisplay.Image as Bitmap; Bitmap newBitmap = (Bitmap)eventArgs.Frame.Clone(); cameraDisplay.Image = newBitmap; oldBitmap?.Dispose(); } catch (System.InvalidOperationException ex) { Add_Log("Error: " + ex.Message); disconnectVideoStream(true); } })); }
2. 修复程序关闭崩溃问题
在窗口关闭时,需要确保摄像头线程完全停止,且不再触发NewFrame事件,同时避免操作已销毁的控件:
- 修改
disconnectVideoStream方法,先移除事件绑定,再停止设备:
private void disconnectVideoStream(bool showLog) { if (videoCaptureDevice != null && videoCaptureDevice.IsRunning) { // 先移除事件处理,防止停止过程中还触发NewFrame videoCaptureDevice.NewFrame -= VideoCaptureDevice_NewFrame; if (showLog) { Add_Log("Disconnecting video stream..."); } videoCaptureDevice.SignalToStop(); // 设置超时时间,避免无限等待设备停止 if (!videoCaptureDevice.WaitForStop(500)) { videoCaptureDevice.Stop(); } } // 安全释放PictureBox的图像资源 if (cameraDisplay != null && !cameraDisplay.IsDisposed) { var img = cameraDisplay.Image; cameraDisplay.Image = null; img?.Dispose(); } if (showLog) { Add_Log("No camera connected!"); } }
- 在
FormClosing事件中,确保所有未完成的UI操作执行完毕后再关闭窗口:
private void Form1_FormClosing(object sender, FormClosingEventArgs e) { disconnectVideoStream(false); // 等待所有未完成的Invoke/BeginInvoke操作执行完毕 Application.DoEvents(); }
3. 额外优化点
- 避免频繁创建
VideoCaptureDevice实例,可复用或确保旧实例被正确释放; - 不要在
NewFrame事件中调用GC.Collect(),手动触发垃圾回收会影响性能,交给自动垃圾回收机制即可; - 日志操作
Add_Log如果涉及UI控件更新,也需要判断是否跨线程,必要时使用Invoke确保线程安全。
内容的提问来源于stack exchange,提问作者Bruno Ferreira
相关产品推荐
相关产品推荐

