Files
djhackersdev_bemanitools/src/main/geninput/dev-list.c
T
2019-09-27 22:36:50 +02:00

174 lines
5.1 KiB
C

#include <windows.h>
#include <setupapi.h>
#include <hidsdi.h>
#include <stdbool.h>
#include <stdlib.h>
#include <wchar.h>
#include "geninput/dev-list.h"
#include "util/log.h"
#include "util/mem.h"
void dev_list_init(struct dev_list *devs, const GUID *class_guid)
{
devs->class_guid = (GUID *) class_guid;
devs->infolist = SetupDiGetClassDevs(devs->class_guid, NULL, NULL,
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (devs->infolist == NULL) {
log_fatal("SetupDiGetClassDevs failed: %08x",
(unsigned int) GetLastError());
}
devs->dev_no = 0;
devs->iface_no = 0;
devs->detail = NULL;
devs->name = NULL;
}
bool dev_list_next(struct dev_list *devs)
{
if (devs->detail != NULL) {
free(devs->detail);
devs->detail = NULL;
}
if (devs->name != NULL) {
free(devs->name);
devs->name = NULL;
}
for (;;) {
devs->dev.cbSize = sizeof(devs->dev);
if (!SetupDiEnumDeviceInfo(devs->infolist, devs->dev_no, &devs->dev)) {
if (GetLastError() == ERROR_NO_MORE_ITEMS) {
return false;
} else {
log_fatal("SetupDiEnumDeviceInfo failed: %08x",
(unsigned int) GetLastError());
}
}
devs->iface.cbSize = sizeof(devs->iface);
if (!SetupDiEnumDeviceInterfaces(devs->infolist, &devs->dev,
devs->class_guid, devs->iface_no, &devs->iface)) {
if (GetLastError() == ERROR_NO_MORE_ITEMS) {
devs->dev_no++;
devs->iface_no = 0;
continue;
} else {
log_fatal("SetupDiEnumDeviceInterfaces failed: %08x",
(unsigned int) GetLastError());
}
}
devs->iface_no++;
return true;
}
}
const char *dev_list_get_dev_node(struct dev_list *devs)
{
DWORD detail_size;
if (devs->detail != NULL) {
return devs->detail->DevicePath;
}
if (SetupDiGetDeviceInterfaceDetail(devs->infolist, &devs->iface, NULL, 0,
&detail_size, NULL)
|| GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
log_fatal("SetupDiGetDeviceInterfaceDetail sizing failed: %08x",
(unsigned int) GetLastError());
}
devs->detail = xmalloc(detail_size);
devs->detail->cbSize = sizeof(*devs->detail);
if (!SetupDiGetDeviceInterfaceDetail(devs->infolist, &devs->iface,
devs->detail, detail_size, NULL, NULL)) {
log_fatal("SetupDiGetDeviceInterfaceDetail content failed: %08x",
(unsigned int) GetLastError());
}
return devs->detail->DevicePath;
}
const wchar_t *dev_list_get_dev_name(struct dev_list *devs)
{
DWORD name_size;
const char* dev_path;
// largest possible product string according to WinAPI docs
wchar_t product_string[126];
DWORD product_size;
bool product_success;
if (devs->name != NULL) {
return devs->name;
}
if (SetupDiGetDeviceRegistryPropertyW(devs->infolist, &devs->dev,
SPDRP_DEVICEDESC, NULL, NULL, 0, &name_size)
|| GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
log_fatal("SetupDiGetDeviceRegistryPropertyW(SPDRP_DEVICEDESC) sizing "
"failed: %08x", (unsigned int) GetLastError());
}
/* This function returns arbitrary data, not just strings. Therefore, we
are actually working in units of bytes and not wchar_t's here for once */
devs->name = xmalloc(name_size);
if (!SetupDiGetDeviceRegistryPropertyW(devs->infolist, &devs->dev,
SPDRP_DEVICEDESC, NULL, (void *) devs->name, name_size, 0)) {
log_fatal("SetupDiGetDeviceRegistryPropertyW(SPDRP_DEVICEDESC) content "
"failed: %08x", (unsigned int) GetLastError());
}
/* Also, try and get the product string - this is usually far more human
readable than the registry value, which is often "HID Compliant Thing".
We then append this in parentheses */
dev_path = dev_list_get_dev_node(devs);
HANDLE hid_hnd = CreateFile(dev_path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if(hid_hnd) {
product_success = HidD_GetProductString(hid_hnd, product_string, sizeof(product_string));
CloseHandle(hid_hnd);
if(product_success && wcslen(product_string) > 0 && wcscmp(product_string, devs->name)) {
// len + null + 2*parentheses + space separator
product_size = (wcslen(product_string)+4) * sizeof(wchar_t);
devs->name = xrealloc(devs->name, name_size + product_size);
wcscat(devs->name, L" (");
wcscat(devs->name, product_string);
wcscat(devs->name, L")");
}
}
return devs->name;
}
void dev_list_fini(struct dev_list *devs)
{
if (devs->detail != NULL) {
free(devs->detail);
}
if (devs->name != NULL) {
free(devs->name);
}
if (!SetupDiDestroyDeviceInfoList(devs->infolist)) {
log_fatal("SetupDiDestroyDeviceList failed: %08x",
(unsigned int) GetLastError());
}
}