DisconnectedContext MDA При вызове функций WMI в однопоточном приложении

StackOverflow https://stackoverflow.com/questions/3921661

Вопрос

Я пишу приложение в C #, .NET 3.0 в VS2005 с особенностью мониторинга вставки / выброса различных съемных дисков (USB Flash Disks, CD-ROM и т. Д.). Я не хотел использовать WMI, поскольку иногда это может быть неоднозначным (например, он может порождать несколько событий вставки для одного USB-накопителя), поэтому я просто переопределяю WM_DEVICECHECHANGE, чтобы поймать сообщение WM_DEVICECHECHECHECH здесь. Отказ Вчера я столкнулся с проблемой, когда оказалось, что мне придется использовать WMI в любом случае, чтобы получить некоторые неясные детали диска, как серийный номер. Оказывается, что вызывая процедуры WMI изнутри WNDProc бросает разъединенныйContext 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 в отдельном потоке - но затем, ожидая его завершения.

Теперь вопрос: Зачем это работает, а Зачем Это должно быть таким? (или, делает это?)

Я не понимаю факт, чтобы получить разъединенныйContext MDA или RPC_E_WRONG_THREAD в первую очередь. Как работает работает GetDrives() Процедура из кнопки Click Обработчик событий отличается от VNDPROC? Разве они не случаются в той же основной нити моего приложения? Кстати, мое приложение полностью однопоточное, так почему же внезапная ошибка со ссылкой на некоторую «неправильную тему»? Использование WMI подразумевает многопотативное и специальное лечение функций от System.Management?

Тем временем я нашел другой вопрос, связанный с этим MDA, это здесь. Отказ Хорошо, я могу принять это, что вызывая WMI означает создание отдельного потока для базового COM-компонента - но для меня все еще не происходит, почему не требуется волшебство при вызове его после нажатия кнопки, и при вызове Это от WndProc.

Я действительно путаю об этом и ценю некоторое разъяснение по этому вопросу. Есть только несколько хуже, чем наличие решения, а не зная, почему он работает: /

Ура, Александр

Это было полезно?

Решение

Существует довольно длительное обсуждение COM квартир и накачки сообщений здесь. Отказ Но основной интерес к тому, что насос сообщений используется для обеспечения правильной маршалевы вызовов в STA. Поскольку нить UI представляет собой вопрос о том, что сообщения должны быть накажены, чтобы убедиться, что все работает должным образом.

Сообщение WM_DEVICECHANGE может быть отправлено в окно несколько раз. Таким образом, в случае, когда вы вызовите GETDDIVES напрямую, вы эффективно в конечном итоге с рекурсивными вызовами. Поместите точку перерыва на вызов 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 bytes
System.windows.forms.dll! System.windows.forms.nappientwindow.debuggableCallback (System.Intptr HWND, INT MSG, System.intptr wparam, system.intptr lparam) + 0x64 bytes [родной для управляемого перехода
Удалось на родной переход
mscorlib.dll! System.Threading.waithandle.internalwaitone (System.runtime.interopservices.safehandle interiforsafehandle, длинные миллисекондсетимеата, bool hasthreadaftaffinity, bool exitecontext) + 0x2b bytes mscorlib.dll! System.Threading.waithandle.waitone (int milliseCondstimeout, bool exitcontext ) + 0x2D байты
mscorlib.dll! System.Threading.waithandle.waitone () + 0x10 bytes system.management.dll! system.management.mtahelper.createinmta (System.type тип) + 0x17b байты
System.Management.dll! System.management.managementpath.createwbempath (строковый путь) + 0x18 bytes system.management.dll! System.Management.ManagementClass.ManagementClass (строковый путь) + 0x29 байт
Deletemewindowsforms.exe! Deletemewindowsforms.form1.getdrives () Линия 23 + 0x1b bytes c #

Настолько эффективно, что сообщения окна накачаны, чтобы убедиться, что вызовы COM будут правильно маршаллированы, но это имеет побочный эффект вызова вашего WNDProc и GetDrives снова (так как есть ожидающие сообщения WM_DEVICHECHANGE), пока все еще в предыдущих вызове GetDrives. Когда вы используете BeginInvoke, вы удалите этот рекурсивный звонок.

Опять же, положите точку останова на вызов GetDrives и нажмите F5 после первого удара. В следующий раз, подождите секунду или два, затем нажмите F5 снова. Иногда он потерпит неудачу, иногда это не будет, и вы снова ударили свою точку останова. На этот раз ваша CALLSTACK будет включать в себя три вызовы на GetDrives, с последним, вызванным перечислением коллекции дискамивелизатора. Потому что еще раз, сообщения накачаны, чтобы гарантировать, что вызовы маршалеты.

Трудно точно определить, почему MDA Срабатывает, но учитывая рекурсивные вызовы, это разумно предположить, что контекст COM может быть разорван преждевременно, и / или объект собирается до того, как основной COM-объект может быть освобожден.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top