c# – 在单线程应用程序中调用WMI函数时,DisconnectedContext MD
我在VS2005中的C#.NET 3.0中编写了一个应用程序,具有监控各种可移动驱动器(USB闪存盘,CD-ROM等)的插入/弹出功能.我不想使用WMI,因为它可能有时是模糊的(例如,它可以为单个USB驱动器产生多个插入事件),所以我只是覆盖我的主窗体的WndProc来捕获WM_DEVICECHANGE消息,如建议
here.昨天我遇到问题,原来我将不得不使用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中调用它不同?它们不是发生在我应用程序的同一主线上吗? BTW,我的应用程序是完全单线程的,所以为什么所有的突然一个错误指的是一些“错误的线程”?使用WMI是否意味着来自System.Management的多线程和功能的特殊处理? 在此期间,我发现了另一个与该MDA相关的问题,它是here.可以,我可以认为,调用WMI意味着为基础COM组件创建一个单独的线程,但是我仍然不会发生,为什么不需要魔术在按下按钮后调用它,并在从WndProc调用它时需要执行魔术. 我真的很困惑,对此感到有些澄清.只有几个更糟糕的事情,而不是解决方案,而不知道为什么它的工作原理: 干杯, 解决方法
有一个相当长的讨论COM公寓和消息抽取
here.但主要的兴趣是消息泵是用来确保STA中的呼叫被正确的封送.由于UI线程是有问题的STA,所以需要抽取消息来确保一切正常.
WM_DEVICECHANGE消息实际上可以多次发送到窗口.因此,在直接调用GetDrives的情况下,您有效地终止了递归调用.在GetDrives调用上放置一个断点,然后附加一个设备来触发事件. 你第一次打破断点,一切都很好.现在按F5继续,你会再次打破断点.这次调用堆栈是这样的:
因此,窗口消息被有效地抽取以确保COM调用已正确编组,但是这仍然会在以前的GetDrives调用中再次调用您的WndProc和GetDrives(因为还有待处理的WM_DEVICECHANGE消息).当您使用BeginInvoke时,您将删除此递归调用. 再次,在GetDrives调用上放置一个断点,并在第一次被击中后按F5.下次再等一两秒再按F5.有时它会失败,有时它不会,你会再次打破你的断点.这一次,您的呼叫堆栈将包含三次GetDrives调用,最后一次由diskDriveList集合枚举触发.因为再次,消息被泵送以确保呼叫被封送. 很难准确地确定为什么MDA被触发,但是给定递归调用,合理假设COM上下文可能会过早地被破坏,和/或一个对象被收集,然后才能释放底层的COM对象. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |