ScpControl/Usb/PnP/UsbNotifier.cs (161 lines of code) (raw):

using System; using System.ComponentModel; using System.Runtime.InteropServices; namespace ScpControl.Usb.PnP { /// <summary> /// A modified version of the USB HID Component for C#. /// <remarks>http://www.codeproject.com/Articles/18099/A-USB-HID-Component-for-C</remarks> /// </summary> public class UsbNotifier : Win32Usb { /// <summary> /// The product id from the USB device you want to use. /// </summary> public ushort ProductId { get; set; } /// <summary> /// The vendor id from the USB device you want to use. /// </summary> public ushort VendorId { get; set; } /// <summary> /// The device class GUID this notifier will listen for. Defaults to <see cref="Win32Usb.HidGuid" />. /// </summary> public Guid ClassGuid { get; private set; } //events /// <summary> /// This event will be triggered when the device you specified is pluged into your usb port on /// the computer. And it is completly enumerated by windows and ready for use. /// </summary> [Description( "The event that occurs when a usb hid device with the specified vendor id and product id is found on the bus" )] [Category("Embedded Event")] [DisplayName("OnSpecifiedDeviceArrived")] public event EventHandler OnSpecifiedDeviceArrived; /// <summary> /// This event will be triggered when the device you specified is removed from your computer. /// </summary> [Description( "The event that occurs when a usb hid device with the specified vendor id and product id is removed from the bus" )] [Category("Embedded Event")] [DisplayName("OnSpecifiedDeviceRemoved")] public event EventHandler OnSpecifiedDeviceRemoved; /// <summary> /// This event will be triggered when a device is pluged into your usb port on /// the computer. And it is completly enumerated by windows and ready for use. /// </summary> [Description("The event that occurs when a usb hid device is found on the bus")] [Category("Embedded Event")] [DisplayName("OnDeviceArrived")] public event EventHandler OnDeviceArrived; /// <summary> /// This event will be triggered when a device is removed from your computer. /// </summary> [Description("The event that occurs when a usb hid device is removed from the bus")] [Category("Embedded Event")] [DisplayName("OnDeviceRemoved")] public event EventHandler OnDeviceRemoved; /// <summary> /// Registers this application, so it will be notified for usb events. /// </summary> /// <param name="handle">a IntPtr, that is a handle to the application.</param> /// <example> /// This sample shows how to implement this method in your form. /// <code> /// protected override void OnHandleCreated(EventArgs e) /// { /// base.OnHandleCreated(e); /// usb.RegisterHandle(Handle); /// } /// </code> /// </example> public void RegisterHandle(IntPtr handle) { _usbEventHandle = RegisterForUsbEvents(handle, ClassGuid); if (_usbEventHandle == IntPtr.Zero) { throw new InvalidOperationException("Couldn't register for receiving USB events"); } _handle = handle; //Check if the device is already present. CheckDevicePresent(); } /// <summary> /// Unregisters this application, so it won't be notified for usb events. /// </summary> /// <returns>Returns if it wass succesfull to unregister.</returns> public bool UnregisterHandle() { return UnregisterForUsbEvents(_handle); } /// <summary> /// This method will filter the messages that are passed for usb device change messages only. /// And parse them and take the appropriate action /// </summary> /// <param name="msg">The window message received by the window procedure.</param> /// <param name="wParam">The wParam argument.</param> public void ParseMessages(int msg, IntPtr wParam) { if (msg != WM_DEVICECHANGE) return; switch (wParam.ToInt32()) // Check the W parameter to see if a device was inserted or removed { case DEVICE_ARRIVAL: // inserted if (OnDeviceArrived != null) { OnDeviceArrived(this, new EventArgs()); } CheckDevicePresent(); break; case DEVICE_REMOVECOMPLETE: // removed if (OnDeviceRemoved != null) { OnDeviceRemoved(this, new EventArgs()); } CheckDevicePresent(); break; } } /// <summary> /// Checks the devices that are present at the moment and checks if one of those /// is the device you defined by filling in the product id and vendor id. /// </summary> public void CheckDevicePresent() { var specifiedDevice = FindDevice(VendorId, ProductId); // look for the device on the USB bus if (specifiedDevice) // did we find it? { if (OnSpecifiedDeviceArrived == null) return; OnSpecifiedDeviceArrived(this, new EventArgs()); } else { if (OnSpecifiedDeviceRemoved != null) { OnSpecifiedDeviceRemoved(this, new EventArgs()); } } } /// <summary> /// Helper method to return the device path given a DeviceInterfaceData structure and an InfoSet handle. /// Used in 'FindDevice' so check that method out to see how to get an InfoSet handle and a DeviceInterfaceData. /// </summary> /// <param name="hInfoSet">Handle to the InfoSet</param> /// <param name="oInterface">DeviceInterfaceData structure</param> /// <returns>The device path or null if there was some problem</returns> private static string GetDevicePath(IntPtr hInfoSet, ref DeviceInterfaceData oInterface) { uint nRequiredSize = 0; // Get the device interface details if ( !SetupDiGetDeviceInterfaceDetail(hInfoSet, ref oInterface, IntPtr.Zero, 0, ref nRequiredSize, IntPtr.Zero)) { var detailDataBuffer = Marshal.AllocHGlobal((int) nRequiredSize); Marshal.WriteInt32(detailDataBuffer, IntPtr.Size == 4 ? 4 + Marshal.SystemDefaultCharSize : 8); try { if (SetupDiGetDeviceInterfaceDetail(hInfoSet, ref oInterface, detailDataBuffer, nRequiredSize, ref nRequiredSize, IntPtr.Zero)) { var pDevicePathName = new IntPtr(detailDataBuffer.ToInt64() + 4); return Marshal.PtrToStringAnsi(pDevicePathName) ?? string.Empty; } } finally { Marshal.FreeHGlobal(detailDataBuffer); } } return string.Empty; } /// <summary> /// Finds a device given its PID and VID /// </summary> private bool FindDevice(int nVid, int nPid) { var strSearch = string.Format("vid_{0:x4}&pid_{1:x4}", nVid, nPid); // first, build the path search string var gHid = ClassGuid; // next, get the GUID from Windows that it uses to represent the HID USB interface var hInfoSet = SetupDiGetClassDevs(ref gHid, null, IntPtr.Zero, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); // this gets a list of all HID devices currently connected to the computer (InfoSet) try { var oInterface = new DeviceInterfaceData(); // build up a device interface data block oInterface.Size = Marshal.SizeOf(oInterface); // Now iterate through the InfoSet memory block assigned within Windows in the call to SetupDiGetClassDevs // to get device details for each device connected var nIndex = 0; while (SetupDiEnumDeviceInterfaces(hInfoSet, 0, ref gHid, (uint) nIndex, ref oInterface)) // this gets the device interface information for a device at index 'nIndex' in the memory block { var strDevicePath = GetDevicePath(hInfoSet, ref oInterface); // get the device path (see helper method 'GetDevicePath') if (strDevicePath.IndexOf(strSearch, StringComparison.Ordinal) >= 0) // do a string search, if we find the VID/PID string then we found our device! { return true; } nIndex++; // if we get here, we didn't find our device. So move on to the next one. } } catch (Exception) { return false; } finally { // Before we go, we have to free up the InfoSet memory reserved by SetupDiGetClassDevs SetupDiDestroyDeviceInfoList(hInfoSet); } return false; // oops, didn't find our device } #region Private fields private IntPtr _handle; private IntPtr _usbEventHandle; #endregion #region Ctors public UsbNotifier() { ClassGuid = HidGuid; } /// <summary> /// </summary> /// <param name="classGuid">GUID that specifies the device interface class.</param> public UsbNotifier(Guid classGuid) { ClassGuid = classGuid; } /// <summary> /// </summary> /// <param name="vid">Vendor identifier.</param> /// <param name="pid">Product identifier.</param> public UsbNotifier(ushort vid, ushort pid) : this() { VendorId = vid; ProductId = pid; } /// <summary> /// </summary> /// <param name="vid">Vendor identifier.</param> /// <param name="pid">Product identifier.</param> /// <param name="classGuid">GUID that specifies the device interface class.</param> public UsbNotifier(ushort vid, ushort pid, Guid classGuid) : this(vid, pid) { ClassGuid = classGuid; } #endregion } }