// 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调用它时需要执行魔术.
我真的很困惑,对此感到有些澄清.只有几个更糟糕的事情,而不是解决方案,而不知道为什么它的工作原理:
干杯,
克瓦希
解决方法
WM_DEVICECHANGE消息实际上可以多次发送到窗口.因此,在直接调用GetDrives的情况下,您有效地终止了递归调用.在GetDrives调用上放置一个断点,然后附加一个设备来触发事件.
你第一次打破断点,一切都很好.现在按F5继续,你会再次打破断点.这次调用堆栈是这样的:
[In a sleep,wait,or join]
DeleteMeWindowsForms.exe!DeleteMeWindowsForms.Form1.WndProc(ref System.Windows.Forms.Message m) Line 46 C#
System.Windows.Forms.dll!System.Windows.Forms.Control.ControlNativeWindow.OnMessage(ref System.Windows.Forms.Message m) + 0x13 bytes
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.NativeWindow.DebuggableCallback(system.intPtr hWnd,int msg,system.intPtr wparam,system.intPtr lparam) + 0x64 bytes
[Native to Managed Transition]
[Managed to Native Transition]
mscorlib.dll!System.Threading.WaitHandle.InternalWaitOne(System.Runtime.InteropServices.SafeHandle waitableSafeHandle,long millisecondsTimeout,bool hasThreadAffinity,bool exitContext) + 0x2b bytes
mscorlib.dll!System.Threading.WaitHandle.WaitOne(int millisecondsTimeout,bool exitContext) + 0x2d bytes
mscorlib.dll!System.Threading.WaitHandle.WaitOne() + 0x10 bytes
System.Management.dll!System.Management.MTAHelper.CreateInMTA(System.Type type) + 0x17b bytes
System.Management.dll!System.Management.ManagementPath.CreateWbemPath(string path) + 0x18 bytes
System.Management.dll!System.Management.ManagementClass.ManagementClass(string path) + 0x29 bytes
DeleteMeWindowsForms.exe!DeleteMeWindowsForms.Form1.GetDrives() Line 23 + 0x1b bytes C#
因此,窗口消息被有效地抽取以确保COM调用已正确编组,但是这仍然会在以前的GetDrives调用中再次调用您的WndProc和GetDrives(因为还有待处理的WM_DEVICECHANGE消息).当您使用BeginInvoke时,您将删除此递归调用.
再次,在GetDrives调用上放置一个断点,并在第一次被击中后按F5.下次再等一两秒再按F5.有时它会失败,有时它不会,你会再次打破你的断点.这一次,您的呼叫堆栈将包含三次GetDrives调用,最后一次由diskDriveList集合枚举触发.因为再次,消息被泵送以确保呼叫被封送.
很难准确地确定为什么MDA被触发,但是给定递归调用,合理假设COM上下文可能会过早地被破坏,和/或一个对象被收集,然后才能释放底层的COM对象.
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。