【问题标题】:Catch USB plug and unplug event System.InvalidCastException捕获 USB 插拔事件 System.InvalidCastException
【发布时间】:2018-10-01 20:23:32
【问题描述】:

我正在尝试使用 WinForm 桌面 C# 应用程序检测 USB 设备插入和移除:

 public Form1()
    {
        InitializeComponent();
        USB();
    }

然后:

private void USB()
{
     WqlEventQuery weqQuery = new WqlEventQuery();
     weqQuery.EventClassName = "__InstanceOperationEvent";
     weqQuery.WithinInterval = new TimeSpan(0, 0, 3);
     weqQuery.Condition = @"TargetInstance ISA 'Win32_DiskDrive'";  
     var m_mewWatcher = new ManagementEventWatcher(weqQuery);
     m_mewWatcher.EventArrived += new EventArrivedEventHandler(m_mewWatcher_EventArrived);
     m_mewWatcher.Start();           
}

和:

static void m_mewWatcher_EventArrived(object sender, EventArrivedEventArgs e)
    {
        bool bUSBEvent = false;
        foreach (PropertyData pdData in e.NewEvent.Properties)
        {
            ManagementBaseObject mbo = (ManagementBaseObject)e.NewEvent.Properties["TargetInstance"].Value;
           // ManagementBaseObject mbo = (ManagementBaseObject)pdData.Value;
            if (mbo != null)
            {
                foreach (PropertyData pdDataSub in mbo.Properties)
                {
                    if (pdDataSub.Name == "InterfaceType" && pdDataSub.Value.ToString() == "USB")
                    {
                        bUSBEvent = true;
                        break;
                    }
                }

                if (bUSBEvent)
                {
                    if (e.NewEvent.ClassPath.ClassName == "__InstanceCreationEvent")
                    {
                        MessageBox.Show ("USB was plugged in");
                    }
                    else if (e.NewEvent.ClassPath.ClassName == "__InstanceDeletionEvent")
                    {
                        MessageBox.Show("USB was plugged out");
                    }
                }
            }
        }
    }

但是当检测到 USB 更改时,ManagementBaseObject mbo = (ManagementBaseObject)pdData.Value; 出现异常:

“System.InvalidCastException”类型的异常发生在 Controller.exe 但未在用户代码中处理

附加信息:无法转换类型为“System.UInt64”的对象 键入“System.Management.ManagementBaseObject”。

编辑:

using System;
using System.Windows.Forms;
using System.Management;

namespace test
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            WqlEventQuery query = new WqlEventQuery()
            {
                EventClassName = "__InstanceOperationEvent",
                WithinInterval = new TimeSpan(0, 0, 3),
                Condition = @"TargetInstance ISA 'Win32_DiskDrive'"
            };

            using (ManagementEventWatcher MOWatcher = new ManagementEventWatcher(query))
            {
                MOWatcher.EventArrived += new EventArrivedEventHandler(DeviceInsertedEvent);
                MOWatcher.Start();
            }
        }

        private void DeviceInsertedEvent(object sender, EventArrivedEventArgs e)
        {
            using (ManagementBaseObject MOBbase = (ManagementBaseObject)e.NewEvent.Properties["TargetInstance"].Value)
            {
                bool DriveArrival = false;
                string EventMessage = string.Empty;
                string oInterfaceType = MOBbase.Properties["InterfaceType"]?.Value.ToString();

                if (e.NewEvent.ClassPath.ClassName.Equals("__InstanceDeletionEvent"))
                {
                    DriveArrival = false;
                    EventMessage = oInterfaceType + " Drive removed";
                }
                else
                {
                    DriveArrival = true;
                    EventMessage = oInterfaceType + " Drive inserted";
                }
                EventMessage += ": " + MOBbase.Properties["Caption"]?.Value.ToString();
                this.BeginInvoke((MethodInvoker)delegate { this.UpdateUI(DriveArrival, EventMessage); });
            }
        }

        private void UpdateUI(bool IsDriveInserted, string message)
        {
            if (IsDriveInserted)
            {
                this.label1.Text = message;
            }             
            else
            {
                this.label1.Text = message;
            }                
        }
    }
}

【问题讨论】:

  • pdData.Value 可以是:UInt16 for EventTypeUInt8 for SECURITY_DESCRIPTORUInt64 for TIME_CREATED。这些是 e.NewEvent 公开的 3 个属性。当然,您不能将其中任何一个投射到ManagementBaseObject。您可以强制转换 e.NewEvent,但它已经派生自该基础对象,因此您不会获得它的另一个“视图”。
  • @Jimi 我不确定我是否做对了,你的意思是我必须解析,就像这个ManagementBaseObject mbo = (ManagementBaseObject)UInt16.Parse(pdData.Value);,但是如果它是字符串的对象。所以我仍然不确定如何获得想要的结果
  • 啊,不,对不起。我没有注意到您更改了 WMI 观察程序方法。你的基类现在是TargetInstance。那是一个完整的ManagementObject。属性名当然是“TargetInstance”,你可以照常解析。它将包含Win32_DiskDrive 基本信息。
  • 好吧,更明确地说,您可以这样转换:ManagementBaseObject MOBbase = (ManagementBaseObject)e.NewEvent.Properties["TargetInstance"].Value;。然后,您有 51 个新属性,它们映射到 Win32_DiskDrive MO 类型,具有常用属性“Caption”、“DeviceID”、“Capabilities”等。
  • @Jimi 我已经将它从ManagementBaseObject mbo = (ManagementBaseObject)pdData.Value; 更改为ManagementBaseObject mbo = (ManagementBaseObject)e.NewEvent.Properties["TargetInstance"].Value;,现在它甚至没有检测到插拔事件

