diff --git a/ManipulationDemo/MainWindow.xaml.cs b/ManipulationDemo/MainWindow.xaml.cs
index 99fbd1c..77b1dc5 100644
--- a/ManipulationDemo/MainWindow.xaml.cs
+++ b/ManipulationDemo/MainWindow.xaml.cs
@@ -15,6 +15,10 @@
using System.Runtime.ExceptionServices;
using System.Threading;
using System.Threading.Tasks;
+using Windows.Win32;
+using static ManipulationDemo.UsbNotification;
+using System.Runtime.InteropServices;
+using System.Text.RegularExpressions;
namespace ManipulationDemo
{
@@ -107,7 +111,7 @@ public MainWindow()
private readonly DispatcherTimer _timer;
- private void OnTick(object sender, EventArgs e)
+ private unsafe void OnTick(object sender, EventArgs e)
{
try
{
@@ -154,6 +158,9 @@ private void OnTick(object sender, EventArgs e)
device.Name, device.StylusDevices.Count, tabletSize));
}
}
+
+ AppendPointerDeviceInfo(builder);
+
PhysicalSizeRun.Text = builder.ToString();
}
catch (Exception ex)
@@ -162,6 +169,38 @@ private void OnTick(object sender, EventArgs e)
}
}
+ ///
+ /// 添加 Pointer 消息的信息
+ ///
+ ///
+ private static unsafe void AppendPointerDeviceInfo(StringBuilder stringBuilder)
+ {
+ try
+ {
+ // 获取 Pointer 设备数量
+ uint deviceCount = 0;
+ PInvoke.GetPointerDevices(ref deviceCount,
+ (Windows.Win32.UI.Controls.POINTER_DEVICE_INFO*) IntPtr.Zero);
+ Windows.Win32.UI.Controls.POINTER_DEVICE_INFO[] pointerDeviceInfo =
+ new Windows.Win32.UI.Controls.POINTER_DEVICE_INFO[deviceCount];
+ fixed (Windows.Win32.UI.Controls.POINTER_DEVICE_INFO* pDeviceInfo = &pointerDeviceInfo[0])
+ {
+ // 这里需要拿两次,第一次获取数量,第二次获取信息
+ PInvoke.GetPointerDevices(ref deviceCount, pDeviceInfo);
+ stringBuilder.AppendLine($"PointerDeviceCount:{deviceCount} 设备列表:");
+ foreach (var info in pointerDeviceInfo)
+ {
+ stringBuilder.AppendLine($" - {info.productString}");
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ // 也许是在非 Win8 或以上的系统,抛出找不到方法,这个 GetPointerDevices 方法是 Win8 加的
+ // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getpointerdevices
+ }
+ }
+
private void OnStylusDown(object sender, StylusDownEventArgs e)
{
StylusDownStoryboard.Begin();
@@ -230,7 +269,8 @@ private void OnManipulationCompleted(object sender, ManipulationCompletedEventAr
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
- var source = (HwndSource) PresentationSource.FromVisual(this);
+ var source = (HwndSource) PresentationSource.FromVisual(this)!;
+ UsbNotification.RegisterUsbDeviceNotification(source.Handle);
source?.AddHook(HwndHook);
Log("程序启动时");
@@ -240,20 +280,80 @@ protected override void OnSourceInitialized(EventArgs e)
private IntPtr HwndHook(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam, ref bool handled)
{
// 检查硬件设备插拔。
- if (msg == (int) WindowMessages.DEVICECHANGE)
+ if (msg == (int)WindowMessages.DEVICECHANGE)
{
- var eventText = $"Event={(WindowsMessageDeviceChangeEventEnum)wparam}";
+ // 是否应该加上通用的变更记录日志
+ bool shouldCommonLog = true;
+
+ bool isDeviceArrival = (int)wparam == (int)WindowsMessageDeviceChangeEventEnum.DBT_DEVICEARRIVAL;
+ bool isDeviceRemoveComplete = (int)wparam == (int)WindowsMessageDeviceChangeEventEnum.DBT_DEVICEREMOVECOMPLETE;
+
+ if (isDeviceArrival || isDeviceRemoveComplete)
+ {
+ // 设备被移除或插入,试试拿到具体是哪个设备
+ DEV_BROADCAST_HDR hdr =
+ (DEV_BROADCAST_HDR) Marshal.PtrToStructure(lparam, typeof(DEV_BROADCAST_HDR));
+ if (hdr.dbch_devicetype == UsbNotification.DbtDevtypDeviceinterface)
+ {
+ DEV_BROADCAST_DEVICEINTERFACE deviceInterface =
+ (DEV_BROADCAST_DEVICEINTERFACE) Marshal.PtrToStructure(lparam,
+ typeof(DEV_BROADCAST_DEVICEINTERFACE));
+
+ var classguid = deviceInterface.dbcc_classguid;
+ // 这里的 classguid 默认会带上 name 上,于是就用不着
+
+ var size = Marshal.SizeOf(typeof(DEV_BROADCAST_DEVICEINTERFACE));
+ var namePtr = lparam + size;
+ var nameSize = hdr.dbch_size - size;
+ // 使用 Unicode 读取的话,一个字符是两个字节
+ var charLength = nameSize / 2;
+ var name = Marshal.PtrToStringUni(namePtr, charLength);
+ if (string.IsNullOrEmpty(name))
+ {
+ name = "读取不到设备名";
+ }
+
+ string pid = string.Empty;
+ string vid = string.Empty;
+
+ var pidMatch = Regex.Match(name, @"PID_([\dA-Fa-f]{4})");
+ if (pidMatch.Success)
+ {
+ pid = pidMatch.Groups[1].Value;
+ }
+
+ var vidMatch = Regex.Match(name, @"VID_([\dA-Fa-f]{4})");
+ if (vidMatch.Success)
+ {
+ vid = vidMatch.Groups[1].Value;
+ }
+
+ Log(DeviceChangeListenerTextBlock, $"[WM_DEVICECHANGE] 设备{(isDeviceArrival?"插入":"拔出")} PID={pid} VID={vid}\r\n{name}", true);
+
+ // 换成带上更多信息的记录,不需要通用记录
+ shouldCommonLog = false;
+ }
+ }
- Log(DeviceChangeListenerTextBlock, $"[WM_DEVICECHANGE]设备发生插拔 0x{wparam.ToString("X4")}-0x{lparam.ToString("X4")};{eventText}", true);
- LogDevices();
+ if (shouldCommonLog)
+ {
+ var eventText = $"Event={(WindowsMessageDeviceChangeEventEnum) wparam}";
+
+ Log(DeviceChangeListenerTextBlock,
+ $"[WM_DEVICECHANGE]设备发生插拔 Param=0x{wparam.ToString("X4")}-0x{lparam.ToString("X4")};{eventText}",
+ true);
+ LogDevices();
+ }
}
- else if (msg == (int) WindowMessages.TABLET_ADDED)
+ else if (msg == (int)WindowMessages.TABLET_ADDED)
{
- Log(DeviceChangeListenerTextBlock, $"[TABLET_ADDED]触摸设备插入 0x{wparam.ToString("X4")} - 0x{lparam.ToString("X4")}", true);
+ Log(DeviceChangeListenerTextBlock,
+ $"[TABLET_ADDED]触摸设备插入 0x{wparam.ToString("X4")} - 0x{lparam.ToString("X4")}", true);
}
- else if (msg == (int) WindowMessages.TABLET_DELETED)
+ else if (msg == (int)WindowMessages.TABLET_DELETED)
{
- Log(DeviceChangeListenerTextBlock, $"[TABLET_DELETED]触摸设备拔出 0x{wparam.ToString("X4")} - 0x{lparam.ToString("X4")}", true);
+ Log(DeviceChangeListenerTextBlock,
+ $"[TABLET_DELETED]触摸设备拔出 0x{wparam.ToString("X4")} - 0x{lparam.ToString("X4")}", true);
}
// 输出消息。
@@ -262,7 +362,7 @@ private IntPtr HwndHook(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam, ref
return IntPtr.Zero;
}
- var formattedMessage = $"{(WindowMessages) msg}({msg})";
+ var formattedMessage = $"{(WindowMessages)msg}({msg})";
Log(HwndMsgTextBlock, formattedMessage);
return IntPtr.Zero;
diff --git a/ManipulationDemo/ManipulationDemo.csproj b/ManipulationDemo/ManipulationDemo.csproj
index 7f36069..816b9d1 100644
--- a/ManipulationDemo/ManipulationDemo.csproj
+++ b/ManipulationDemo/ManipulationDemo.csproj
@@ -1,12 +1,29 @@
-
- WinExe
- netcoreapp3.1;net45
- true
- ManipulationDemo.Program
-
-
-
-
+
+ WinExe
+ net6.0-windows;net45
+ true
+ latest
+ ManipulationDemo.Program
+
+
+
+
+
+
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
diff --git a/ManipulationDemo/NativeMethods.txt b/ManipulationDemo/NativeMethods.txt
new file mode 100644
index 0000000..8654ad0
--- /dev/null
+++ b/ManipulationDemo/NativeMethods.txt
@@ -0,0 +1 @@
+GetPointerDevices
\ No newline at end of file
diff --git a/ManipulationDemo/Properties/Settings.Designer.cs b/ManipulationDemo/Properties/Settings.Designer.cs
index 1bb4fd8..7777495 100644
--- a/ManipulationDemo/Properties/Settings.Designer.cs
+++ b/ManipulationDemo/Properties/Settings.Designer.cs
@@ -12,7 +12,7 @@ namespace ManipulationDemo.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.6.0.0")]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.7.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
@@ -27,7 +27,7 @@ public static Settings Default {
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("3,5,6,7,8,13,14,15,19,20,24,25,28,31,32,36,61,70,71,124,125,127,131,132,133,134,1" +
"60,161,174,274,281,282,283,356,522,526,532,533,534,561,562,641,642,674,675,725,7" +
- "99,49283,49343,49586")]
+ "99,49283,49343,49586,")]
public string IgnoredMsgs {
get {
return ((string)(this["IgnoredMsgs"]));
diff --git a/ManipulationDemo/UsbNotification.cs b/ManipulationDemo/UsbNotification.cs
new file mode 100644
index 0000000..e61d3f6
--- /dev/null
+++ b/ManipulationDemo/UsbNotification.cs
@@ -0,0 +1,81 @@
+using System.Runtime.InteropServices;
+using System;
+
+namespace ManipulationDemo
+{
+ ///
+ /// Copy From: https://stackoverflow.com/questions/1976573/using-registerdevicenotification-in-a-net-app
+ ///
+ internal static class UsbNotification
+ {
+ public const int DbtDevicearrival = 0x8000; // system detected a new device
+ public const int DbtDeviceremovecomplete = 0x8004; // device is gone
+ public const int WmDevicechange = 0x0219; // device change event
+ public const int DbtDevtypDeviceinterface = 5;
+ private static readonly Guid GuidDevinterfaceUSBDevice = new Guid("A5DCBF10-6530-11D2-901F-00C04FB951ED"); // USB devices
+ private static IntPtr _notificationHandle;
+
+ ///
+ /// Registers a window to receive notifications when USB devices are plugged or unplugged.
+ ///
+ /// Handle to the window receiving notifications.
+ public static void RegisterUsbDeviceNotification(IntPtr windowHandle)
+ {
+ DevBroadcastDeviceinterface dbi = new DevBroadcastDeviceinterface
+ {
+ DeviceType = DbtDevtypDeviceinterface,
+ Reserved = 0,
+ ClassGuid = GuidDevinterfaceUSBDevice,
+ Name = 0
+ };
+
+ dbi.Size = Marshal.SizeOf(dbi);
+ IntPtr buffer = Marshal.AllocHGlobal(dbi.Size);
+ Marshal.StructureToPtr(dbi, buffer, true);
+
+ _notificationHandle = RegisterDeviceNotification(windowHandle, buffer, 0);
+ }
+
+ ///
+ /// Unregisters the window for USB device notifications
+ ///
+ public static void UnregisterUsbDeviceNotification()
+ {
+ UnregisterDeviceNotification(_notificationHandle);
+ }
+
+ [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
+ private static extern IntPtr RegisterDeviceNotification(IntPtr recipient, IntPtr notificationFilter, int flags);
+
+ [DllImport("user32.dll")]
+ private static extern bool UnregisterDeviceNotification(IntPtr handle);
+
+ [StructLayout(LayoutKind.Sequential)]
+ private struct DevBroadcastDeviceinterface
+ {
+ internal int Size;
+ internal int DeviceType;
+ internal int Reserved;
+ internal Guid ClassGuid;
+ internal short Name;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct DEV_BROADCAST_HDR
+ {
+ public int dbch_size;
+ public int dbch_devicetype;
+ public int dbch_reserved;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct DEV_BROADCAST_DEVICEINTERFACE
+ {
+ public int dbcc_size;
+ public int dbcc_devicetype;
+ public int dbcc_reserved;
+ public Guid dbcc_classguid;
+ //public string dbcc_name;
+ }
+ }
+}
\ No newline at end of file