/********************************************************
* Display HID devices and their usages in a tree *
* TODO: Setting some values, showing values live *
* haftmann#software, Win32-API *
********************************************************/
#define _WIN32_WINNT 0x0500
#include "hidparse.h"
#include <commdlg.h>
#include <setupapi.h>
#include <devguid.h>
#include <dbt.h>
#define INITGUID
#include <guiddef.h>
#include <usbiodef.h>
#ifdef _DEBUG // cannot link UUID.lib
DEFINE_GUID(GUID_DEVCLASS_HIDCLASS,0x745a17a0L,0x74d3,0x11d0,0xb6,0xfe,0x00,0xa0,0xc9,0x0f,0x57,0xda);
#endif
HINSTANCE hInst;
UsageDecode ud;
// Decode UsagePage and Usage using HidGen's files (dt.ini, *.upg)
// 0 p u -> Usage(p:u)
// p p u -> Usage(u)
// p q u -> Usage(q:u)
// p 0 u -> Usage(p:u) not used
static const TCHAR* DecodeUsage(USAGE p, DWORD u) {
static TCHAR buf[128];
BYTE flags=1;
USAGE_AND_PAGE pu=*(USAGE_AND_PAGE*)&u;
if (!pu.UsagePage) pu.UsagePage=p;
if (pu.UsagePage!=p) flags=3;
ud.Decode(pu,flags,buf,elemof(buf));
return buf;
}
static const TCHAR* DecodeUsagePage(USAGE p) {
static TCHAR buf[64];
USAGE_AND_PAGE pu={0,p};
ud.Decode(pu,2,buf,elemof(buf));
return buf;
}
// This one tries to generate SI units
static const TCHAR* DecodeUnit(ULONG unit, int&exp) {
static TCHAR buf[64];
switch (unit) {
case 0x11: exp-=2; return T("m");
case 0x101: exp-=3; return T("kg");
case 0x1001: return T("s");
case 0xF011: exp-=2; return T("m/s");
case 0xF111: exp-=5; return T("mN");
case 0xE011: exp-=2; return T("m/s²");
case 0xE111: exp-=5; return T("N");
case 0xE121: exp-=7; return T("J");
case 0xE012: return T("rad/s²");
case 0xF0D121: exp-=7; return T("V");
case 0x100001: return T("A");
case 0xE1F1: exp-=1; return T("Pa");
}
buf[0]=0;
return buf;
}
// Another one decodes the unit as-is (TODO!)
static const TCHAR* DecodeCollection(unsigned CollectionType) {
switch (CollectionType) {
case 0: return T("Physical");
case 1: return T("Application");
case 2: return T("Logical");
case 3: return T("Report");
case 4: return T("Named Array");
case 5: return T("Usage Switch");
case 6: return T("Usage Modifier");
default: return T("unknown");
}
}
// For helping the tree enumeration process
static struct TREE{
HWND hTree;
HANDLE hDev;
PHIDP_PREPARSED_DATA pd;
HIDP_CAPS hidcaps;
union{
struct{
char *Input[256]; // one (cached) report for each ID possible
char *Output[256];
char *Feature[256];
};
char *all[3][256];
}ReportCache;
HTREEITEM Append(HTREEITEM parent, int iImage, const TCHAR*fmt, ...) const;
void InsertCollectionNode(HTREEITEM p, const HIDP_LINK_COLLECTION_NODE* n, int i, int par) const;
HTREEITEM OutCapsComm(HTREEITEM p2, const HIDP_BUTTON_CAPS *caps) const;
char* GetReport(HIDP_REPORT_TYPE rt, BYTE ReportID, DWORD*ReportLen);
void FreeReports();
void OutCaps(HTREEITEM p, const TCHAR*title, HIDP_REPORT_TYPE rt);
void HexDump(HTREEITEM p, const char*data, int len) const;
void Build();
}tree;
// Append a textual item and an icon
HTREEITEM TREE::Append(HTREEITEM parent, int iImage, const TCHAR*fmt, ...) const{
TCHAR s[256];
wvnsprintf(s,elemof(s),fmt,(va_list)(&fmt+1));
TVINSERTSTRUCT tvi;
tvi.hParent=parent;
tvi.hInsertAfter=TVI_LAST;
tvi.item.mask=TVIF_TEXT|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
tvi.item.pszText=s;
tvi.item.iImage=tvi.item.iSelectedImage=iImage;
return TreeView_InsertItem(hTree,&tvi);
}
// recursive function to build the tree
void TREE::InsertCollectionNode(HTREEITEM p, const HIDP_LINK_COLLECTION_NODE* n, int i, int par) const{
p=Append(p,2,T("Collection (%s), %s%s"),
DecodeCollection(n[i].CollectionType),
DecodeUsage(par>=0?n[par].LinkUsagePage:0,*(DWORD*)&n[i].LinkUsage),
// possibly short usage for deeper levels but enforce long usage for top-level
n[i].IsAlias ? T(", ALIAS") : T(""));
int k=n[i].FirstChild;
for (int j=0; j<n[i].NumberOfChildren; j++) {
InsertCollectionNode(p,n,k,i); // recurse over all children
k=n[k].NextSibling; // take next index
}
}
// HIDP_BUTTON_CAPS and HIDP_VALUE_CAPS is very similar, emit same things here!
HTREEITEM TREE::OutCapsComm(HTREEITEM p2, const HIDP_BUTTON_CAPS *caps) const{
HTREEITEM p3,p4;
p3=Append(p2,2,T("%s, ReportID (%d), Link%s%s"),
DecodeUsagePage(caps->UsagePage),
caps->ReportID,
DecodeUsage(caps->UsagePage,*(DWORD*)&caps->LinkUsage),
caps->IsAlias ? T(", ALIAS") : T(""));
Append(p3,2,caps->IsRange ? T("UsageMin (0x%02X), UsageMax(0x%02X)") : T("Usage (0x%02X)"),
caps->Range.UsageMin, caps->Range.UsageMax);
// Do not emit String ID == 0
if (caps->Range.StringMin) {
int a=caps->Range.StringMin;
int e=caps->IsStringRange ? caps->Range.StringMax : a;
p4=Append(p3,2,caps->IsStringRange ? T("StringMin (0x%02X), StringMax(0x%02X)") : T("StringIndex (0x%02X)"),
a, e);
// Insert strings here
WCHAR *str = new WCHAR[128];
for (;a<=e; a++) {
HidD_GetIndexedString(hDev,a,str,256); // What about language ID here??
Append(p4,1,T("`") USTR T("´"),str);
}
delete[] str;
}
// Do not emit Designator ID == 0
if (caps->Range.DesignatorMin) {
int a=caps->Range.DesignatorMin;
int e=caps->IsDesignatorRange ? caps->Range.DesignatorMax : a;
p4=Append(p3,2,caps->IsDesignatorRange ? T("DesignatorMin (0x%02X), DesignatorMax(0x%02X)") : T("DesignatorIndex (0x%02X)"),
a, e);
// TODO: Insert Physical Descriptor here!
BYTE *buf = new BYTE[1024];
HidD_GetPhysicalDescriptor(hDev,buf,1024);
for (;a<=e; a++) {
// TODO: Parse all physical descriptors
}
delete[] buf;
}
Append(p3,2,caps->IsRange ? T("DataIndexMin (0x%02X), DataIndexMax(0x%02X)") : T("DataIndex (0x%02X)"),
caps->Range.DataIndexMin, caps->Range.DataIndexMax);
Append(p3,2,caps->IsAbsolute ? T("ABSOLUTE") : T("RELATIVE"));
Append(p3,2,T("Bitfield = 0x%02X, LinkCollection = 0x%02X"),
caps->BitField, caps->LinkCollection);
#if 1 // Not in Win98 DDK! TODO!
HIDP_EXTENDED_ATTRIBUTES *ea;
ULONG lea = 1024;
ea = (HIDP_EXTENDED_ATTRIBUTES*) new BYTE[lea];
ea->NumGlobalUnknowns=0;
HidP_GetExtendedAttributes(HidP_Input,caps->NotRange.DataIndex,pd,ea,&lea);
if (ea->NumGlobalUnknowns) {
p4=Append(p3,2,T("GlobalUnknowns: %d"),ea->NumGlobalUnknowns);
PHIDP_UNKNOWN_TOKEN t=ea->GlobalUnknowns; // is NULL for me!
if (!t) t=(PHIDP_UNKNOWN_TOKEN)ea->Data; // seems to contain the data
for (int i=0; i<ea->NumGlobalUnknowns; i++,t++) {
Append(p4,2,T("Token = 0x%02X, BitField = 0x%02X = %d"),t->Token,t->BitField,t->BitField);
}
}
delete ea;
#endif
return p3;
}
void deletePtr(char*&p) {
delete[] p;
p=0;
}
static BOOL(WINAPI*pHidD_GetInputReport)(HANDLE,char*,int);
// Retrieves a (cached) (Input/Feature) report, allocate an initalized Output report
char* TREE::GetReport(HIDP_REPORT_TYPE rt, BYTE id, DWORD*ReportLen) {
DWORD len=(&hidcaps.InputReportByteLength)[rt];
char **pReport=&ReportCache.all[rt][id]; // pointer to one report buffer array index
if (len && !*pReport && (*pReport=new char[len])) {
HidP_InitializeReportForID(rt,id,pd,*pReport,len);
// **pReport=id; // Transfer report ID wanted down to system
switch (rt) {
case HidP_Input: if (Config.flags&2 && pHidD_GetInputReport) {
if (!pHidD_GetInputReport(hDev,*pReport,len))// is better suited here but available at XP+
deletePtr(*pReport);
}else{
OVERLAPPED o;
o.hEvent=CreateEvent(NULL,FALSE,FALSE,NULL);
if (ReadFile(hDev,*pReport,len,&len,&o) // completes synchronously
|| (GetLastError()==ERROR_IO_PENDING // OR will complete later
&& !WaitForSingleObject(o.hEvent,50) // AND completes in a reasonable time frame
&& GetOverlappedResult(hDev,&o,&len,FALSE))){// AND have result: OK
}else{
CancelIo(hDev); // tear down async operation
deletePtr(*pReport); // free buffer and clear link
}
CloseHandle(o.hEvent);
}break;
case HidP_Feature:
if (!HidD_GetFeature(hDev,*pReport,len)) // OK
deletePtr(*pReport); // free buffer and clear link
break;
}
}
if (ReportLen) *ReportLen=len;
if (*pReport) **pReport=id; // (handle erraneous V-USB devices)
return *pReport;
}
void TREE::FreeReports() {
for (char** p=&ReportCache.all[0][0]; p<&ReportCache.all[0][0]+3*256/*elemof(ReportCache.all)*/; p++)
if (*p) deletePtr(*p);
}
// Emit ButtonCaps, ValueCaps, and DataIndices arrays (if any) for one report type
void TREE::OutCaps(HTREEITEM p, const TCHAR*title, HIDP_REPORT_TYPE rt) {
USHORT *numbers=&hidcaps.NumberInputButtonCaps+3*rt;
if (!numbers[0] && !numbers[1] && !numbers[2]) return; // nothing useful!
p=Append(p,2,title);
HTREEITEM p2,p3;
int i;
char *report;
ULONG reportlen;
if (numbers[0]) {
p2=Append(p,2,T("ButtonCaps"));
HIDP_BUTTON_CAPS *caps = new HIDP_BUTTON_CAPS[numbers[0]];
HidP_GetButtonCaps(rt,caps,numbers,pd);
for (i=0; i<numbers[0]; i++) {
p3=OutCapsComm(p2,caps+i);
report=GetReport(rt,caps[i].ReportID,&reportlen);
if (report) {
ULONG l;
l=HidP_MaxUsageListLength(rt,caps[i].UsagePage,pd);
Append(p3,2,T("MaxUsageListLength: %d"),l);
USAGE *u = new USAGE[l];
HidP_GetUsages(rt,caps[i].UsagePage,caps[i].LinkCollection,u,&l,pd,report,reportlen);
TCHAR s[256],*p=s;
for (unsigned i=0; i<l; i++) {
if (i) *p++=',';
p+=wnsprintf(p,(int)(s+elemof(s)-1-p),T("0x%02X"),u[i]);
}
delete[] u;
Append(p3,3,T("Usages: {%s}"),s);
}
}
delete[] caps;
}
if (numbers[1]) {
p2=Append(p,2,T("ValueCaps"));
HIDP_VALUE_CAPS *caps = new HIDP_VALUE_CAPS[numbers[1]];
HidP_GetValueCaps(rt,caps,&numbers[1],pd);
for (i=0; i<numbers[1]; i++) {
p3=OutCapsComm(p2,(HIDP_BUTTON_CAPS*)(caps+i));
int a=caps[i].Range.UsageMin;
int e=caps[i].IsRange ? caps[i].Range.UsageMax : a;
Append(p3,2,T("HasNull: %d, BitSize: %d, ReportCount: %d"),
caps[i].HasNull, caps[i].BitSize, caps[i].ReportCount);
Append(p3,2,T("LogicalMin: %d, LogicalMax: %d"),
caps[i].LogicalMin,caps[i].LogicalMax);
report=GetReport(rt,caps[i].ReportID,&reportlen);
if (report) {
ULONG l;
for(int j=a;j<=e;j++) {
HidP_GetUsageValue(rt,caps[i].UsagePage,caps[i].LinkCollection,j,&l,pd,report,reportlen);
Append(p3,3,T("UsageValue: %d {Usage(0x%02X)}"),l,j);
}
}
if (caps[i].UnitsExp || caps[i].Units || caps[i].PhysicalMin || caps[i].PhysicalMax) {
// Windows is floated with bugs here. While exp is a nibble value in the descriptor,
// i.e. -3 = 0x0D (not 0xFD), Windows doesn't sign-expand it to a useful value.
// So I do it myself.
int exp=(char)(caps[i].UnitsExp<<4)>>4;
Append(p3,2,T("UnitsExp: %d, Units: 0x%X"),
exp,caps[i].Units);
Append(p3,2,T("PhysicalMin: %d, PhysicalMax: %d"),
caps[i].PhysicalMin,caps[i].PhysicalMax);
if (report) {
LONG l;
for(int j=a;j<=e;j++) {
HidP_GetScaledUsageValue(rt,caps[i].UsagePage,caps[i].LinkCollection,j,&l,pd,report,reportlen);
int e=exp; // e gets trashed
Append(p3,3,T("ScaledUsageValue: %d * 10^%d %s {Usage(0x%02X)}"),
l,e,DecodeUnit(caps[i].Units,e),j);
// All Windows versions (Win98, Win2k, Win7-32) expose a bug here:
// While Logical Min/Max AND Physical Min/Max are non-negative,
// an out-of-range error occur in this combination:
// LogicalMin = 0, LogicalMax = 255, Value = 240
}
}
}
}
delete[] caps;
}
if (numbers[2]) {
p2=Append(p,3,T("DataIndices"));
if (report) {
ULONG len=numbers[2];
HIDP_DATA *data = new HIDP_DATA[len];
HidP_GetData(rt,data,&len,pd,report,reportlen); // Wo ist hier die Report-ID??
for (i=0; i<(int)len; i++) {
Append(p2,3,T("DataIndex: 0x%02X, RawValue: %d = 0x%X"),
data[i].DataIndex,data[i].RawValue,data[i].RawValue);
}
delete[] data;
}
}
reportlen=(&hidcaps.InputReportByteLength)[rt];
p2=Append(p,3,T("Reports read, len = %d (first byte == report ID)"),reportlen);
for (i=0; i<256; i++) if (ReportCache.all[rt][i]) HexDump(p2,ReportCache.all[rt][i],reportlen);
}
void TREE::HexDump(HTREEITEM p, const char*data, int len) const{
if (len<=0) return;
TCHAR s[256], *sp=s;
do {
sp+=wsprintf(sp,T("%02X "),(unsigned char)*data++);
if (--len && sp-s>elemof(s)-8) {
lstrcpy(sp,T("..."));
sp+=4;
break;
}
}while(len);
*--sp=0; // remove trailing space
Append(p,3,s);
}
void TREE::Build() {
GUID hidGuid; // GUID for HID driver (not the same as GUID_DEVCLASS_HIDCLASS)
HidD_GetHidGuid(&hidGuid);
HDEVINFO devs = SetupDiGetClassDevs(&hidGuid, NULL, 0, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);
if (devs==INVALID_HANDLE_VALUE) return;
for (int i=0; ; i++) {
SP_DEVICE_INTERFACE_DATA devinterface;
devinterface.cbSize = sizeof(devinterface);
if (!SetupDiEnumDeviceInterfaces(devs, 0, &hidGuid, i, &devinterface)) break;
SP_DEVINFO_DATA devinfo;
union{
SP_DEVICE_INTERFACE_DETAIL_DATA detail;
TCHAR space[MAX_PATH+4];
}u;
devinfo.cbSize = sizeof(devinfo);
u.detail.cbSize = sizeof(u.detail);
if (!SetupDiGetDeviceInterfaceDetail(devs, &devinterface, &u.detail, sizeof(u), 0, &devinfo)) {
Append(TVI_ROOT,0,T("can't get DevicePath for index %d"),i);
continue;
}
hDev = CreateFile(u.detail.DevicePath, GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (hDev == INVALID_HANDLE_VALUE)
hDev = CreateFile(u.detail.DevicePath,0, // zweiter Versuch für Tastaturen und Mäuse
FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (hDev == INVALID_HANDLE_VALUE) hDev = 0;
if (!hDev) {
Append(TVI_ROOT,0,T("can't open: %s"),u.detail.DevicePath);
continue;
}
if (HidD_GetPreparsedData(hDev,&pd)
&& HidP_GetCaps(pd,&hidcaps)) {
HTREEITEM p1=Append(TVI_ROOT,0,T("Idx: %d, %s"),i,DecodeUsage(0,*(DWORD*)&hidcaps.Usage));
// Device Strings
HIDD_ATTRIBUTES a;
a.Size=sizeof(a);
HidD_GetAttributes(hDev,&a);
HTREEITEM p2=Append(p1,1,T("Device Strings and IDs"));
Append(p2,0,T("DevicePath: `%s´"), u.detail.DevicePath);
WCHAR str[128];
if (!HidD_GetManufacturerString(hDev,str,256)) str[0]=0;
Append(p2,1,T("Manufacturer: `") USTR T("´ (%d = 0x%X)"), str,a.VendorID,a.VendorID);
if (!HidD_GetProductString(hDev,str,256)) str[0]=0;
Append(p2,1,T("Product: `") USTR T("´ (%d = 0x%X)"), str,a.ProductID,a.ProductID);
if (!HidD_GetSerialNumberString(hDev,str,256)) str[0]=0;
Append(p2,1,T("SerialNumber: `") USTR T("´ (%d = 0x%X)"), str, a.VersionNumber, a.VersionNumber);
// Report lengths
// p2=Append(p1,2,T("ReportLengths (inclusive ReportID)"));
// Append(p2,2,T("Input: %d bytes"),hidcaps.InputReportByteLength);
// Append(p2,2,T("Output: %d bytes"),hidcaps.OutputReportByteLength);
// Append(p2,2,T("Feature: %d bytes"),hidcaps.FeatureReportByteLength);
// HID-Baum einholen (Baum zeichnen)
ULONG len = hidcaps.NumberLinkCollectionNodes;
HIDP_LINK_COLLECTION_NODE* n = new HIDP_LINK_COLLECTION_NODE[len];
if (HidP_GetLinkCollectionNodes(n,&len,pd)) {
p2=Append(p1,2,T("LinkColletionNodes"));
InsertCollectionNode(p2,n,0,-1);
}
delete[] n;
// Show information for the Input, Output, and Feature report(s)
OutCaps(p1,T("Input"),HidP_Input);
OutCaps(p1,T("Output"),HidP_Output);
OutCaps(p1,T("Feature"),HidP_Feature);
HidD_FreePreparsedData(pd); pd=NULL;
}
CloseHandle(hDev);
FreeReports();
}
SetupDiDestroyDeviceInfoList(devs);
}
struct CONFIG Config;
TCHAR DtIniPath[MAX_PATH-8];
static void LoadConfig() {
HKEY k;
if (!RegOpenKey(HKEY_CURRENT_USER,T("Software\\h#s\\HidParse"),&k)) {
DWORD len=sizeof(Config);
RegQueryValueEx(k,T("Config"),NULL,NULL,(LPBYTE)&Config,&len);
len=sizeof(DtIniPath);
RegQueryValueEx(k,T("dt.ini"),NULL,NULL,(LPBYTE)DtIniPath,&len);
RegCloseKey(k);
}
}
static void SaveConfig() {
RegSetValue(HKEY_CURRENT_USER,T("Software\\h#s"),REG_SZ,T("haftmann#software"),17*sizeof(TCHAR));
HKEY k;
if (!RegCreateKey(HKEY_CURRENT_USER,T("Software\\h#s\\HidParse"),&k)) {
// Store information for human registry hackers
TCHAR s[64];
int l=LoadString(0,1,s,elemof(s));
RegSetValue(k,NULL,REG_SZ,s,l*sizeof(TCHAR));
// Save Window placement info and other things
RegSetValueEx(k,T("Config"),0,REG_BINARY,(LPBYTE)&Config,sizeof(Config));
// Save path-to-dt.ini
RegSetValueEx(k,T("dt.ini"),0,REG_SZ,(LPBYTE)DtIniPath,(lstrlen(DtIniPath)+1)*sizeof(TCHAR));
RegCloseKey(k);
}
}
static INT_PTR CALLBACK ConfigDlgProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
switch (Msg) {
case WM_INITDIALOG: {
SetDlgItemText(Wnd,10,DtIniPath);
}return TRUE;
case WM_COMMAND: switch (LOWORD(wParam)) {
case 1: GetDlgItemText(Wnd,10,DtIniPath,elemof(DtIniPath)); nobreak;
case 2: EndDialog(Wnd,wParam); break;
case 11: {
OPENFILENAME ofn;
TCHAR fname[MAX_PATH];
TCHAR dir[elemof(DtIniPath)];
TCHAR title[64];
lstrcpyn(fname,T("dt.ini"),elemof(fname));
GetDlgItemText(Wnd,10,dir,elemof(dir));
LoadString(0,11,title,elemof(title));
ZeroMemory(&ofn,sizeof(ofn));
*(BYTE*)&ofn.lStructSize=sizeof(ofn);
ofn.hwndOwner=Wnd;
ofn.lpstrFilter=T("HID Descriptor Table\0dt.ini\0");
ofn.lpstrFile=fname;
ofn.nMaxFile=elemof(fname);
ofn.lpstrInitialDir=dir;
ofn.lpstrTitle=title;
ofn.Flags=OFN_HIDEREADONLY|OFN_FILEMUSTEXIST|OFN_NOCHANGEDIR;
if (GetOpenFileName(&ofn)) {
fname[ofn.nFileOffset-1]=0;
SetDlgItemText(Wnd,10,fname);
}
}break;
}
}
return FALSE;
}
static HDEVNOTIFY hNotify;
// The main window is a dialog box with its client area filled with the TreeView.
static INT_PTR CALLBACK MainDlgProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
switch (Msg) {
case WM_INITDIALOG: {
if (Config.showCmd) {
// Reposition window as saved, but ShowWindow() is called later (don't flicker) TODO
WINDOWPLACEMENT wp;
wp.length=sizeof(wp);
GetWindowPlacement(Wnd,&wp);
Config.winpos>>wp.rcNormalPosition;
wp.showCmd=Config.showCmd;
SetWindowPlacement(Wnd,&wp);
HMENU m=GetMenu(Wnd);
CheckMenuItem(m,21,Config.flags&1?MF_CHECKED:MF_UNCHECKED);
CheckMenuItem(m,24,Config.flags&2?MF_CHECKED:MF_UNCHECKED);
WORD v=(WORD)GetVersion();
v=v>>8|v<<8; // swap(), htons() - a smart x86 compiler would generate "xchg ah,al"
if (v<0x501) EnableMenuItem(m,24,MF_GRAYED);
}
DEV_BROADCAST_DEVICEINTERFACE fi;
fi.dbcc_size=sizeof(fi);
fi.dbcc_devicetype=DBT_DEVTYP_DEVICEINTERFACE;
// HidD_GetHidGuid(&fi.dbcc_classguid);
fi.dbcc_classguid=GUID_DEVINTERFACE_USB_DEVICE;
hNotify=RegisterDeviceNotification(Wnd,&fi,DEVICE_NOTIFY_WINDOW_HANDLE);
tree.hTree=GetDlgItem(Wnd,10);
SP_CLASSIMAGELIST_DATA ild;
ild.cbSize=sizeof(ild);
SetupDiGetClassImageList(&ild);
int iImage;
SetupDiGetClassImageIndex(&ild,&GUID_DEVCLASS_HIDCLASS,&iImage);
HICON ico=ImageList_GetIcon(ild.ImageList,iImage,ILD_TRANSPARENT);
HIMAGELIST il=ImageList_Create(16,16,ILC_MASK,4,0);
ImageList_AddIcon(il,ico);
for (int i=65; i<68; i++) {
ImageList_AddIcon(il,LoadIcon(hInst,MAKEINTRESOURCE(i)));
}
TreeView_SetImageList(tree.hTree,il,TVSIL_NORMAL);
TreeView_SetIndent(tree.hTree,0); // less waste of space?
tree.Build();
}return TRUE;
case WM_SIZE: {
tree.hTree=GetDlgItem(Wnd,10);
SetWindowPos(tree.hTree,0,0,0,GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam),SWP_NOMOVE|SWP_NOZORDER);
}break;
case WM_COMMAND: switch (LOWORD(wParam)) {
case 2: {
WINDOWPLACEMENT wp;
wp.length=sizeof(wp);
GetWindowPlacement(Wnd,&wp);
Config.winpos=wp.rcNormalPosition;
Config.showCmd=(char)wp.showCmd;
SaveConfig();
UnregisterDeviceNotification(hNotify);
EndDialog(Wnd,wParam);
}break;
case 21: {
Config.flags^=1;
CheckMenuItem(GetMenu(Wnd),21,Config.flags&1?MF_CHECKED:MF_UNCHECKED);
}break;
case 24: {
Config.flags^=2;
CheckMenuItem(GetMenu(Wnd),24,Config.flags&2?MF_CHECKED:MF_UNCHECKED);
}break;
case 22: {
TreeView_DeleteAllItems(tree.hTree);
tree.Build();
}break;
case 23: if (DialogBox(0,MAKEINTRESOURCE(23),Wnd,ConfigDlgProc)) SaveConfig();
break;
case 99: MessageBox(Wnd,
T("Parses HID devices using HidD and HidP functions\nhaftmann#software, 04/13"),
T("About HidParse"),MB_OK|MB_ICONINFORMATION); break;
}break;
case WM_DEVICECHANGE: switch (wParam) {
case DBT_DEVICEARRIVAL:
case DBT_DEVICEREMOVECOMPLETE: {
if (Config.flags&1) SendMessage(Wnd,WM_COMMAND,22,0);
}break;
}break;
}
return FALSE;
}
/********************
* main entry point *
********************/
EXTERN_C void CALLBACK WinMainCRTStartup() {
hInst=GetModuleHandle(NULL);
InitCommonControls();
Config.flags=0x01;
HINSTANCE h=LoadLibraryA("HID");
if (h) {
pHidD_GetInputReport=(BOOL(WINAPI*)(HANDLE,char*,int))GetProcAddress(h,"HidD_GetInputReport");
FreeLibrary(h);
}
lstrcpyn(DtIniPath,T("C:\\Programme\\MSVC\\DDK\\tools\\HidGen"),elemof(DtIniPath));
LoadConfig();
ud.Init(DtIniPath);
ExitProcess((UINT)DialogBox(0,MAKEINTRESOURCE(100),0,MainDlgProc));
}
Vorgefundene Kodierung: ANSI (CP1252) | 4
|
|