Здесь довольно долго обсуждается COM Apartments и прокачка сообщений здесь .Но основной интерес представляет то, что насос сообщений используется для обеспечения правильного маршалинга вызовов в STA.Поскольку поток пользовательского интерфейса является рассматриваемой STA, сообщения должны быть закачаны, чтобы гарантировать, что все работает правильно.
Сообщение WM_DEVICECHANGE может фактически отправляться в окно несколько раз.Таким образом, в случае, когда вы вызываете GetDrives напрямую, вы фактически получаете рекурсивные вызовы.Установите точку останова на вызов GetDrives, а затем подключите устройство для запуска события.
При первом достижении точки останова все в порядке.Теперь нажмите F5, чтобы продолжить, и вы достигнете точки останова во второй раз.На этот раз стек вызовов выглядит примерно так:
[В режиме ожидания, ожидания или присоединения] DeleteMeWindowsForms.exe! DeleteMeWindowsForms.Form1.WndProc (ref System.Windows.Forms.Message m) Строка 46C # 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.InteropSerlele waitSafele waitSavices.afele, длинные миллисекундыTimeout, bool hasThreadAffinity, bool exitContext) + 0x2b байт mscorlib.dll! System.Threading.WaitHandle.WaitOne (int millisecondsTimeout, bool exitContext) + 0x2d байт
mscorlib.dll! System.Threading.WaitHandle.WaitOne () + 0x10 байт System.Management.dll! System.Management.MTAHelper.CreateInMTA (тип System.Type) + 0x17b байт
система.Management.dll! System.Management.ManagementPath.CreateWbemPath (путь строки) + 0x18 байт System.Management.dll! System.Management.ManagementClass.ManagementClass (путь строки) + 0x29 байт
DeleteMeWindowsForms.exe! DeleteMeWindowsForms.Form1.GetDrives () Строка 23 + 0x1b байтов C #
Таким образом, сообщения окна перекачиваются так, чтобы обеспечить правильное распределение вызовов COM, но это побочный эффект повторного вызова WndProc и GetDrives (какесть ожидающие сообщения WM_DEVICECHANGE), пока они находятся в предыдущем вызове GetDrives.Когда вы используете BeginInvoke, вы удаляете этот рекурсивный вызов.
Снова установите точку останова на вызов GetDrives и нажмите F5 после первого нажатия.В следующий раз подождите секунду или две, затем снова нажмите F5.Иногда это терпит неудачу, иногда нет, и вы снова достигнете своей точки останова.На этот раз ваш стек вызовов будет включать три вызова GetDrives, причем последний вызов будет вызван перечислением коллекции diskDriveList.Потому что сообщения перекачиваются, чтобы гарантировать, что вызовы маршалируются.
Трудно точно определить, почему вызван MDA , но, учитывая рекурсивные вызовы, разумно предположить, что COM-контекст можетбыть преждевременно снятым и / или объект будет собран до того, как основной объект COM может быть освобожден.