微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

c# – 在单线程应用程序中调用WMI函数时,DisconnectedContext MDA

我在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继续,你会再次打破断点.这次调用堆栈是这样的:

[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 举报,一经查实,本站将立刻删除。

相关推荐


原文地址:http://msdn.microsoft.com/en-us/magazine/cc163791.aspx 原文发布日期: 9/19/2005 原文已经被 Microsoft 删除了,收集过程中发现很多文章图都不全,那是因为原文的图都不全,所以特收集完整全文。 目录 前言 CLR启动程序
前言 随着近些年微服务的流行,有越来越多的开发者和团队所采纳和使用,它的确提供了很多的优势也解决了很多的问题,但是我们也知道也并不是银弹,提供优势的同时它也给我们的开发人员和团队也带来了很多的挑战。 为了迎接或者采用这些新技术,开发团队需要更加注重一些流程或工具的使用,这样才能更好的适应这些新技术所
最近因为比较忙,好久没有写博客了,这篇主要给大家分享一下PLINQ中的分区。上一篇介绍了并行编程,这边详细介绍一下并行编程中的分区和自定义分区。 先做个假设,假设我们有一个200Mb的文本文件需要读取,怎么样才能做到最优的速度呢?对,很显然就是拆分,把文本文件拆分成很多个小文件,充分利用我们计算机中
在多核CPU在今天和不久的将来,计算机将拥有更多的内核,Microsoft为了利用这个硬件特性,于是在Visual Studio 2010 和 .NET Framework 4的发布及以上版本中,添加了并行编程这个新特性,我想它以后势必会改变我们的开发方式。 在以前或者说现在,我们在并行开发的时候可
c语言输入成绩怎么判断等级
字符型数据在内存中的存储形式是什么
c语言怎么求字符串的长度并输出
c语言函数的三种调用方式是什么
c语言中保留两位小数怎么表示
double的输入格式符是什么