Ale*_*der 10 c# wmi visual-studio-2005 mda
我在VS2005的C#,.NET 3.0中编写了一个应用程序,其功能是监视各种可移动驱动器(USB闪存盘,CD-ROM等)的插入/弹出.我不想使用WMI,因为它可以有时会产生歧义(例如,它可以生成一个单一的USB驱动器插入多个事件),所以我干脆重写我的MainForm赶上WM_DEVICECHANGE消息的WndProc,因为建议在这里.昨天我遇到了一个问题,结果发现我将不得不使用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();
Run Code Online (Sandbox Code Playgroud)
这基本上意味着在一个单独的线程上运行WMI相关的过程 - 但是,等待它完成.
现在,问题是:它为什么有效,为什么必须这样?(或者,是吗?)
我不了解首先获取DisconnectedContext MDA或RPC_E_WRONG_THREAD的事实.GetDrives()按钮单击事件处理程序的运行过程与从WndProc调用它的步骤有何不同?它们不是发生在我的应用程序的同一主线程上吗?顺便说一句,我的应用程序是完全单线程的,那么为什么所有突然出现错误都指的是一些"错误的线程"?使用WMI是否意味着System.Management中的多线程和函数的特殊处理?
与此同时,我发现了另一个与MDA有关的问题,就在这里.好吧,我可以认为调用WMI意味着为底层COM组件创建一个单独的线程 - 但是我仍然没有想到为什么在按下按钮后调用它时需要使用no-magic并且在调用时需要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 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 [本机到托管转换]
[管理到本机转换]
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系统 .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(字符串路径)+ 0x29字节
DeleteMeWindowsForms.exe!DeleteMeWindowsForms.Form1.GetDrives()第23行+ 0x1b字节C#
因此有效地抽取窗口消息以确保COM调用被正确编组,但这会产生副作用,即在仍然在之前的GetDrives调用中再次调用WndProc和GetDrives(因为有待处理的WM_DEVICECHANGE消息).使用BeginInvoke时,将删除此递归调用.
再次,在GetDrives调用上放置一个断点,并在第一次触发后按F5.下一次,等一两秒再按F5.有时它会失败,有时它会失败,你会再次击中你的断点.这次,您的callstack将包含三次对GetDrives的调用,最后一次调用由diskDriveList集合的枚举触发.因为再一次,消息被泵送以确保调用被封送.
很难确切地确定为什么MDA被触发,但是给定递归调用,可以合理地假设COM上下文可能过早地被拆除和/或在可以释放底层COM对象之前收集对象.
| 归档时间: |
|
| 查看次数: |
3949 次 |
| 最近记录: |