如何在没有 Windows 窗体的情况下接收即插即用设备通知
Posted
技术标签:
【中文标题】如何在没有 Windows 窗体的情况下接收即插即用设备通知【英文标题】:How to receive Plug & Play device notifications without a windows form 【发布时间】:2011-01-04 21:26:00 【问题描述】:我正在尝试编写一个可以捕获 Windows 消息的类库,以便在设备已连接或移除时通知我。通常,在 Windows 窗体应用程序中,我只会覆盖 WndProc 方法,但在这种情况下没有 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。但这似乎有其自身的问题。以上是关于如何在没有 Windows 窗体的情况下接收即插即用设备通知的主要内容,如果未能解决你的问题,请参考以下文章