最近做一個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) 實現。