#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
|
|