Source file: /~heha/hs/UNI-T/he2325u.zip/src/he2325u.cpp

#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;
}
Detected encoding: ANSI (CP1252)4
Wrong umlauts? - Assume file is ANSI (CP1252) encoded