【问题标题】:How to receive Plug & Play device notifications without a windows form如何在没有 Windows 窗体的情况下接收即插即用设备通知
【发布时间】:2011-01-04 21:26:00
【问题描述】:

我正在尝试编写一个可以捕获 Windows 消息的类库,以便在设备已连接或移除时通知我。通常,在 Windows 窗体应用程序中,我只会覆盖 WndProc 方法,但在这种情况下没有 WndProc 方法。有没有其他方法可以获取消息?

【问题讨论】:

    标签: c# overriding console-application winforms wndproc


    【解决方案1】:

    你需要一扇窗户,这是没有办法的。这是一个示例实现。为 DeviceChangeNotifier.DeviceNotify 事件实现事件处理程序以获取通知。在程序开始时调用 DeviceChangeNotifier.Start() 方法。在程序结束时调用 DeviceChangeNotifier.Stop()。请注意,DeviceNotify 事件是在后台线程上引发的,请务必根据需要锁定以保持代码线程安全。

    using System;
    using System.Windows.Forms;
    using System.Threading;
    
    class DeviceChangeNotifier : Form {
      public delegate void DeviceNotifyDelegate(Message msg);
      public static event DeviceNotifyDelegate DeviceNotify;
      private static DeviceChangeNotifier mInstance;
    
      public static void Start() {
        Thread t = new Thread(runForm);
        t.SetApartmentState(ApartmentState.STA);
        t.IsBackground = true;
        t.Start();
      }
      public static void Stop() {
        if (mInstance == null) throw new InvalidOperationException("Notifier not started");
        DeviceNotify = null;
        mInstance.Invoke(new MethodInvoker(mInstance.endForm));
      }
      private static void runForm() {
        Application.Run(new DeviceChangeNotifier());
      }
    
      private void endForm() {
        this.Close();
      }
      protected override void SetVisibleCore(bool value) {
        // Prevent window getting visible
        if (mInstance == null) CreateHandle();
        mInstance = this;
        value = false;
        base.SetVisibleCore(value);
      }
      protected override void WndProc(ref Message m) {
        // Trap WM_DEVICECHANGE
        if (m.Msg == 0x219) {
          DeviceNotifyDelegate handler = DeviceNotify;
          if (handler != null) handler(m);
        }
        base.WndProc(ref m);
      }
    }
    

    【讨论】:

    • 我看到你使用.Invoke。如果委托不在 UI 中,这是否有效?
    • @nobugz,我正在使用它并且它可以工作,但由于某种原因,我每次插入设备时都会收到两条设备附加消息。知道为什么吗?
    • @jordan - 注意消息的 wparam 值。
    • 我是个白痴,两次调用了EventHandler...抱歉浪费你的时间
    • 谢谢!你又来了!
    【解决方案2】:

    在 Windows CE / Windows Mobile / SmartDevice 项目中,标准 Form 不提供对 WndProc 方法的覆盖,但这可以通过创建基于 Microsoft.WindowsCE.Forms.MessageWindow 的类来实现,创建一个构造函数表单,将该表单保存在局部变量中,以便在检测到消息时调用该表单上的方法。这是一个按比例缩小的示例来说明。希望这对 CE / Windows Mobile 世界中的某些人有所帮助。

      public class MsgWindow : Microsoft.WindowsCE.Forms.MessageWindow {
    
        public const int WM_SER = 0x500;
        public const int WM_SER_SCANDONE = WM_SER + 0;
    
        frmMain msgform { get; set; }
    
        public MsgWindow(frmMain msgform) {
          this.msgform = msgform;
        }
    
        protected override void WndProc(ref Microsoft.WindowsCE.Forms.Message m) {
          switch (m.Msg) {
            case WM_SER_SCANDONE:
              this.msgform.RespondToMessage(WM_SER_SCANDONE);
              break;
            default:
              break;
          }
          base.WndProc(ref m);
        }
    
      }
    
      public partial class frmMain : Form {
    
        public frmMain() {
          InitializeComponent();
        }
    
        public void RespondToMessage(int nMsg) {
          try {
            switch (nMsg) {
              case MsgWindow.WM_SER_SCANDONE:
                // do something here based on the message
                break;
              default:
                break;
            }
          } catch (Exception ex) {
            MessageBox.Show(string.Format("{0} - {1}", ex.Message, ex.ToString()), "RespondToMessage() Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1);
            // throw;
          }
        }
    
      }
    

    【讨论】:

      【解决方案3】:

      我有一个有效的 USB 通信类,如果有人感兴趣,它会以稍微不同的方式实现设备更改通知。它非常紧凑(没有 cmets)并且不依赖于客户端中的 Threading 或 OnSourceInitialized 和 HwndHandler 的东西。此外,您不需要提到的 Form 或 Window。可以使用任何可以覆盖WndProc() 的类型。我使用Control。

      该示例仅包含通知所需的代码,没有其他内容。示例代码是 C++/CLI,虽然我不赞成将可执行代码放在头文件中的做法,但为了简洁起见,我在这里这样做。

      #pragma once
      
      #include <Windows.h>    // Declares required datatypes.
      #include <Dbt.h>        // Required for WM_DEVICECHANGE messages.
      #include <initguid.h>   // Required for DEFINE_GUID definition (see below).
      
      namespace USBComms 
      {
          using namespace System;
          using namespace System::Runtime::InteropServices;
          using namespace System::Windows;
          using namespace System::Windows::Forms;
      
          // This function is required for receieving WM_DEVICECHANGE messages.
          // Note: name is remapped "RegisterDeviceNotificationUM"
          [DllImport("user32.dll" , CharSet = CharSet::Unicode, EntryPoint="RegisterDeviceNotification")]                 
          extern "C" HDEVNOTIFY WINAPI RegisterDeviceNotificationUM(
              HANDLE hRecipient,
              LPVOID NotificationFilter,
              DWORD Flags);
      
          // Generic guid for usb devices (see e.g. http://msdn.microsoft.com/en-us/library/windows/hardware/ff545972%28v=vs.85%29.aspx).
          // Note: GUIDs are device and OS specific and may require modification. Using the wrong guid will cause notification to fail.
          // You may have to tinker with your device to find the appropriate GUID. "hid.dll" has a function `HidD_GetHidGuid' that returns
          // "the device interfaceGUID for HIDClass devices" (see http://msdn.microsoft.com/en-us/library/windows/hardware/ff538924%28v=vs.85%29.aspx).
          // However, testing revealed it does not always return a useful value. The GUID_DEVINTERFACE_USB_DEVICE value, defined as
          // {A5DCBF10-6530-11D2-901F-00C04FB951ED}, has worked with cell phones, thumb drives, etc. For more info, see e.g.
          // http://msdn.microsoft.com/en-us/library/windows/hardware/ff553426%28v=vs.85%29.aspx. 
          DEFINE_GUID(GUID_DEVINTERFACE_USB_DEVICE, 0xA5DCBF10L, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED);
      
          /// <summary>
          /// Declare a delegate for the notification event handler.
          /// </summary>
          /// <param name="sender">The object where the event handler is attached.</param>
          /// <param name="e">The event data.</param>
          public delegate void NotificationEventHandler(Object^ sender, EventArgs^ e);
      
          /// <summary>
          /// Class that generetaes USB Device Change notification events.
          /// </summary>
          /// <remarks>
          /// A Form is not necessary. Any type wherein you can override WndProc() can be used.
          /// </remarks>
          public ref class EventNotifier : public Control
          {
          private:
              /// <summary>
              /// Raises the NotificationEvent.
              /// </summary>
              /// <param name="e">The event data.</param>
              void RaiseNotificationEvent(EventArgs^ e) {
                  NotificationEvent(this, e);
              }
      
          protected:
              /// <summary>
              /// Overrides the base class WndProc method.
              /// </summary>
              /// <param name="message">The Windows Message to process. </param>
              /// <remarks>
              /// This method receives Windows Messages (WM_xxxxxxxxxx) and
              /// raises our NotificationEvent as appropriate. Here you should
              /// add any message filtering (e.g. for the WM_DEVICECHANGE) and
              /// preprocessing before raising the event (or not).
              /// </remarks>
              virtual void WndProc(Message% message) override {
                  if(message.Msg == WM_DEVICECHANGE)
                  {
                      RaiseNotificationEvent(EventArgs::Empty);
                  }
                  __super::WndProc(message);
              }
      
          public:
              /// <summary>
              /// Creates a new instance of the EventNotifier class.
              /// </summary>
              EventNotifier(void) {
                  RequestNotifications(this->Handle); // Register ourselves as the Windows Message processor.
              }
      
              /// <summary>
              /// Registers an object, identified by the handle, for
              /// Windows WM_DEVICECHANGE messages.
              /// </summary>
              /// <param name="handle">The object's handle.</param>
              bool RequestNotifications(IntPtr handle) {
                  DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
      
                  ZeroMemory(&NotificationFilter, sizeof(NotificationFilter));
                  NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
                  NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
                  NotificationFilter.dbcc_reserved = 0;
                  NotificationFilter.dbcc_classguid = GUID_DEVINTERFACE_USB_DEVICE;
                  return RegisterDeviceNotificationUM((HANDLE)handle, &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE) != NULL;
              }
      
              /// <summary>
              /// Defines the notification event.
              /// </summary>
              virtual event NotificationEventHandler^ NotificationEvent;
          };
      }
      

      然后,在'receiver'(订阅并消费我们的NotificationEvent的对象)中,你所要做的就是:

      void Receiver::SomeFunction(void)
      {
          USBComms::EventNotifier usb = gcnew USBComms::EventNotifier();
      
          usb->NotificationEvent += gcnew USBComms::NotificationEventHandler(this, &Receiver::USBEvent);
      }
      
      void Receiver::USBEvent(Object^ sender, EventArgs^ e)
      {
          // Handle the event notification as appropriate.
      }
      

      【讨论】:

      • Form 是 Control 的后代,并且 control 无论如何都会包装一个 win32 窗口实例。我发现的唯一非基于窗口的方法是使用 ManagementEventWatcher。但这似乎有其自身的问题。
      猜你喜欢
      • 2021-04-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-18
      • 2021-10-05
      相关资源
      最近更新 更多