簡體   English   中英

在單線程應用程序中調用WMI函數時,DisconnectedContext MDA

[英]DisconnectedContext MDA when calling WMI functions in single-threaded application

我用VS2005中的C#和.NET 3.0編寫了一個應用程序,具有監視各種可移動驅動器(USB閃存盤,CD-ROM等)的插入/彈出的功能。 我不想使用WMI,因為它可以有時會產生歧義(例如,它可以生成一個單一的USB驅動器插入多個事件),所以我干脆重寫我的MainForm的的WndProc趕WM_DEVICECHANGE消息,如建議在這里 昨天,當我發現無論如何我都必須使用WMI來檢索一些晦澀的磁盤詳細信息(例如序列號)時,我遇到了一個問題。 事實證明,從WndProc內部調用WMI例程會引發DisconnectedContext MDA。

經過一番挖掘之后,我以一個尷尬的解決方法告終。 代碼如下:

    // the function for calling WMI 
    private void GetDrives()
    {
        ManagementClass diskDriveClass = new ManagementClass("Win32_DiskDrive");
        // THIS is the line I get DisconnectedContext MDA on when it happens:
        ManagementObjectCollection diskDriveList = diskDriveClass.GetInstances();
        foreach (ManagementObject dsk in diskDriveList)
        {
            // ...
        }
    }

    private void button1_Click(object sender, EventArgs e)
    {
        // here it works perfectly fine
        GetDrives();
    }


    protected override void WndProc(ref Message m)
    {
        base.WndProc(ref m);

        if (m.Msg == WM_DEVICECHANGE)
        {
            // here it throws DisconnectedContext MDA 
            // (or RPC_E_WRONG_THREAD if MDA disabled)
            // GetDrives();
            // so the workaround:
            DelegateGetDrives gdi = new DelegateGetDrives(GetDrives);
            IAsyncResult result = gdi.BeginInvoke(null, "");
            gdi.EndInvoke(result);
        }
    }
    // for the workaround only
    public delegate void DelegateGetDrives();

這基本上意味着要在單獨的線程上運行與WMI相關的過程-然后等待它完成。

現在的問題是: 為什么它起作用, 為什么必須這樣呢? (或者,是嗎?)

我不了解首先獲得DisconnectedContext MDA或RPC_E_WRONG_THREAD的事實。 從按鈕單擊事件處理程序運行GetDrives()過程與從WndProc調用過程有何不同? 它們不是在我的應用程序的同一主線程上發生嗎? 順便說一句,我的應用程序完全是單線程的,那么為什么突然出現一個錯誤消息,指出某個“錯誤的線程”? 使用WMI是否意味着對System.Management中的函數進行多線程處理和特殊處理?

在此期間,我發現了另一個與該MDA相關的問題 好的,我可以認為調用WMI意味着為基礎COM組件創建一個單獨的線程-但是我仍然沒有想到為什么在按下按鈕后調用它時不需要魔術,而在調用時需要do-magic它來自WndProc。

我對此感到非常困惑,希望能對此做一些澄清。 只有一些比解決方案更糟糕的事情,而不知道為什么它會起作用:/

干杯,亞歷山大

有COM公寓和消息的抽一個相當長的討論在這里 但是,主要的關注點是消息泵用於確保STA中的呼叫被正確封送。 由於UI線程是所討論的STA,因此需要發送消息以確保一切正常。

實際上,WM_DEVICECHANGE消息可以多次發送到窗口。 因此,在直接調用GetDrives的情況下,有效地導致了遞歸調用。 在GetDrives調用上放置一個斷點,然后連接一個設備以觸發該事件。

第一次達到斷點時,一切都很好。 現在按F5繼續,您將再次達到斷點。 這次調用堆棧類似於:

[在睡眠,等待或加入時] DeleteMeWindowsForms.exe!DeleteMeWindowsForms.Form1.WndProc(ref System.Windows.Forms.Message m)第46行C#System.Windows.Forms.dll!System.Windows.Forms.Control.ControlNativeWindow .OnMessage(ref System.Windows.Forms.Message m)+ 0x13字節
System.Windows.Forms.dll!System.Windows.Forms.Control.ControlNativeWindow.WndProc(ref System.Windows.Forms.Message m)+ 0x31字節
System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.DebuggableCallback(System.IntPtr hWnd,int msg,System.IntPtr wparam,System.IntPtr lparam)+ 0x64字節[本地到托管過渡]
[管理到本地過渡]
mscorlib.dll!System.Threading.WaitHandle.InternalWaitOne(System.Runtime.InteropServices.SafeHandle waitableSafeHandle,長毫秒超時,bool hasThreadAffinity,bool exitContext)+ 0x2b字節mscorlib.dll!System.Threading.WaitHandle。 )+ 0x2d字節
mscorlib.dll!System.Threading.WaitHandle.WaitOne()+ 0x10字節System.Management.dll!System.Management.MTAHelper.CreateInMTA(System.Type類型)+ 0x17b字節
System.Management.dll!System.Management.ManagementPath.CreateWbemPath(字符串路徑)+ 0x18字節System.Management.dll!System.Management.ManagementClass.ManagementClass(字符串路徑)+ 0x29字節
DeleteMeWindowsForms.exe!DeleteMeWindowsForms.Form1.GetDrives()第23行+ 0x1b字節C#

如此有效地發送了窗口消息,以確保正確地整理了COM調用,但這具有在仍然處於先前的GetDrives調用中的同時再次調用WndProc和GetDrives(因為有待處理的WM_DEVICECHANGE消息)的副作用。 使用BeginInvoke時,將刪除此遞歸調用。

再次,在GetDrives調用上放置一個斷點,並在第一次命中后按F5。 下一次,請等待一兩秒鍾,然后再次按F5。 有時它將失敗,有時則不會,您將再次遇到斷點。 這次,您的調用堆棧將包括對GetDrives的三個調用,最后一個由diskDriveList集合的枚舉觸發。 再次,因為消息被抽出以確保呼叫被封送。

很難確切指出觸發MDA的原因,但是鑒於遞歸調用,可以合理地假定COM上下文可能會過早地被破壞和/或在釋放基礎COM對象之前收集了一個對象。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM