- 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/
此代码似乎在启用优化的广泛使用的编译器上中断,尽管它在 Visual Studio 中运行良好。 struct foo { foo(int a) { s[0] = '0'+a%10;s[1]
我想要一个图表,其中有一个单线箭头,如下所示: 1 2 3 4 5 或者像这样(其中/假设是一个箭头:)): \/ -----------------
我正在为 Java 编写自定义规则。有两个 Tree.KIND 实例(STRING_LITERAL 和 ASSIGNMENT)需要捕获。有一个特定的行,字符串文字和赋值的逻辑都会引发问题。但 Sona
Rosettacode.org 在 Ruby 中有这个出色的单行 FizzBuzz 解决方案。 1.upto(100){|n|puts'FizzBuzz '[i=n**4%-15,i+13]||n
很多时候我使用了这个命令,它在当前目录打开了一个临时的 HTTP 服务器: python3 -m http.server 现在我需要接收文件,有没有打开ftp服务器的一行命令? 我只是在寻找一个命令行
相关主题 std::unique_ptr, deleters and the Win32 API 要将 Win32 句柄用作 RAII,我可以使用以下行 std::unique_ptr::type,
我认为必须有一个单行 Guava 解决方案来将一个不可变列表转换为另一个不可变列表,但我找不到它。假设我们有以下对象: ImmutableList input = ImmutableList.of("
我有以下 Highcharts ( http://www.highcharts.com ) 散点图。请注意,轴从 -10 开始,到 10 停止,中间为 0。我希望每条 0 线的宽度或颜色都与其他线不同
我有一个项目需要将一个视频文件与另一个音频文件合并。预期的输出是一个视频文件,其中包含来自实际视频的音频和合并后的音频文件。输出视频文件的长度将与实际视频文件的大小相同。 是否有单行 FFMPEG 命
我在 python3 类中有 2 个列表: self.keys = ["a","b","c","d"] self.values = [1,2,3,4] len(self.keys) == len(se
我有一个不同长度的数组列表,我想将它们组合成一个最大维度的矩阵,并在末尾填充零。例如(伪代码): combine( [1,2,3], [4,5]) [[1,2,3],[4,5,0]] 这是我目前的解决
例如,给定 i=5 和 n=8,我想生成 [0;0;0;0;1;0; 0;0]。具体来说,我想生成向量 v 以便: v = zeros(n,1); v(i) = 1; 有没有一种(合理的)方法可以在一
我是一名优秀的程序员,十分优秀!