- android - RelativeLayout 背景可绘制重叠内容
- android - 如何链接 cpufeatures lib 以获取 native android 库?
- java - OnItemClickListener 不起作用,但 OnLongItemClickListener 在自定义 ListView 中起作用
- java - Android 文件转字符串
我在 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 调用它有何不同?它们不是发生在我应用程序的同一个主线程上吗?顺便说一句,我的应用程序完全是单线程的,那么为什么突然出现一个错误指的是一些“错误的线程”?使用 WMI 是否意味着多线程和对 System.Management 函数的特殊处理?
与此同时,我发现了另一个与该 MDA 相关的问题,它是 here .好的,我可以认为调用 WMI 意味着为底层 COM 组件创建一个单独的线程 - 但我仍然没有想到为什么在按下按钮后调用它时不需要魔法,而在调用时需要 do-magic它来自 WndProc。
我真的很困惑,希望能就此事作出一些澄清。没有比拥有解决方案却不知道它为什么有效更糟糕的事情了:/
干杯,亚历山大
最佳答案
关于 COM 单元和消息泵的讨论相当长 here .但主要的兴趣点是消息泵用于确保正确编码 STA 中的调用。由于 UI 线程是有问题的 STA,因此需要发送消息以确保一切正常。
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 调用被正确编码,但这具有再次调用 WndProc 和 GetDrives 的副作用(因为有待处理的 WM_DEVICECHANGE 消息),同时仍在之前的 GetDrives 调用中。当您使用 BeginInvoke 时,您删除了这个递归调用。
再次,在 GetDrives 调用上放置一个断点,并在第一次命中后按 F5。下一次,等待一两秒钟,然后再次按 F5。有时会失败,有时不会,您会再次遇到断点。这一次,您的调用堆栈将包括对 GetDrives 的三个调用,最后一个调用由 diskDriveList 集合的枚举触发。因为同样,消息被抽取以确保调用被编码。
很难准确地指出为什么 MDA被触发,但考虑到递归调用,可以合理地假设 COM 上下文可能会过早拆除和/或在释放基础 COM 对象之前收集对象。
关于c# - 在单线程应用程序中调用 WMI 函数时的 DisconnectedContext MDA,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3921661/
C语言sscanf()函数:从字符串中读取指定格式的数据 头文件: ?
最近,我有一个关于工作预评估的问题,即使查询了每个功能的工作原理,我也不知道如何解决。这是一个伪代码。 下面是一个名为foo()的函数,该函数将被传递一个值并返回一个值。如果将以下值传递给foo函数,
CStr 函数 返回表达式,该表达式已被转换为 String 子类型的 Variant。 CStr(expression) expression 参数是任意有效的表达式。 说明 通常,可以
CSng 函数 返回表达式,该表达式已被转换为 Single 子类型的 Variant。 CSng(expression) expression 参数是任意有效的表达式。 说明 通常,可
CreateObject 函数 创建并返回对 Automation 对象的引用。 CreateObject(servername.typename [, location]) 参数 serv
Cos 函数 返回某个角的余弦值。 Cos(number) number 参数可以是任何将某个角表示为弧度的有效数值表达式。 说明 Cos 函数取某个角并返回直角三角形两边的比值。此比值是
CLng 函数 返回表达式,此表达式已被转换为 Long 子类型的 Variant。 CLng(expression) expression 参数是任意有效的表达式。 说明 通常,您可以使
CInt 函数 返回表达式,此表达式已被转换为 Integer 子类型的 Variant。 CInt(expression) expression 参数是任意有效的表达式。 说明 通常,可
Chr 函数 返回与指定的 ANSI 字符代码相对应的字符。 Chr(charcode) charcode 参数是可以标识字符的数字。 说明 从 0 到 31 的数字表示标准的不可打印的
CDbl 函数 返回表达式,此表达式已被转换为 Double 子类型的 Variant。 CDbl(expression) expression 参数是任意有效的表达式。 说明 通常,您可
CDate 函数 返回表达式,此表达式已被转换为 Date 子类型的 Variant。 CDate(date) date 参数是任意有效的日期表达式。 说明 IsDate 函数用于判断 d
CCur 函数 返回表达式,此表达式已被转换为 Currency 子类型的 Variant。 CCur(expression) expression 参数是任意有效的表达式。 说明 通常,
CByte 函数 返回表达式,此表达式已被转换为 Byte 子类型的 Variant。 CByte(expression) expression 参数是任意有效的表达式。 说明 通常,可以
CBool 函数 返回表达式,此表达式已转换为 Boolean 子类型的 Variant。 CBool(expression) expression 是任意有效的表达式。 说明 如果 ex
Atn 函数 返回数值的反正切值。 Atn(number) number 参数可以是任意有效的数值表达式。 说明 Atn 函数计算直角三角形两个边的比值 (number) 并返回对应角的弧
Asc 函数 返回与字符串的第一个字母对应的 ANSI 字符代码。 Asc(string) string 参数是任意有效的字符串表达式。如果 string 参数未包含字符,则将发生运行时错误。
Array 函数 返回包含数组的 Variant。 Array(arglist) arglist 参数是赋给包含在 Variant 中的数组元素的值的列表(用逗号分隔)。如果没有指定此参数,则
Abs 函数 返回数字的绝对值。 Abs(number) number 参数可以是任意有效的数值表达式。如果 number 包含 Null,则返回 Null;如果是未初始化变量,则返回 0。
FormatPercent 函数 返回表达式,此表达式已被格式化为尾随有 % 符号的百分比(乘以 100 )。 FormatPercent(expression[,NumDigitsAfterD
FormatNumber 函数 返回表达式,此表达式已被格式化为数值。 FormatNumber( expression [,NumDigitsAfterDecimal [,Inc
我是一名优秀的程序员,十分优秀!