@mcesystems/usb-device-listener 1.0.31 → 1.0.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/Release/usb_device_listener.node +0 -0
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/native/addon.cc +1 -0
- package/native/usb_listener_common.h +1 -0
- package/native/usb_listener_mac.cc +54 -16
- package/native/usb_listener_win.cc +35 -0
- package/native/usb_listener_win.h +1 -0
- package/package.json +1 -1
|
Binary file
|
package/dist/types.d.ts
CHANGED
|
@@ -19,6 +19,12 @@ export interface DeviceInfo {
|
|
|
19
19
|
* Empty string if the device doesn't expose a serial number
|
|
20
20
|
*/
|
|
21
21
|
serialNumber: string;
|
|
22
|
+
/**
|
|
23
|
+
* USB product name / device description
|
|
24
|
+
* Examples: "iPhone", "SAMSUNG_Android", "Arduino Uno"
|
|
25
|
+
* Empty string if not available
|
|
26
|
+
*/
|
|
27
|
+
deviceName: string;
|
|
22
28
|
/**
|
|
23
29
|
* USB Vendor ID (decimal)
|
|
24
30
|
* Use .toString(16) to convert to hex string
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,MAAM,WAAW,UAAU;IAC1B;;;;;OAKG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;;OAGG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;;;OAIG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC5B;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;CACZ;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B;;;;;;;;;;;;;OAaG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAExC;;;;;;;;;;OAUG;IACH,aAAa,CAAC,EAAE,YAAY,EAAE,CAAC;IAE/B;;;;;;;;;;;OAWG;IACH,cAAc,CAAC,EAAE,YAAY,EAAE,CAAC;IAEhC;;;;;;;;;;OAUG;IACH,iBAAiB,CAAC,EAAE,YAAY,EAAE,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;AAEjE;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;AAEpE;;GAEG;AACH,MAAM,WAAW,kBAAkB;IAClC;;;;;;;;;;;;;;;;OAgBG;IACH,cAAc,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC;IAE7C;;;;;;;;OAQG;IACH,aAAa,IAAI,IAAI,CAAC;IAEtB;;;;;;;;;;;;;;OAcG;IACH,WAAW,CAAC,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAE/C;;;;;;;;;;;;;OAaG;IACH,cAAc,CAAC,QAAQ,EAAE,oBAAoB,GAAG,IAAI,CAAC;IAErD;;;;;;;;;;;;;;OAcG;IACH,WAAW,IAAI,UAAU,EAAE,CAAC;CAC5B"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,MAAM,WAAW,UAAU;IAC1B;;;;;OAKG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;;;OAIG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;;OAGG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;;;OAIG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC5B;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;CACZ;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B;;;;;;;;;;;;;OAaG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAExC;;;;;;;;;;OAUG;IACH,aAAa,CAAC,EAAE,YAAY,EAAE,CAAC;IAE/B;;;;;;;;;;;OAWG;IACH,cAAc,CAAC,EAAE,YAAY,EAAE,CAAC;IAEhC;;;;;;;;;;OAUG;IACH,iBAAiB,CAAC,EAAE,YAAY,EAAE,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;AAEjE;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;AAEpE;;GAEG;AACH,MAAM,WAAW,kBAAkB;IAClC;;;;;;;;;;;;;;;;OAgBG;IACH,cAAc,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC;IAE7C;;;;;;;;OAQG;IACH,aAAa,IAAI,IAAI,CAAC;IAEtB;;;;;;;;;;;;;;OAcG;IACH,WAAW,CAAC,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAE/C;;;;;;;;;;;;;OAaG;IACH,cAAc,CAAC,QAAQ,EAAE,oBAAoB,GAAG,IAAI,CAAC;IAErD;;;;;;;;;;;;;;OAcG;IACH,WAAW,IAAI,UAAU,EAAE,CAAC;CAC5B"}
|
package/native/addon.cc
CHANGED
|
@@ -98,6 +98,7 @@ Napi::Value ListDevices(const Napi::CallbackInfo& info) {
|
|
|
98
98
|
Napi::Object obj = Napi::Object::New(env);
|
|
99
99
|
obj.Set("deviceId", Napi::String::New(env, devices[i].deviceId));
|
|
100
100
|
obj.Set("serialNumber", Napi::String::New(env, devices[i].serialNumber));
|
|
101
|
+
obj.Set("deviceName", Napi::String::New(env, devices[i].deviceName));
|
|
101
102
|
obj.Set("vid", Napi::Number::New(env, devices[i].vid));
|
|
102
103
|
obj.Set("pid", Napi::Number::New(env, devices[i].pid));
|
|
103
104
|
obj.Set("locationInfo", Napi::String::New(env, devices[i].locationInfo));
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
struct DeviceInfo {
|
|
17
17
|
std::string deviceId; // Device ID (serial number if available, otherwise platform-specific)
|
|
18
18
|
std::string serialNumber; // USB serial number (raw, without formatting)
|
|
19
|
+
std::string deviceName; // USB product name / device description
|
|
19
20
|
uint16_t vid{0}; // Vendor ID
|
|
20
21
|
uint16_t pid{0}; // Product ID
|
|
21
22
|
std::string locationInfo; // Physical port location (platform-specific format)
|
|
@@ -26,39 +26,72 @@ static const char* GetUSBDeviceClassName() {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
|
-
* Get
|
|
30
|
-
* Returns empty string if
|
|
29
|
+
* Get a string property from an IOKit service
|
|
30
|
+
* Returns empty string if the property is not available
|
|
31
31
|
*/
|
|
32
|
-
static std::string
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
service, CFSTR(kUSBSerialNumberString), kCFAllocatorDefault, 0);
|
|
32
|
+
static std::string GetStringProperty(io_service_t service, CFStringRef propertyName) {
|
|
33
|
+
CFStringRef stringRef = (CFStringRef)IORegistryEntryCreateCFProperty(
|
|
34
|
+
service, propertyName, kCFAllocatorDefault, 0);
|
|
36
35
|
|
|
37
|
-
|
|
38
|
-
if (!serialRef) {
|
|
39
|
-
serialRef = (CFStringRef)IORegistryEntryCreateCFProperty(
|
|
40
|
-
service, CFSTR("USB Serial Number"), kCFAllocatorDefault, 0);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
if (!serialRef) {
|
|
36
|
+
if (!stringRef) {
|
|
44
37
|
return "";
|
|
45
38
|
}
|
|
46
39
|
|
|
47
40
|
// Convert CFString to std::string
|
|
48
|
-
CFIndex length = CFStringGetLength(
|
|
41
|
+
CFIndex length = CFStringGetLength(stringRef);
|
|
49
42
|
CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
|
|
50
43
|
std::string result(maxSize, '\0');
|
|
51
44
|
|
|
52
|
-
if (CFStringGetCString(
|
|
45
|
+
if (CFStringGetCString(stringRef, &result[0], maxSize, kCFStringEncodingUTF8)) {
|
|
53
46
|
result.resize(strlen(result.c_str()));
|
|
54
47
|
} else {
|
|
55
48
|
result.clear();
|
|
56
49
|
}
|
|
57
50
|
|
|
58
|
-
CFRelease(
|
|
51
|
+
CFRelease(stringRef);
|
|
59
52
|
return result;
|
|
60
53
|
}
|
|
61
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Get the USB product name / device name from the IOKit service
|
|
57
|
+
* Returns empty string if not available
|
|
58
|
+
*/
|
|
59
|
+
static std::string GetDeviceName(io_service_t service) {
|
|
60
|
+
// Try kUSBProductString first (standard USB property)
|
|
61
|
+
std::string name = GetStringProperty(service, CFSTR(kUSBProductString));
|
|
62
|
+
|
|
63
|
+
// Fallback to "USB Product Name"
|
|
64
|
+
if (name.empty()) {
|
|
65
|
+
name = GetStringProperty(service, CFSTR("USB Product Name"));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Fallback to IOKit device name
|
|
69
|
+
if (name.empty()) {
|
|
70
|
+
io_name_t deviceName;
|
|
71
|
+
if (IORegistryEntryGetName(service, deviceName) == KERN_SUCCESS) {
|
|
72
|
+
name = deviceName;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return name;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Get the USB serial number from the IOKit service
|
|
81
|
+
* Returns empty string if serial number is not available
|
|
82
|
+
*/
|
|
83
|
+
static std::string GetSerialNumber(io_service_t service) {
|
|
84
|
+
// Try kUSBSerialNumberString first (standard USB property)
|
|
85
|
+
std::string serial = GetStringProperty(service, CFSTR(kUSBSerialNumberString));
|
|
86
|
+
|
|
87
|
+
// Fallback to "USB Serial Number" property name
|
|
88
|
+
if (serial.empty()) {
|
|
89
|
+
serial = GetStringProperty(service, CFSTR("USB Serial Number"));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return serial;
|
|
93
|
+
}
|
|
94
|
+
|
|
62
95
|
/**
|
|
63
96
|
* Format serial number in iOS-style dash-separated format
|
|
64
97
|
* iOS UDIDs are typically 24-25 characters and formatted as: XXXXXXXX-XXXXXXXXXXXXXXXX
|
|
@@ -290,6 +323,7 @@ void USBListener::HandleDeviceAdded(io_iterator_t iterator) {
|
|
|
290
323
|
Napi::Object obj = Napi::Object::New(env);
|
|
291
324
|
obj.Set("deviceId", Napi::String::New(env, data->deviceId));
|
|
292
325
|
obj.Set("serialNumber", Napi::String::New(env, data->serialNumber));
|
|
326
|
+
obj.Set("deviceName", Napi::String::New(env, data->deviceName));
|
|
293
327
|
obj.Set("vid", Napi::Number::New(env, data->vid));
|
|
294
328
|
obj.Set("pid", Napi::Number::New(env, data->pid));
|
|
295
329
|
obj.Set("locationInfo", Napi::String::New(env, data->locationInfo));
|
|
@@ -342,6 +376,7 @@ void USBListener::HandleDeviceRemoved(io_iterator_t iterator) {
|
|
|
342
376
|
Napi::Object obj = Napi::Object::New(env);
|
|
343
377
|
obj.Set("deviceId", Napi::String::New(env, data->deviceId));
|
|
344
378
|
obj.Set("serialNumber", Napi::String::New(env, data->serialNumber));
|
|
379
|
+
obj.Set("deviceName", Napi::String::New(env, data->deviceName));
|
|
345
380
|
obj.Set("vid", Napi::Number::New(env, data->vid));
|
|
346
381
|
obj.Set("pid", Napi::Number::New(env, data->pid));
|
|
347
382
|
obj.Set("locationInfo", Napi::String::New(env, data->locationInfo));
|
|
@@ -401,6 +436,9 @@ bool USBListener::GetDeviceInfoFromService(io_service_t service, DeviceInfo& inf
|
|
|
401
436
|
// Get USB serial number
|
|
402
437
|
info.serialNumber = GetSerialNumber(service);
|
|
403
438
|
|
|
439
|
+
// Get device name
|
|
440
|
+
info.deviceName = GetDeviceName(service);
|
|
441
|
+
|
|
404
442
|
// Use serial number as device ID if available (formatted for iOS style)
|
|
405
443
|
// Otherwise fall back to VID/PID/location combination
|
|
406
444
|
if (!info.serialNumber.empty()) {
|
|
@@ -188,6 +188,7 @@ void USBListener::HandleDeviceChange(WPARAM wParam, LPARAM lParam) {
|
|
|
188
188
|
Napi::Object obj = Napi::Object::New(env);
|
|
189
189
|
obj.Set("deviceId", Napi::String::New(env, data->deviceId));
|
|
190
190
|
obj.Set("serialNumber", Napi::String::New(env, data->serialNumber));
|
|
191
|
+
obj.Set("deviceName", Napi::String::New(env, data->deviceName));
|
|
191
192
|
obj.Set("vid", Napi::Number::New(env, data->vid));
|
|
192
193
|
obj.Set("pid", Napi::Number::New(env, data->pid));
|
|
193
194
|
obj.Set("locationInfo", Napi::String::New(env, data->locationInfo));
|
|
@@ -205,6 +206,7 @@ void USBListener::HandleDeviceChange(WPARAM wParam, LPARAM lParam) {
|
|
|
205
206
|
Napi::Object obj = Napi::Object::New(env);
|
|
206
207
|
obj.Set("deviceId", Napi::String::New(env, data->deviceId));
|
|
207
208
|
obj.Set("serialNumber", Napi::String::New(env, data->serialNumber));
|
|
209
|
+
obj.Set("deviceName", Napi::String::New(env, data->deviceName));
|
|
208
210
|
obj.Set("vid", Napi::Number::New(env, data->vid));
|
|
209
211
|
obj.Set("pid", Napi::Number::New(env, data->pid));
|
|
210
212
|
obj.Set("locationInfo", Napi::String::New(env, data->locationInfo));
|
|
@@ -290,6 +292,9 @@ bool USBListener::GetDeviceInfo(const std::string& devicePath, DeviceInfo& info)
|
|
|
290
292
|
info.deviceId = fullDeviceId;
|
|
291
293
|
}
|
|
292
294
|
|
|
295
|
+
// Get device name
|
|
296
|
+
info.deviceName = GetDeviceName(deviceInfoSet, devInfoData);
|
|
297
|
+
|
|
293
298
|
SetupDiDestroyDeviceInfoList(deviceInfoSet);
|
|
294
299
|
return true;
|
|
295
300
|
}
|
|
@@ -334,6 +339,29 @@ bool USBListener::GetVidPid(const std::string& deviceId, uint16_t& vid, uint16_t
|
|
|
334
339
|
}
|
|
335
340
|
}
|
|
336
341
|
|
|
342
|
+
/**
|
|
343
|
+
* Get device description/name from Windows registry
|
|
344
|
+
*/
|
|
345
|
+
std::string USBListener::GetDeviceName(HDEVINFO deviceInfoSet, SP_DEVINFO_DATA& devInfoData) {
|
|
346
|
+
WCHAR buffer[MAX_PATH];
|
|
347
|
+
|
|
348
|
+
// Try to get device description first (usually the friendly name)
|
|
349
|
+
if (SetupDiGetDeviceRegistryPropertyW(deviceInfoSet, &devInfoData, SPDRP_DEVICEDESC,
|
|
350
|
+
nullptr, reinterpret_cast<PBYTE>(buffer), sizeof(buffer), nullptr)) {
|
|
351
|
+
std::wstring nameW = buffer;
|
|
352
|
+
return std::string(nameW.begin(), nameW.end());
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Fallback to friendly name
|
|
356
|
+
if (SetupDiGetDeviceRegistryPropertyW(deviceInfoSet, &devInfoData, SPDRP_FRIENDLYNAME,
|
|
357
|
+
nullptr, reinterpret_cast<PBYTE>(buffer), sizeof(buffer), nullptr)) {
|
|
358
|
+
std::wstring nameW = buffer;
|
|
359
|
+
return std::string(nameW.begin(), nameW.end());
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
return "";
|
|
363
|
+
}
|
|
364
|
+
|
|
337
365
|
/**
|
|
338
366
|
* Get USB serial number from device instance ID
|
|
339
367
|
* The serial number is typically the last part after the last backslash
|
|
@@ -416,6 +444,9 @@ void USBListener::EnumerateConnectedDevices() {
|
|
|
416
444
|
info.deviceId = fullDeviceId;
|
|
417
445
|
}
|
|
418
446
|
|
|
447
|
+
// Get device name
|
|
448
|
+
info.deviceName = GetDeviceName(deviceInfoSet, devInfoData);
|
|
449
|
+
|
|
419
450
|
// Cache device with exclusive lock
|
|
420
451
|
{
|
|
421
452
|
std::unique_lock lock(m_cacheMutex);
|
|
@@ -427,6 +458,7 @@ void USBListener::EnumerateConnectedDevices() {
|
|
|
427
458
|
Napi::Object obj = Napi::Object::New(env);
|
|
428
459
|
obj.Set("deviceId", Napi::String::New(env, data->deviceId));
|
|
429
460
|
obj.Set("serialNumber", Napi::String::New(env, data->serialNumber));
|
|
461
|
+
obj.Set("deviceName", Napi::String::New(env, data->deviceName));
|
|
430
462
|
obj.Set("vid", Napi::Number::New(env, data->vid));
|
|
431
463
|
obj.Set("pid", Napi::Number::New(env, data->pid));
|
|
432
464
|
obj.Set("locationInfo", Napi::String::New(env, data->locationInfo));
|
|
@@ -481,6 +513,9 @@ std::vector<DeviceInfo> USBListener::ListAllDevices() {
|
|
|
481
513
|
info.deviceId = fullDeviceId;
|
|
482
514
|
}
|
|
483
515
|
|
|
516
|
+
// Get device name
|
|
517
|
+
info.deviceName = GetDeviceName(deviceInfoSet, devInfoData);
|
|
518
|
+
|
|
484
519
|
devices.push_back(info);
|
|
485
520
|
}
|
|
486
521
|
}
|
|
@@ -75,6 +75,7 @@ private:
|
|
|
75
75
|
bool GetDeviceInfoFromPath(const std::string& devicePath, DeviceInfo& info); // Parse device info from path (for disconnections)
|
|
76
76
|
bool GetLocationInfo(DEVINST devInst, std::string& locationInfo); // Get physical port location
|
|
77
77
|
std::string GetSerialNumber(HDEVINFO deviceInfoSet, SP_DEVINFO_DATA& devInfoData); // Get USB serial number
|
|
78
|
+
std::string GetDeviceName(HDEVINFO deviceInfoSet, SP_DEVINFO_DATA& devInfoData); // Get device description/name
|
|
78
79
|
bool GetVidPid(const std::string& deviceId, uint16_t& vid, uint16_t& pid); // Parse VID/PID from device ID string
|
|
79
80
|
void EnumerateConnectedDevices(); // Enumerate devices on startup
|
|
80
81
|
|