Cómo recibir notificaciones de dispositivos Plug & Play sin un formulario de Windows

Intento escribir una biblioteca de clases que pueda captar los mensajes de Windows para notificarme si un dispositivo se ha adjuntado o eliminado. Normalmente, en una aplicación de formularios de Windows simplemente anularía el método WndProc, pero en este caso no existe el método WndProc. ¿Hay alguna otra forma en que pueda recibir los mensajes?

Necesitarás una ventana, no hay forma de evitar eso. Aquí hay una implementación de muestra. Implemente un controlador de eventos para el evento DeviceChangeNotifier.DeviceNotify para recibir notificaciones. Llame al método DeviceChangeNotifier.Start () al comienzo de su progtwig. Llame a DeviceChangeNotifier.Stop () al final de su progtwig. Tenga en cuenta que el evento DeviceNotify se genera en una secuencia de fondo, asegúrese de bloquearlo según sea necesario para mantener su código seguro para la ejecución de subprocesos.

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); } } 

Tengo una clase de comunicación USB que implementa la notificación de cambio de dispositivo de una manera ligeramente diferente si alguien está interesado. Es bastante compacto (sin los comentarios) y no depende de Threading o las cosas OnSourceInitialized y HwndHandler en el cliente. Además, no necesita un Form o ventana como se menciona. Se puede WndProc() cualquier tipo donde pueda anular WndProc() . Yo uso un Control

La muestra contiene solo el código necesario para la notificación y nada más. El código de muestra es C ++ / CLI y aunque no me suscribo a la práctica de poner código ejecutable en archivos de encabezado, en aras de la brevedad, lo hago aquí.

 #pragma once #include  // Declares required datatypes. #include  // Required for WM_DEVICECHANGE messages. #include  // 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 eg 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 eg // 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); ///  /// Declare a delegate for the notification event handler. ///  /// The object where the event handler is attached. /// The event data. public delegate void NotificationEventHandler(Object^ sender, EventArgs^ e); ///  /// Class that generetaes USB Device Change notification events. ///  ///  /// A Form is not necessary. Any type wherein you can override WndProc() can be used. ///  public ref class EventNotifier : public Control { private: ///  /// Raises the NotificationEvent. ///  /// The event data. void RaiseNotificationEvent(EventArgs^ e) { NotificationEvent(this, e); } protected: ///  /// Overrides the base class WndProc method. ///  /// The Windows Message to process.  ///  /// This method receives Windows Messages (WM_xxxxxxxxxx) and /// raises our NotificationEvent as appropriate. Here you should /// add any message filtering (eg for the WM_DEVICECHANGE) and /// preprocessing before raising the event (or not). ///  virtual void WndProc(Message% message) override { if(message.Msg == WM_DEVICECHANGE) { RaiseNotificationEvent(EventArgs::Empty); } __super::WndProc(message); } public: ///  /// Creates a new instance of the EventNotifier class. ///  EventNotifier(void) { RequestNotifications(this->Handle); // Register ourselves as the Windows Message processor. } ///  /// Registers an object, identified by the handle, for /// Windows WM_DEVICECHANGE messages. ///  /// The object's handle. 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; } ///  /// Defines the notification event. ///  virtual event NotificationEventHandler^ NotificationEvent; }; } 

Luego, en el ‘receptor’ (el objeto que se suscribe y consume nuestro NotificationEvent ), todo lo que tiene que hacer es:

 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. } 

En los proyectos de Windows CE / Windows Mobile / SmartDevice, el Form estándar no proporciona una anulación del método WndProc , pero esto se puede lograr haciendo una clase basada en Microsoft.WindowsCE.Forms.MessageWindow , creando un constructor que adopta una forma, mantenga ese formulario en una variable local para que se pueda invocar un método en ese formulario siempre que se detecte el mensaje. Aquí hay una muestra reducida para ilustrar. Espero que esto sea útil para alguien en el mundo de 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; } } }