Source file: /~heha/mb-iwp/Kleinkram/radariq.zip/src/comsel.cpp

#include "comsel.h"
#include <windowsx.h>
#include <shlwapi.h>
#include <setupapi.h>
#include <devguid.h>
#include <cfgmgr32.h>	//CM_Get_Parent
#include "util/util.h"	//elemof

DWORD decodeBaud(BYTE b) {
 BYTE k,m=b>>4&3,n=b&15;
 switch (b>>6) {
  case 0: k=2; break;
  case 1: k=6; break;
  case 2: k=3; break;
  default: switch (b&63) {
   case 1: return 110;
   default: k=n;
  }
 }
 DWORD ret=1U<<n;
 if (m) do ret*=3; while(--m);
 if (k) do ret*=5; while(--k);
 return ret;
}

BYTE encodeBaud(DWORD rate) {
 switch (rate) {
  case 0: return 0;	// cannot encode
  case 110: return 0xC1;
 }
 BYTE k=0,m=0,n=0;
 while (!(rate&1)) {rate>>=1; ++n;}	// count trailing zeroes
 while (!(rate%3)) {rate/=3; ++m;}
 while (!(rate%5)) {rate/=5; ++k;}
 if (rate!=1) return 0;	// cannot encode
 if (n>15) return 0;
 if (m>3) return 0;
 switch (k) {
  case 2: return 0x00|m<<4|n;
  case 6: return 0x40|m<<4|n;
  case 3: return 0x80|m<<4|n;
  default: if (k==n) return 0xC0|m<<4|n;
 }
 return 0;	// cannot encode
}

#define DEF_BAUDCODES 0x17,0x18,0x19,0x28,0x29,0x2A,0x2B,0x2C

TCHAR*getExeName(TCHAR exepath[MAX_PATH]) {
 GetModuleFileName(0,exepath,MAX_PATH);
 TCHAR*n=PathFindFileName(exepath),	// name only, no path
      *e=PathFindExtension(n);		// onto last '.' or terminating '\0'
 if (e!=n) *e=0;			// kill extension but avoid misbehaviour on ".htaccess"-like name
 return n;
}

ComSel::ComSel(BYTE p, BYTE bc):showcmd(0),port(p),baudcode(bc) {load();}

bool ComSel::load() {
 HKEY k1,k2;
 DWORD size=sizeof*this;
 if (RegOpenKey(HKEY_CURRENT_USER,TEXT("Software\\h#s"),&k1)) return false;
 TCHAR exepath[MAX_PATH];
 if (!RegOpenKey(k1,getExeName(exepath),&k2)) {
  if (RegQueryValueEx(k2,TEXT("ComSel"),0,0,(LPBYTE)this,&size)) size=0;
  RegCloseKey(k2);
 }
 RegCloseKey(k1);
 return !!size;
}

static void GetStringFileInfo(const TCHAR*subkey,TCHAR*s,UINT slen) {
 HGLOBAL r=LoadResource(0,FindResource(0,MAKEINTRESOURCE(1),RT_VERSION));
 void*p=LockResource(r);
 DWORD lang;	// = GetUserDefaultUILanguage() geht nicht unter Windows 98
 GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_IDEFAULTLANGUAGE|LOCALE_RETURN_NUMBER,(TCHAR*)&lang,4);
 for(;;) {
  wnsprintf(s,slen,TEXT("\\StringFileInfo\\%08x\\%s"),MAKELONG(1200,lang),subkey);
  void*res;
  UINT rlen=slen;
  if (VerQueryValue(p,s,&res,&rlen)) {
   lstrcpyn(s,(TCHAR*)res,slen);
   wnsprintf(s,slen,TEXT("%s"),res);
   break;
  }
  *s=0;
  if (lang==0x0409) break;
  lang=0x0409;	// englisch-USA
 }
 UnlockResource(r);
 FreeResource(r);
}

bool ComSel::save() {
 HKEY k1,k2;
// Schlüssel-Zweig öffnen
 if (RegCreateKey(HKEY_CURRENT_USER,TEXT("Software\\h#s"),&k1)) return false;
 TCHAR s[64];
// eine Beschreibung schreiben (damit der Anwender weiß, worum es geht)
 GetStringFileInfo(TEXT("CompanyName"),s,elemof(s));
 if (*s) RegSetValue(k1,0,REG_SZ,s,(lstrlen(s)+1)*sizeof(TCHAR));
 TCHAR exepath[MAX_PATH];
 if (!RegCreateKey(k1,getExeName(exepath),&k2)) {
  GetStringFileInfo(TEXT("FileDescription"),s,elemof(s));
  if (*s) RegSetValue(k2,0,REG_SZ,s,(lstrlen(s)+1)*sizeof(TCHAR));
  RegSetValueEx(k2,TEXT("ComSel"),0,REG_BINARY,(PBYTE)this,sizeof*this);
  RegCloseKey(k2);
 }
 RegCloseKey(k1);
 return true;
}