标签: c# winforms exception usb system.management


【解决方案1】:

这里,ManagementEventWatcher 是在 Button.Click() 事件上初始化的。当然,你可以在别处初始化它。以Form.Load() 为例。

订阅了ManagementEventWatcher.EventArrived 事件,将委托设置为private void DeviceInsertedEvent()。观看过程是使用ManagementEventWatcher.Start() (MOWatcher.Start();)

当通知事件时,EventArrivedEventArgs e.NewEvent.Properties["TargetInstance"].Value 将设置为 ManagementBaseObject,它将引用 Win32_DiskDrive WMI/CIM 类。
阅读文档以了解此类中可用的信息。

此事件不会在 UI 线程中引发。要通知主 UI 界面事件的性质,我们需要 .Invoke() 该线程中的一个方法。

this.BeginInvoke((MethodInvoker)delegate { this.UpdateUI(DriveArrival, EventMessage); });

在这里,private void UpdateUI() 方法被调用,将 UI 更新委托给在 UI 线程中执行的方法。

更新:
新增RegisterWindowMessage()注册QueryCancelAutoPlay,防止我们的Form前面弹出AutoPlay窗口,窃取焦点。

结果的视觉样本:

[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
internal static extern uint RegisterWindowMessage(string lpString);

private uint CancelAutoPlay = 0;

private void button1_Click(object sender, EventArgs e)
{
    WqlEventQuery query = new WqlEventQuery() {
        EventClassName = "__InstanceOperationEvent",
        WithinInterval = new TimeSpan(0, 0, 3),
        Condition = @"TargetInstance ISA 'Win32_DiskDrive'"
    };

    ManagementScope scope = new ManagementScope("root\\CIMV2");
    using (ManagementEventWatcher MOWatcher = new ManagementEventWatcher(query))
    {
        MOWatcher.Options.Timeout = ManagementOptions.InfiniteTimeout;
        MOWatcher.EventArrived += new EventArrivedEventHandler(DeviceChangedEvent);
        MOWatcher.Start();
    }
}

private void DeviceChangedEvent(object sender, EventArrivedEventArgs e)
{
    using (ManagementBaseObject MOBbase = (ManagementBaseObject)e.NewEvent.Properties["TargetInstance"].Value)
    {
        bool DriveArrival = false;
        string EventMessage = string.Empty;
        string oInterfaceType = MOBbase.Properties["InterfaceType"]?.Value.ToString();

        if (e.NewEvent.ClassPath.ClassName.Equals("__InstanceDeletionEvent"))
        {
            DriveArrival = false;
            EventMessage = oInterfaceType + " Drive removed";
        }
        else
        {
            DriveArrival = true;
            EventMessage = oInterfaceType + " Drive inserted";
        }
        EventMessage += ": " + MOBbase.Properties["Caption"]?.Value.ToString();
        this.BeginInvoke((MethodInvoker)delegate { this.UpdateUI(DriveArrival, EventMessage); });
    }
}


private void UpdateUI(bool IsDriveInserted, string message)
{
    if (IsDriveInserted)
        this.lblDeviceArrived.Text = message;
    else
        this.lblDeviceRemoved.Text = message;
}


[SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
protected override void WndProc(ref Message m)
{
    base.WndProc(ref m);

    if (CancelAutoPlay == 0)
        CancelAutoPlay = RegisterWindowMessage("QueryCancelAutoPlay");

    if ((int)m.Msg == CancelAutoPlay) { m.Result = (IntPtr)1; }
}

【讨论】:

  • 好吧,我已经尝试过您的代码,没有任何更改,包括按钮。第一次插入时,在 UI 标签中显示IDE Drive inserted: HGST HTS541010A9E680,与第一次拔出相同,由于某种原因在两种情况下都是“驱动器已插入”,但更重要的是,它只检测一次 USB 更改事件,甚至没有插入和拔出在一个调试会话中检测,仅在加载后一次,然后没有插入和拔出 USB。它是相等的代码,没有任何改变,我不确定我做错了什么=(
  • 好吧,让我们来看看:) 首先,您使用的是什么框架版本(这里是 FW 4.7.1,VS 15.8.4)?然后,查看您的项目属性中没有Prefer 32-bit。在您的问题中发布您的最后一个代码。您正在使用的整个表单(当然,设计器除外)。
  • 添加了代码,.NET Framework 4.5.2C# 版本v4.0.30319,是的,在项目属性构建中是32-bit,我没有选中Prefer 32-bit,但结果是一样的,这确实不影响特定问题
  • 我无法重现该问题。我已经复制了您的 form1 表单,使用 .Net 4.5.2 创建了一个项目。它像我的一样运行。每次插入 USB 设备(相同或不同)时,都会通知该事件并显示 AutoRun 窗口。在我的项目中,我禁用了它,但它不会改变结果。尝试清理解决方案/重建解决方案(不是项目)并重新启动。您当前的代码正在运行。此外,检查您正在测试的 USB 驱动器。在多次插入之后,他们可能会遇到(经典)问题。当 Windows 有时要求您修复设备而您没有修复时,它就会出现。
  • 好吧,我已经用这个相同的代码清理、重建并创建了新项目,没有任何帮助。我刚刚看到它,它显示Drive inserted 和更新的Drive removed 并且只提供一次事件检测,没有任何USB操作,但在3秒后点击按钮(我没有按住按钮),并且然后什么都没有
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-16
相关资源
最近更新 更多