unsafe static IntPtr DeviceMonitorWindowProc(IntPtr window, uint message, IntPtr wParam, IntPtr lParam) { if (message == NativeMethods.WM_DEVICECHANGE) { var ev = (NativeMethods.WM_DEVICECHANGE_wParam)(int)(long) wParam; HidSharpDiagnostics.Trace("Received a device change event, {0}.", ev); var eventArgs = (NativeMethods.DEV_BROADCAST_HDR *)(void *) lParam; if (ev == NativeMethods.WM_DEVICECHANGE_wParam.DBT_DEVICEARRIVAL || ev == NativeMethods.WM_DEVICECHANGE_wParam.DBT_DEVICEREMOVECOMPLETE) { if (eventArgs->DeviceType == NativeMethods.DBT_DEVTYP_DEVICEINTERFACE) { var diEventArgs = (NativeMethods.DEV_BROADCAST_DEVICEINTERFACE *)eventArgs; if (diEventArgs->ClassGuid == NativeMethods.HidD_GetHidGuid()) { DeviceListDidChange(ref _hidNotifyObject); } else if (diEventArgs->ClassGuid == NativeMethods.GuidForBluetoothLEDevice) { DeviceListDidChange(ref _bleNotifyObject); } } } else if (ev == NativeMethods.WM_DEVICECHANGE_wParam.DBT_CUSTOMEVENT) { if (eventArgs->DeviceType == NativeMethods.DBT_DEVTYP_HANDLE) { var handleEventArgs = (NativeMethods.DEV_BROADCAST_HANDLE *)eventArgs; if (handleEventArgs->EventGuid == NativeMethods.GuidForBluetoothHciEvent) { var hciEvent = (NativeMethods.BTH_HCI_EVENT_INFO *) & handleEventArgs->Data[0]; HidSharpDiagnostics.Trace("Bluetooth HCI event: address {0:X}, type {1}, connected {2}", hciEvent->bthAddress, hciEvent->connectionType, hciEvent->connected); } else if (handleEventArgs->EventGuid == NativeMethods.GuidForBluetoothRadioInRange) { var radioInRange = (NativeMethods.BTH_RADIO_IN_RANGE *) & handleEventArgs->Data[0]; HidSharpDiagnostics.Trace("Radio in range event: address {0:X}, flags {1}, class {2}, name '{3}{4}'", radioInRange->deviceInfo.address, radioInRange->deviceInfo.flags, radioInRange->deviceInfo.classOfDevice, (char)radioInRange->deviceInfo.name[0], (char)radioInRange->deviceInfo.name[1]); } else if (handleEventArgs->EventGuid == NativeMethods.GuidForBluetoothRadioOutOfRange) { var address = (NativeMethods.BLUETOOTH_ADDRESS *) & handleEventArgs->Data[0]; HidSharpDiagnostics.Trace("Radio out of range event: address {0:X}", address->Addr); } else { HidSharpDiagnostics.Trace("Custom event: GUID {0}", handleEventArgs->EventGuid); } // For now, this doesn't raise the DeviceList Changed event. It only seems to occur at connection. } } return((IntPtr)1); } return(NativeMethods.DefWindowProc(window, message, wParam, lParam)); }
protected override void Run(Action readyCallback) { const string className = "HidSharpDeviceMonitor"; NativeMethods.WindowProc windowProc = DeviceMonitorWindowProc; var wc = new NativeMethods.WNDCLASS() { ClassName = className, WindowProc = windowProc }; RunAssert(0 != NativeMethods.RegisterClass(ref wc), "HidSharp RegisterClass failed."); var hwnd = NativeMethods.CreateWindowEx(0, className, className, 0, NativeMethods.CW_USEDEFAULT, NativeMethods.CW_USEDEFAULT, NativeMethods.CW_USEDEFAULT, NativeMethods.CW_USEDEFAULT, NativeMethods.HWND_MESSAGE, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); RunAssert(hwnd != IntPtr.Zero, "HidSharp CreateWindow failed."); var hidNotifyHandle = RegisterDeviceNotification(hwnd, NativeMethods.HidD_GetHidGuid()); var bleNotifyHandle = RegisterDeviceNotification(hwnd, NativeMethods.GuidForBluetoothLEDevice); #if BLUETOOTH_NOTIFY var bleHandles = new List <BleRadio>(); // FIXME: We don't handle the removal of USB Bluetooth dongles here, as far as notifications go. IntPtr searchHandle, radioHandle; var searchParams = new NativeMethods.BLUETOOTH_FIND_RADIO_PARAMS() { Size = Marshal.SizeOf(typeof(NativeMethods.BLUETOOTH_FIND_RADIO_PARAMS)) }; searchHandle = NativeMethods.BluetoothFindFirstRadio(ref searchParams, out radioHandle); if (searchHandle != IntPtr.Zero) { do { var radio = new BleRadio(); radio.RadioHandle = radioHandle; radio.NotifyHandle = RegisterDeviceNotification(hwnd, radioHandle); bleHandles.Add(radio); }while (NativeMethods.BluetoothFindNextRadio(searchHandle, out radioHandle)); NativeMethods.BluetoothFindRadioClose(searchHandle); } if (bleHandles.Count > 0) { HidSharpDiagnostics.Trace("Found {0} Bluetooth radio(s).", bleHandles.Count); } #endif //_bleDiscoveryThread = new Thread(BleDiscoveryThread) { IsBackground = true, Name = "HidSharp BLE Discovery" }; //_bleDiscoveryThread.Start(); _serialWatcherShutdownEvent = NativeMethods.CreateManualResetEventOrThrow(); _serialWatcherThread = new Thread(SerialWatcherThread) { IsBackground = true, Name = "HidSharp Serial Watcher" }; _serialWatcherThread.Start(); _hidNotifyObject = new object(); _serNotifyObject = new object(); _bleNotifyObject = new object(); _notifyThread = new Thread(DeviceMonitorEventThread) { IsBackground = true, Name = "HidSharp RaiseChanged" }; _notifyThread.Start(); readyCallback(); NativeMethods.MSG msg; while (true) { int result = NativeMethods.GetMessage(out msg, hwnd, 0, 0); if (result == 0 || result == -1) { break; } NativeMethods.TranslateMessage(ref msg); NativeMethods.DispatchMessage(ref msg); } //lock (_bleDiscoveryThread) { _bleDiscoveryShuttingDown = true; Monitor.Pulse(_bleDiscoveryThread); } lock (_notifyThread) { _notifyThreadShuttingDown = true; Monitor.Pulse(_notifyThread); } NativeMethods.SetEvent(_serialWatcherShutdownEvent); //_bleDiscoveryThread.Join(); _notifyThread.Join(); _serialWatcherThread.Join(); UnregisterDeviceNotification(hidNotifyHandle); UnregisterDeviceNotification(bleNotifyHandle); #if BLUETOOTH_NOTIFY foreach (var bleHandle in bleHandles) { UnregisterDeviceNotification(bleHandle.NotifyHandle); NativeMethods.CloseHandle(bleHandle.RadioHandle); } #endif RunAssert(NativeMethods.DestroyWindow(hwnd), "HidSharp DestroyWindow failed."); RunAssert(NativeMethods.UnregisterClass(className, IntPtr.Zero), "HidSharp UnregisterClass failed."); GC.KeepAlive(windowProc); }