static void PlaceWnd(HWND hWnd,const Rect<short>&r) {
 WINDOWPLACEMENT wp;
 wp.length=sizeof wp;
 GetWindowPlacement(hWnd,&wp);
 r.setRECT(wp.rcNormalPosition);
 SetWindowPlacement(hWnd,&wp);
// Im Gegensatz zu MoveWindow sollte SetWindowPlacement dafür sorgen,
// dass das Fenster nicht außerhalb des Desktops platziert wird.
}

static char GetWndPlace(HWND hWnd,Rect<short>&r) {
 WINDOWPLACEMENT wp;
 wp.length=sizeof wp;
 GetWindowPlacement(hWnd,&wp);
 r = wp.rcNormalPosition;
 return wp.showCmd;
}

// Sucht Index in nach ItemData sortierter Liste,
// dessen ItemData geradeso größer t ist
static int FindIndex(HWND hCombo, LPARAM t) {
 int l=0, r=ComboBox_GetCount(hCombo);
 while(l!=r) {
  int m=(l+r)>>1;
  LPARAM itemdata=ComboBox_GetItemData(hCombo,m);
  if (itemdata==t) return m;	// bei Treffer sofort raus (= chaotisches Einsortieren gleicher Werte)
  if (itemdata>t) r=m; else l=m+1;	// Sonst weiter teilen
 }
 return l;
}

static SP_CLASSIMAGELIST_DATA ild;

HANDLE ComSel::Open(UINT ComNr,DWORD dwFlags) {
 TCHAR s[12];
 wnsprintf(s,elemof(s),TEXT("\\\\.\\COM%u"),ComNr+1);
 HANDLE h=CreateFile(s,GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,dwFlags,0);
 if (h==INVALID_HANDLE_VALUE) h=0;
 return h;
}

static void FillComboComPorts(HWND hCombo,UINT nr) {
// serielle Schnittstellen (neu) listen (bei jedem WM_DEVICECHANGE bspw. für USB)
 ComboBox_ResetContent(hCombo);
 HANDLE devs=SetupDiGetClassDevs((LPGUID)&GUID_DEVCLASS_PORTS,0,0,DIGCF_PRESENT);
 if (devs==INVALID_HANDLE_VALUE) return;
 SP_DEVINFO_DATA devInfo;
 devInfo.cbSize=sizeof devInfo;
 TCHAR s[80];
 COMBOBOXEXITEM cbei;
 cbei.mask=CBEIF_IMAGE|CBEIF_LPARAM|CBEIF_SELECTEDIMAGE|CBEIF_TEXT/*|CBEIF_IOVERLAY*/;
 cbei.pszText=s;
 for (DWORD i=0; SetupDiEnumDeviceInfo(devs,i,&devInfo); i++) {
  HKEY hKey=SetupDiOpenDevRegKey(devs,&devInfo,DICS_FLAG_GLOBAL,0,DIREG_DEV,KEY_READ);
  if (hKey==INVALID_HANDLE_VALUE) continue;
  TCHAR t[16];	// noch ein Unicode-String, wird später noch mal gebraucht
  *t=0;
  DWORD len=sizeof(t);
  RegQueryValueEx(hKey,TEXT("PortName"),0,0,(LPBYTE)t,&len);
  RegCloseKey(hKey);
  if (*t!='C') continue;	// Fehlschläge und LPTx ausfiltern
  cbei.lParam=StrToInt(t+3)-1;	// nullbasierte Nummer
  DEVINST parent;
  const GUID*pguid=&GUID_DEVCLASS_PORTS;
// Neu: Klassen-Icon der übergeordneten Geräteklasse anzeigen (USB oder Steckkarte; bei com0com "Anschlüsse")
  if (!CM_Get_Parent(&parent,devInfo.DevInst,0)) {
   ULONG l=sizeof s;
   if (!CM_Get_DevNode_Registry_Property(parent,CM_DRP_CLASSGUID,0,s,&l,0)) {
    GUID guid;
#ifdef UNICODE
    if (!CLSIDFromString(s,&guid)) pguid=&guid;	// Zeiger ändern bei Erfolg
#else
    WCHAR u[40];
    MultiByteToWideChar(CP_UTF8,0,s,-1,u,elemof(u));
    if (!CLSIDFromString(u,&guid)) pguid=&guid;
#endif
   }
  }
  SetupDiGetClassImageIndex(&ild,pguid,&cbei.iImage);
  cbei.iSelectedImage=cbei.iImage;
//Das was im Gerätemanager zu sehen ist: Lang aber hilfreicher als nur COMx
  SetupDiGetDeviceRegistryProperty(devs,&devInfo,SPDRP_FRIENDLYNAME,0,(PBYTE)s,sizeof s,0);	// ab Windows 2k?
//Die COM-Portnummer sollte da enthalten sein. Falls nicht wird sie angehangen
  if (!StrStr(s,t)) {	// Achtung! StrStr(Heuhaufen,Nadel) vs. strstr(needle,haystack)
   int l=lstrlen(s);
   wnsprintf(s+l,elemof(s)-l,TEXT(" (%s)"),t);
  }
  cbei.iItem=FindIndex(hCombo,cbei.lParam);
  SendMessage(hCombo,CBEM_INSERTITEM,0,(LPARAM)&cbei);
  if (UINT(cbei.lParam)==nr) ComboBox_SetCurSel(hCombo,cbei.iItem);
 }
 SetupDiDestroyDeviceInfoList(devs);
}

