Repository navigation
Expand file tree
/
Copy pathProbe.cs
More file actions
157 lines (145 loc) · 7.96 KB
/
Copy pathProbe.cs
File metadata and controls
157 lines (145 loc) · 7.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
static class N {
public const int DIGCF_PRESENT = 0x02;
public const int DIGCF_DEVICEINTERFACE = 0x10;
public const uint GENERIC_READ = 0x80000000;
public const uint FILE_SHARE_READ = 1, FILE_SHARE_WRITE = 2;
public const uint OPEN_EXISTING = 3;
public static readonly IntPtr INVALID = new IntPtr(-1);
[StructLayout(LayoutKind.Sequential)]
public struct SP_DEVICE_INTERFACE_DATA {
public int cbSize; public Guid InterfaceClassGuid; public int Flags; public IntPtr Reserved;
}
[StructLayout(LayoutKind.Sequential)]
public struct HIDD_ATTRIBUTES {
public int Size; public ushort VendorID; public ushort ProductID; public ushort VersionNumber;
}
[StructLayout(LayoutKind.Sequential)]
public struct HIDP_CAPS {
public ushort Usage, UsagePage, InputReportByteLength, OutputReportByteLength, FeatureReportByteLength;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)] public ushort[] Reserved;
public ushort NumberLinkCollectionNodes, NumberInputButtonCaps, NumberInputValueCaps, NumberInputDataIndices,
NumberOutputButtonCaps, NumberOutputValueCaps, NumberOutputDataIndices,
NumberFeatureButtonCaps, NumberFeatureValueCaps, NumberFeatureDataIndices;
}
[DllImport("hid.dll")] public static extern void HidD_GetHidGuid(out Guid g);
[DllImport("hid.dll")] public static extern bool HidD_GetAttributes(IntPtr h, ref HIDD_ATTRIBUTES a);
[DllImport("hid.dll")] public static extern bool HidD_GetPreparsedData(IntPtr h, out IntPtr pp);
[DllImport("hid.dll")] public static extern bool HidD_FreePreparsedData(IntPtr pp);
[DllImport("hid.dll")] public static extern int HidP_GetCaps(IntPtr pp, ref HIDP_CAPS caps);
[DllImport("hid.dll", CharSet = CharSet.Unicode)] public static extern bool HidD_GetProductString(IntPtr h, StringBuilder sb, int len);
[DllImport("setupapi.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr SetupDiGetClassDevs(ref Guid g, IntPtr en, IntPtr hwnd, int flags);
[DllImport("setupapi.dll")]
public static extern bool SetupDiEnumDeviceInterfaces(IntPtr s, IntPtr di, ref Guid g, int i, ref SP_DEVICE_INTERFACE_DATA d);
[DllImport("setupapi.dll", CharSet = CharSet.Unicode)]
public static extern bool SetupDiGetDeviceInterfaceDetail(IntPtr s, ref SP_DEVICE_INTERFACE_DATA d, IntPtr det, int size, ref int req, IntPtr dd);
[DllImport("setupapi.dll")] public static extern bool SetupDiDestroyDeviceInfoList(IntPtr s);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr CreateFile(string p, uint acc, uint share, IntPtr sa, uint disp, uint flags, IntPtr tmpl);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool ReadFile(IntPtr h, byte[] buf, int n, out int read, IntPtr ov);
[DllImport("kernel32.dll")] public static extern bool CloseHandle(IntPtr h);
}
class Dev {
public string Path, Product;
public ushort Vid, Pid, UsagePage, Usage, InLen;
}
class Probe {
static List<Dev> Enumerate() {
var list = new List<Dev>();
Guid g; N.HidD_GetHidGuid(out g);
IntPtr set = N.SetupDiGetClassDevs(ref g, IntPtr.Zero, IntPtr.Zero, N.DIGCF_PRESENT | N.DIGCF_DEVICEINTERFACE);
if (set == N.INVALID) return list;
var did = new N.SP_DEVICE_INTERFACE_DATA();
did.cbSize = Marshal.SizeOf(typeof(N.SP_DEVICE_INTERFACE_DATA));
for (int i = 0; N.SetupDiEnumDeviceInterfaces(set, IntPtr.Zero, ref g, i, ref did); i++) {
int req = 0;
N.SetupDiGetDeviceInterfaceDetail(set, ref did, IntPtr.Zero, 0, ref req, IntPtr.Zero);
if (req <= 0) continue;
IntPtr buf = Marshal.AllocHGlobal(req);
try {
Marshal.WriteInt32(buf, IntPtr.Size == 8 ? 8 : 6);
if (!N.SetupDiGetDeviceInterfaceDetail(set, ref did, buf, req, ref req, IntPtr.Zero)) continue;
string path = Marshal.PtrToStringUni(new IntPtr(buf.ToInt64() + 4));
IntPtr h = N.CreateFile(path, 0, N.FILE_SHARE_READ | N.FILE_SHARE_WRITE, IntPtr.Zero, N.OPEN_EXISTING, 0, IntPtr.Zero);
if (h == N.INVALID) continue;
try {
var at = new N.HIDD_ATTRIBUTES(); at.Size = Marshal.SizeOf(typeof(N.HIDD_ATTRIBUTES));
if (!N.HidD_GetAttributes(h, ref at)) continue;
IntPtr pp;
if (!N.HidD_GetPreparsedData(h, out pp)) continue;
var caps = new N.HIDP_CAPS();
N.HidP_GetCaps(pp, ref caps);
N.HidD_FreePreparsedData(pp);
var sb = new StringBuilder(256);
string prod = N.HidD_GetProductString(h, sb, 256) ? sb.ToString() : "";
list.Add(new Dev { Path = path, Product = prod, Vid = at.VendorID, Pid = at.ProductID,
UsagePage = caps.UsagePage, Usage = caps.Usage, InLen = caps.InputReportByteLength });
} finally { N.CloseHandle(h); }
} finally { Marshal.FreeHGlobal(buf); }
}
N.SetupDiDestroyDeviceInfoList(set);
return list;
}
static string Hex(byte[] b, int n) {
var sb = new StringBuilder();
for (int i = 0; i < n; i++) sb.Append(b[i].ToString("X2")).Append(' ');
return sb.ToString().TrimEnd();
}
static void Main(string[] args) {
bool listOnly = args.Length > 0 && args[0] == "list";
var all = Enumerate();
Console.WriteLine("=== HID collections for Apple (VID 0x004C) ===");
var apple = new List<Dev>();
foreach (var d in all) {
if (d.Vid != 0x004C) continue;
apple.Add(d);
Console.WriteLine(string.Format(
" [{0}] PID=0x{1:X4} UsagePage=0x{2:X2} Usage=0x{3:X2} InputReportLen={4} Product=\"{5}\"",
apple.Count - 1, d.Pid, d.UsagePage, d.Usage, d.InLen, d.Product));
Console.WriteLine(" " + d.Path);
}
if (apple.Count == 0) {
Console.WriteLine(" none found - is the keyboard connected right now?");
return;
}
if (listOnly) return;
Console.WriteLine();
Console.WriteLine("=== Listening. Press F10, F11, F12 (a few times each), then Ctrl+C ===");
Console.WriteLine();
int opened = 0;
foreach (var d in apple) {
if (d.UsagePage == 0x01 && d.Usage == 0x06) continue; // plain keyboard collection: owned by Windows
var dd = d;
var t = new Thread(delegate() {
IntPtr h = N.CreateFile(dd.Path, N.GENERIC_READ, N.FILE_SHARE_READ | N.FILE_SHARE_WRITE,
IntPtr.Zero, N.OPEN_EXISTING, 0, IntPtr.Zero);
if (h == N.INVALID) {
Console.WriteLine(string.Format(" ! cannot open UsagePage=0x{0:X2} Usage=0x{1:X2} (win32 err {2})",
dd.UsagePage, dd.Usage, Marshal.GetLastWin32Error()));
return;
}
Console.WriteLine(string.Format(" + reading UsagePage=0x{0:X2} Usage=0x{1:X2}", dd.UsagePage, dd.Usage));
int len = dd.InLen > 0 ? dd.InLen : 64;
var buf = new byte[len];
while (true) {
int read;
if (!N.ReadFile(h, buf, len, out read, IntPtr.Zero)) break;
if (read <= 0) continue;
Console.WriteLine(string.Format("UP=0x{0:X2} U=0x{1:X2} | {2}", dd.UsagePage, dd.Usage, Hex(buf, read)));
}
N.CloseHandle(h);
});
t.IsBackground = true;
t.Start();
opened++;
}
if (opened == 0) { Console.WriteLine(" no non-keyboard collections to read."); return; }
Thread.Sleep(Timeout.Infinite);
}
}