#define WIN32_LEAN_AND_MEAN
#include "he2325u.h"
#include <setupapi.h>
//#include <stdio.h>
//#include <tchar.h>
extern "C" {
#include <hidsdi.h>
#include <hidpi.h>
}
#include "gnul.h"
#include <shellapi.h>
#include <shlwapi.h>
/* PROBLEM: Zumindest Win98 weist HID-Geräte nicht per USB-Port zu!
Also: Bei nur einem angesteckten Gerät wandert das DeviceInterfaceDetail unverändert mit.
Erst beim zweiten Gerät wird ein zweites DeviceInterfaceDetail angelegt.
Nonpresent_Devices lassen sich nicht mit CreateFile() öffnen, wie soll man dann an VID+PID herankommen?
Auch später weist Win98 die IDs nicht per Port zu, sondern immer von vorn nach hinten: Schönes Chaos!
Deshalb ist der hier betriebene Aufwand unter Win98 vollkommen für die Katz'.
Windows (alle Versionen) führt bei Mehrfachgeräten einen "RefCount"-Wert in der Registrierung mit,
der mir aber nicht zugreifbar erscheint. Hat jemand eine Idee zur Verwendung?
*/
#define T(x) TEXT(x)
#define elemof(x) (sizeof(x)/sizeof(*(x)))
#define nobreak
static HINSTANCE ghInst;
struct NewDev{
NewDev *next;
HANDLE h;
SP_DEVINFO_DATA info;
SP_DEVICE_INTERFACE_DATA did;
};
#pragma bss_seg(".shared")
#pragma comment(linker,"/SECTION:.shared,RWS")
static HINSTANCE hRunas;
#pragma bss_seg()
static HANDLE HeInternal(char List[HE_NUM_MAX], int n=-1, DWORD FlagsAndAttributes=0) {
HANDLE ret=INVALID_HANDLE_VALUE;
if (n>HE_NUM_MAX) {
SetLastError(ERROR_INVALID_PARAMETER);
return ret;
}
GUID hidGuid;
TCHAR sKey[16]; // "HeEnum", non-localizable
LoadString(ghInst,1,sKey,elemof(sKey));
RtlFillMemory(List,HE_NUM_MAX,NOT_AVAILABLE); // default to non-available devices, i.e. free slots
NewDev *NewList = NULL,cur;
HidD_GetHidGuid(&hidGuid);
HDEVINFO Devs = SetupDiGetClassDevs(&hidGuid,NULL,NULL,DIGCF_DEVICEINTERFACE);
if (Devs==INVALID_HANDLE_VALUE) return ret; // Cannot proceed
cur.info.cbSize = sizeof cur.info;
cur.did.cbSize = sizeof cur.did; // .Flags contains SPINT_ACTIVE,SPINT_DEFAULT,SPINT_REMOVED
for (int i=0; SetupDiEnumDeviceInterfaces(Devs,NULL,&hidGuid,i,&cur.did); i++){
DWORD size;
// Query the associated <HeEnum> key for matching to <n>
HKEY k=SetupDiOpenDeviceInterfaceRegKey(Devs,&cur.did,0,KEY_QUERY_VALUE);
int nn=0; // this human-readable 1-based ID
if (k!=INVALID_HANDLE_VALUE) {
DWORD nl=4;
RegQueryValueEx(k,sKey,NULL,NULL,(BYTE*)&nn,&nl);
RegCloseKey(k);
if ((unsigned)nn>HE_NUM_MAX) nn=0; // invalid entry (made by a registry hacker)
}
if (nn) { // already enumerated (1..256)
List[nn-1]=AVAILABLE; // This index is used up
if (n>0 && n!=nn) continue; // Not the device we want
}
// Non-active devices cannot be processed
if (!(cur.did.Flags&SPINT_ACTIVE)) {
if (nn) { // indicating non-active but known device
List[nn-1]=REMOVED;
if (n>=0) {
ret=(HANDLE)REMOVED;
goto ex; // exiting for real open attempt
}
}
continue;
}
// For the device we want, open it.
// For an un-enumerated device, check whether this is a new HOITEK HE2325U device.
struct{
SP_DEVICE_INTERFACE_DETAIL_DATA h;
TCHAR fill[MAX_PATH-1];
}Details;
size=sizeof(Details);
#ifdef _WIN64
Details.h.cbSize = 8;
#else
Details.h.cbSize = sizeof(Details.h.cbSize)+sizeofTCHAR;
#endif
// SetupDiGetDeviceInterfaceDetailA really needs a cbSize of 5, whereas
// SetupDiGetDeviceInterfaceDetailW needs 6 for this value.
SetupDiGetDeviceInterfaceDetail(Devs,&cur.did,&Details.h,size,NULL,&cur.info);
// Open exclusive, non-shareable. Sharing one HE2325U device seems not to be useful.
cur.h=CreateFile(Details.h.DevicePath,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FlagsAndAttributes,NULL);
if (cur.h==INVALID_HANDLE_VALUE) {
if (nn) { // indicating non-openable but known device
List[nn-1]=CANNOT_OPEN;
if (n>=0) {
ret=(HANDLE)CANNOT_OPEN;
goto ex; // exiting for real open attempt
}
} // non-openable AND unknown devices cannot be allocated here, even if this is a new HE2325U device
continue;
}
HIDD_ATTRIBUTES Attributes;
Attributes.Size = sizeof(Attributes);
if (HidD_GetAttributes(cur.h,&Attributes)
&& (*(DWORD*)&Attributes.VendorID == 0x249004FA // the low-speed device
|| *(DWORD*)&Attributes.VendorID == 0xE0081A86)) { // the full-speed device
if (!nn) { // Found a new, not enumerated device!
NewDev *p=new NewDev;
*p=cur; // Copy structure for later setting the <HeEnum> key
p->next=NewList; // We don't know yet whether this is our device, so close later
NewList=p; // Link node into list
}else if (n>=0) { // otherwise, we have the right device
ret=cur.h; // save the handle for returning
n=-1; // in case of n==0 (or registry-hacked equal numbers), don't match again
ex: if (!NewList) break; // Further looping only when non-enumerated device(s) found, to fill up the <List> array
}else CloseHandle(cur.h); // don't save handle but close - for HeEnum() invocation
}else CloseHandle(cur.h); // This is not a HOITEK HE2325U
}
// Now we have all enumerated devices as !=-1 in <List>, we can assign new unique numbers
while (NewDev *p=NewList) { // While we have new items
char*b=(char*)memchr(List,NOT_AVAILABLE,HE_NUM_MAX); // Find a free slot
if (b) { // In rare case we have eaten up all 256 slots, do nothing
*b=AVAILABLE; // Allocate free slot
int nn=(int)(b-List+1); // Make a one-based index
if (!hRunas) { // let ShellExecute only once
HKEY k=SetupDiCreateDeviceInterfaceRegKey(Devs,&p->did,0,KEY_SET_VALUE,NULL,NULL);
if (k!=INVALID_HANDLE_VALUE) {
// This makes (in Windows 2000, XP, Vista) the following entry:
// HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\DeviceClasses\{4d1e55b2-f16f-11cf-88cb-001111000030}\##?#HID#Vid_04fa&Pid_2490#5&8074e6f&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}\#\Device Parameters\HeEnum:DWORD
// and in Windows 98:
// HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\DeviceClasses\{4d1e55b2-f16f-11cf-88cb-001111000030}\##.#000000000000005c#{4d1e55b2-f16f-11cf-88cb-001111000030}\#\Device Parameters\HeEnum:DWORD
// PROBLEM: Windows Vista+ UAC denies this write access in non-elevated user mode!
// TODO: A solution that doesn't require elevated mode at all.
RegSetValueEx(k,sKey,0,REG_DWORD,(BYTE*)&nn,4);
RegCloseKey(k);
#ifdef _DEBUG
MessageBoxA(0,"Hier!",NULL,0);
#endif
TCHAR s[64],t[64];
LoadString(ghInst,2,t,elemof(t));
int l=wnsprintf(s,elemof(s),t,nn)+1;
if (!GnulIsAnsi()) l<<=1;
SetupDiSetDeviceRegistryProperty(Devs,&p->info,SPDRP_FRIENDLYNAME,(PBYTE)s,l);
SP_DEVINSTALL_PARAMS dip;
dip.cbSize = GnulIsAnsi() ? sizeof(SP_DEVINSTALL_PARAMS_A) : sizeof(SP_DEVINSTALL_PARAMS_W);
SetupDiGetDeviceInstallParams(Devs,&p->info,&dip);
dip.Flags |= DI_PROPERTIES_CHANGE;
SetupDiSetDeviceInstallParams(Devs,&p->info,&dip); // keine Wirkung (bei mir unter XP), liefert TRUE
}else{ // Let an elevated process do the work, and then exit
WCHAR p[MAX_PATH];
lstrcpy(p+GetModuleFileName(ghInst,p,elemof(p)-7),L",ShEnum");
hRunas=ShellExecute(0,L"runas",L"rundll32.exe",p,NULL,SW_SHOW);
}
}
if (!n || n==nn) { // The index we have searched?
ret=p->h;
n=-1; // In case of n==0, don't match again
}else{ // Not the index searched
CloseHandle(p->h);
}
}
NewList=p->next; // Unlink node
delete p; // Free node
}
SetupDiDestroyDeviceInfoList(Devs);
return ret;
}
// This is a quick-and-dirty workaround for the Vista/7 UAC problem.
// This function is run in elevated context (after user's confirmation).
FUNC(void) ShEnumW(HWND,HINSTANCE,LPWSTR,int) {
char List[HE_NUM_MAX]; // List of found and enumerated devices
HeInternal(List);
}
FUNC(void) HeEnum(char List[HE_NUM_MAX]) {
HeInternal(List);
}
FUNC(HANDLE) HeOpen(int n, long baud, DWORD FlagsAndAttributes) {
char List[HE_NUM_MAX]; // List of found and enumerated devices
HANDLE ret=HeInternal(List,n,FlagsAndAttributes);
if ((int)ret>=0 && baud) {
HEFEATUREREPORT FeatureReport={0,baud,3};
if (!HidD_SetFeature(ret,&FeatureReport,sizeof(FeatureReport))) {
CloseHandle(ret); ret=INVALID_HANDLE_VALUE;
}
}
return ret;
}
FUNC(int) HeRead(HANDLE h, BYTE *buf, int len, UINT IntervalTimeOut, UINT TotalTimeOut, LPOVERLAPPED o) {
HEINPUTREPORT InputReport;
UINT tic=GetTickCount(); // record timestamp of function entry
int ret=0;
InputReport.ReportID=0;
while (len>=7) { // enough space in buffer to copy complete record data?
if (!HeReadReport(h,&InputReport,o)) return -1; // this should block 8 ms
int l=InputReport.NumBytes&0x07;
UINT tic2,toc=GetTickCount(); // take timestamp of HeReadReport() return
if (l) {
RtlCopyMemory(buf,InputReport.data,l);
buf+=l;
len-=l;
ret+=l;
tic2=toc; // record timestamp when bytes were received
}else if (ret && toc-tic2>=IntervalTimeOut) break; // bail out when interval too long
if (toc-tic>=TotalTimeOut) break; // bail out when total time expired
}
return ret;
}
FUNC(BOOL) HeWrite(HANDLE h, const BYTE *data, int dlen, LPOVERLAPPED o) {
HEOUTPUTREPORT OutputReport;
OutputReport.ReportID=0;
while (dlen) {
int l=dlen;
if (l>7) l=7;
OutputReport.NumBytes=l|0xF0;
RtlCopyMemory(OutputReport.data,data,l);
if (!HeWriteReport(h,&OutputReport,o)) return FALSE;
dlen-=l;
data+=l;
}
return TRUE;
}
FUNC(BOOL) HeReadReport(HANDLE h, HEINPUTREPORT *report, LPOVERLAPPED o) {
HidD_FlushQueue(h);
DWORD br;
return (ReadFile(h,report,sizeof(HEINPUTREPORT),&br,o)
|| GetLastError()==ERROR_IO_PENDING
&& !WaitForMultipleObjects(2,&o->hEvent,FALSE,INFINITE)
&& GetOverlappedResult(h,o,&br,FALSE))
&& br==sizeof(HEINPUTREPORT);
}
FUNC(BOOL) HeWriteReport(HANDLE h, const HEOUTPUTREPORT *report, LPOVERLAPPED o) {
DWORD bw;
return (WriteFile(h,report,sizeof(HEOUTPUTREPORT),&bw,o)
|| GetLastError()==ERROR_IO_PENDING
&& !WaitForMultipleObjects(2,&o->hEvent,FALSE,INFINITE)
&& GetOverlappedResult(h,o,&bw,FALSE))
&& bw==sizeof(HEOUTPUTREPORT);
}
EXTERN_C BOOL WINAPI _DllMainCRTStartup(HINSTANCE hInst,ULONG Reason,LPVOID) {
switch (Reason) {
case DLL_PROCESS_ATTACH: {
ghInst=hInst;
DisableThreadLibraryCalls(hInst);
GnulInit(hInst,NULL,NULL);
}break;
}
return TRUE;
}
Vorgefundene Kodierung: ANSI (CP1252) | 4
|
|