最近做一个winForm的小工具,用到了 ManagementObjectSearcher/ManagementClass 和 WndProc ,涉及到对 移动设备的检测。
窗体加载时会执行一个 Reload()方法(通过 ManagementObjectSearcher/ManagementClass 重新获取移动设备盘符),但是当把设备 拔出或插入时,由 WndProc 去执行 Reload() 方法,老是报 :
托管调试助手 "DisconnectedContext":“上下文 0xf20540 已断开连接。将不会使用代理来处理 COM 组件上的请求。这可能会导致损坏或数据丢失。要避免此问题,请确保在应用程序全部完成 RuntimeCallableWrapper (表示其内部的 COM 组件)之前,所有上下文/单元都保持活动状态。
怎么来解决这个问题?我也不知道,网上找了很久,没由合适的的解决办法。只有自己分析,去尝试解决了。。
通过调试,发现 Reload() 在三处有调用,窗体加载,设备插入,设备拔出。窗体加载时,Reload() 没有报错,正常加载,而当设备插入或拔出后,执行Reload() 就会报异常。
由此可见,可能好像是 不是一个线程 去执行 的 Reload() 导致,窗体加载时是UI线程 主线程 执行,而 插拔 执行的 WndProc 是另外的线程去执行。
在此,为了保证 Reload() 的 3处地方 执行都是一个线程去执行,怎么实现呢?看代码:
#region 检测可移动设备 public const int WM_DEVICECHANGE = 0x219; public const int DBT_DEVICEARRIVAL = 0x8000; public const int DBT_CONFIGCHANGECANCELED = 0x0019; public const int DBT_CONFIGCHANGED = 0x0018; public const int DBT_CUSTOMEVENT = 0x8006; public const int DBT_DEVICEQUERYREMOVE = 0x8001; public const int DBT_DEVICEQUERYREMOVEFAILED = 0x8002; public const int DBT_DEVICEREMOVECOMPLETE = 0x8004; public const int DBT_DEVICEREMOVEPENDING = 0x8003; public const int DBT_DEVICETYPESPECIFIC = 0x8005; public const int DBT_DEVNODES_CHANGED = 0x0007; public const int DBT_QUERYCHANGECONFIG = 0x0017; public const int DBT_USERDEFINED = 0xFFFF; /// <summary> /// 检测可移动设备 /// </summary> protected override void WndProc(ref Message m) { try { if (m.Msg == WM_DEVICECHANGE) { switch (m.WParam.ToInt32()) { case WM_DEVICECHANGE: break; case DBT_DEVICEARRIVAL://可移动设备 插入 isReload = true; break; case DBT_CONFIGCHANGECANCELED: break; case DBT_CONFIGCHANGED: break; case DBT_CUSTOMEVENT: break; case DBT_DEVICEQUERYREMOVE: break; case DBT_DEVICEQUERYREMOVEFAILED: break; case DBT_DEVICEREMOVECOMPLETE: //可移动设备 卸载 isReload = true; break; case DBT_DEVICEREMOVEPENDING: break; case DBT_DEVICETYPESPECIFIC: break; case DBT_DEVNODES_CHANGED: break; case DBT_QUERYCHANGECONFIG: break; case DBT_USERDEFINED: break; default: break; } } } catch (Exception ex) { MessageBox.Show((ex.InnerException ?? ex).ToString(), "检测可移动设备时出错", MessageBoxButtons.OK, MessageBoxIcon.Warning); } base.WndProc(ref m); } #endregion
/// <summary> /// 因为 Form_Load 与 WndProc 两个不同线程 去调用 ManagementClass 会报异常,故这里 开启一个线程去后台维护更新下拉框。 /// </summary> public Thread thread = null; /// <summary> /// 是否重新加载 下拉框 /// </summary> public bool isReload = false; private void MainForm_Load(object sender, EventArgs e) { thread = new Thread(Reload); thread.IsBackground = true; isReload = true; thread.Start(); }
/// <summary> /// 重新加载 移动硬盘盘符 /// </summary> private void Reload() { while (true) { if (!isReload) { Thread.Sleep(500); } else { //这里编写业务逻辑代码。。。。。
isReload = false; } } }
注:使用 While True + Thread.Sleep + 执行标识(isReload) 实现。