static void FillComboBaudRates(HWND hCombo,BYTE bc) {
 ComboBox_ResetContent(hCombo);
 static const BYTE defs[]={DEF_BAUDCODES};
 bool found=false;
 TCHAR s[16];
 for (int i=0; i<elemof(defs); i++) {
  DWORD cur=decodeBaud(defs[i]);
  if (cur%1000000) wnsprintf(s,elemof(s),TEXT("%u"),cur);
  else wnsprintf(s,elemof(s),TEXT("%u M"),cur/1000000);
  int idx=ComboBox_AddString(hCombo,s);
  ComboBox_SetItemData(hCombo,idx,defs[i]);
  if (defs[i]==bc) {ComboBox_SetCurSel(hCombo,idx); found=true;}
 }
 if (!found) {
  wnsprintf(s,elemof(s),TEXT("%u"),decodeBaud(bc));
  SetWindowText(hCombo,s);	// set editable value
 }
}

void ComSel::dialogHandler(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) {
 switch (msg) {
  case WM_INITDIALOG: {
   ild.cbSize=sizeof ild;
   SetupDiGetClassImageList(&ild);
   if (showcmd) PlaceWnd(hDlg,r);
   HWND hCombo=GetDlgItem(hDlg,101);
   SendMessage(hCombo,CBEM_SETIMAGELIST,0,(LPARAM)ild.ImageList);
   FillComboComPorts(hCombo,port);
// Wenn nur 1 COM-Port da ist, und nicht selektiert, wird dieses sogleich selektiert
   if (ComboBox_GetCount(hCombo)==1 && ComboBox_GetCurSel(hCombo)) {
    ComboBox_SetCurSel(hCombo,0);
    SendMessage(hDlg,WM_COMMAND,MAKELONG(101,CBN_SELCHANGE),(LPARAM)hCombo);
   }
   if (ComboBox_GetCurSel(hCombo)==CB_ERR) EnableWindow(GetDlgItem(hDlg,IDOK),FALSE);
   FillComboBaudRates(GetDlgItem(hDlg,102),baudcode);
  }break;

  case WM_DEVICECHANGE: SetTimer(hDlg,1,1500,0); break;

  case WM_TIMER: switch (wParam) {
   case 1: {
    KillTimer(hDlg,(UINT)wParam);
    FillComboComPorts(GetDlgItem(hDlg,101),port);
   }break;
  }break;

  case WM_COMMAND: switch (wParam) {
   case MAKELONG(101,CBN_SELCHANGE): {
    port=(BYTE)ComboBox_GetItemData((HWND)lParam,ComboBox_GetCurSel((HWND)lParam));
    EnableWindow(GetDlgItem(hDlg,IDOK),true);
   }break;
   case MAKELONG(102,CBN_EDITCHANGE): {
    BYTE b=encodeBaud(GetDlgItemInt(hDlg,102,0,FALSE));
    if (b) baudcode=b;
    EnableWindow(GetDlgItem(hDlg,IDOK),!!b);
   }break;
   case MAKELONG(102,CBN_SELCHANGE): {
    baudcode=(BYTE)ComboBox_GetItemData((HWND)lParam,ComboBox_GetCurSel((HWND)lParam));
   }break;
   case IDOK:
   case IDCANCEL: {
    showcmd=GetWndPlace(hDlg,r);
    save();
    EndDialog(hDlg,int(wParam));
   }break;
  }break;
  case WM_DESTROY: {
   SetupDiDestroyClassImageList(&ild);
  }break;
 }
}

Detected encoding: ANSI (CP1252)4
Wrong umlauts? - Assume file is ANSI (CP1252) encoded