Source file: /~heha/hs/Funkuhr.zip/src/DlgHardware.c

/********************************
 * Projekt: Funkuhr DCF77	*
 * Eigenschaftsseite äHardwareô	*
 * Auswahl serielles oder	*
 * Parallelport + Leitung	*
 ********************************/

#include "Funkuhr.h"
#include <devguid.h>
//#include <dbt.h>

/*
// Eine Reihe aufeinanderfolgender Fenster ein- oder ausschalten
static void EnableWindows(HWND Wnd, UINT count, BOOL state) {
 for (; count; Wnd=GetNextSibling(Wnd),count--) {
  EnableWindow(Wnd,state);
 }
}
*/
// Je nach Config.Where Dialogelemente sichtbar machen
static void ShowWhere(HWND Wnd) {
 int i;
 for (i=0; i<6; i++) {
  int j=i*0x10+0x20;
  int k;
  HWND w;
  for (w=GetDlgItem(Wnd,j); w && ((k=(short)GetDlgCtrlID(w))==-1 || k<j+0x10); w=GetNextSibling(w)) {
   ShowWindow(w,Config.Where==i ? SW_SHOW : SW_HIDE);
  }
 }
}

#ifdef UNICODE
int ComboBox_AddStringA(HWND Wnd, const char*s) {
 TCHAR buf[64];
 MultiByteToWideChar(CP_ACP,0,s,-1,buf,elemof(buf));
 return ComboBox_AddString(Wnd,buf);
}
#else
# define ComboBox_AddStringA ComboBox_AddString
#endif

// Hilfsfunktion: Fⁿllt Combobox mit doppelnullterminierter Item-Liste
static void cbFillA(HWND Wnd,const char*Items,int CurSel) {
 for(;*Items;Items+=lstrlenA(Items)+1) ComboBox_AddStringA(Wnd,Items);
 ComboBox_SetCurSel(Wnd,CurSel);
}

// Hilfsfunktion: Formatierte Textzeile hinzufⁿgen
// Wenn t==NULL dann 1 uninterpretierten String-Parameter annehmen
// Wenn t<65536 dann t = String-Resource-ID
// Liefert true wenn itemdata==curitem (um weitere Aktionen zu starten)
static bool cbAddItem(HWND cb, int itemdata, int curitem, PCTSTR t, ...) {
 int idx;
 va_list va;
 va_start(va,t);
 if (t) {
  TCHAR s[64];
  if (IS_INTRESOURCE(t)) {
   TCHAR r[64];
   LoadString(ghInstance,(UINT)t,r,elemof(r));
   t=r;
  }
  wvnsprintf(s,elemof(s),t,va);
  t=s;
 }else t=va_arg(va,PCTSTR);
 idx=ComboBox_AddString(cb,t);
 ComboBox_SetItemData(cb,idx,itemdata);
 if (itemdata==curitem) {
  ComboBox_SetCurSel(cb,idx);
  return true;
 }
 return false;
}

static const WORD DefAddrs[]={0x378,0x278,0x3BC,0x201};

// Fⁿllt Combobox mit typischen Parallelportadressen
static void cbFillAddrs(HWND Wnd) {
 int i;
 bool set=false;
 for (i=0; i<elemof(DefAddrs); i++) {
  if (cbAddItem(Wnd,DefAddrs[i],Config.ParallelAddr,
    i<3 ? T("%Xh (%u, LPT%u)") : MAKEINTRESOURCE(11),	// "%Xh (Joystick)"
    DefAddrs[i],DefAddrs[i],i+1)) set=true;
 }
 if (!set) wndSetText(Wnd,T("%03Xh"),Config.ParallelAddr);
}

// Fⁿllt Combobox mit Parallelport-Bits
static void cbFillBits(HWND Wnd, BYTE Current) {
 static const char ParallelLines[]=	// ab Bitadresse 0
  "D0 (2)\0D1 (3)\0D2 (4)\0D3 (5)\0D4 (6)\0D5 (7)\0D6 (8)\0D7 (9)\0"
  "ERR (15)\0ONL (13)\0PE (12)\0ACK (10)\0BSY (11)\0"
  "STB (1)\0AF (14)\0INI (16)\0SEL (17)\0";
 static const char JoystickLines[]=	// ab Bitadresse 4, nur EingΣnge
  "TA1 (2)\0TA2 (7)\0TB1 (10)\0TB2 (14)\0";
 int i,j;
 const char *p;
 ComboBox_ResetContent(Wnd);
 if (Current&0x80) {	// UsbPrn-Modus
  p=ParallelLines;
  for (i=0; i<11; i++) {
   if (i>=8) ComboBox_AddStringA(Wnd,p);
   p+=lstrlenA(p)+1;
  }
  ComboBox_SetCurSel(Wnd,Current-0x80-013);
 }else{			// InpOut32-Modus
  for (i=0; i<elemof(DefAddrs); i++) {
   if (Config.ParallelAddr==DefAddrs[i]) {
    i= i==3 ? 4 : 0;	// Start-Bitadresse
    p= i ? JoystickLines : ParallelLines;
    if (i && !Current) {	// Sonderfall Jostick-Stromversorgung
     ComboBox_AddStringA(Wnd,"Ucc (1,8,9,15)");
     ComboBox_SetCurSel(Wnd,0);
     return;
    }
    for(;*p;p+=lstrlenA(p)+1) {
     j=ComboBox_AddStringA(Wnd,p);
     ComboBox_SetItemData(Wnd,j,i);	// Bitnummer zuordnen
     if (i==Current) ComboBox_SetCurSel(Wnd,j);
     i++; if (i==8) i+=3;	// nΣchstes Bit, 3 fehlende IN-Leitungen ⁿberspringen
    }
    return;
   }
  }
  for (i=0; i<8; i++) cbAddItem(Wnd,i,Current,T("Bit %u"),i);
 }
}

// Wenn die Parallelport-Adresse verΣndert wurde,
// FALSE bei falscher Angabe
static bool OnParallelAddrChange(HWND Wnd) {
 TCHAR buf[16];
 int a;
 buf[0]='0';
 buf[1]='x';
 GetDlgItemText(Wnd,0x31,buf+2,elemof(buf)-2);
 if (!StrToIntEx(buf,STIF_SUPPORT_HEX,&a)) return false;
 if (HIWORD(a)) return false;	// >FFFFh
 if (!HIBYTE(a)) return false;	// <100h
 if (Config.ParallelAddr==(WORD)a) return true;	// unverΣndert
 Config.ParallelAddr=(WORD)a;	// zuweisen
 if (Config.ParallelAddr==DefAddrs[3]) {	// Sonderfall Joystick
  if (Config.ParallelLineIn<4) Config.ParallelLineIn=4;
  Config.ParallelLineOut=0;
 }
 cbFillBits(GetDlgItem(Wnd,0x32),Config.ParallelLineIn);
 cbFillBits(GetDlgItem(Wnd,0x33),Config.ParallelLineOut);
 RestartEmpfang();
 return true;
}

static void PrepareRedString(HWND Wnd) {
 TCHAR t[64];
 GetWindowText(Wnd,t,elemof(t));	// Template mit %s laden
 wndSetText(Wnd,t,INPOUTDLL);
 SendMessage(Wnd,WM_SETFONT,(WPARAM)GdiObj.fnFett,FALSE);
}

// Bei Auswahl einer Soundkarte à
static void FillSamplerates(HWND Wnd) {
 WAVEINCAPS wc;
 static const int PossibleRates[]={8000,11025,16000,22050,32000,44100,48000,96000,192000,200000};
 if (!waveInGetDevCaps(Config.iSoundCard,&wc,sizeof wc)) {
  HWND w=GetDlgItem(Wnd,0x42);
  WAVEFORMATEX wf;
  int j,r=Config.iSampleRate*1000;
  ComboBox_ResetContent(w);
//  for (j=0; j<elemof(PossibleRates); j++) {
//   if (Config.iSampleRate==PossibleRates[j]/1000) r=PossibleRates[j];
//  }
  wndSetText(w,T("%d"),r);
  wf.wFormatTag=WAVE_FORMAT_PCM;
  wf.nChannels=1;
  wf.wBitsPerSample=16;
  wf.nBlockAlign=2;
  for (j=0; j<elemof(PossibleRates); j++) {
   wf.nSamplesPerSec=PossibleRates[j];
   wf.nAvgBytesPerSec=wf.nSamplesPerSec*wf.nBlockAlign;
   if (!waveInOpen(NULL,Config.iSoundCard,&wf,0,0,WAVE_FORMAT_QUERY)) {
    cbAddItem(w,wf.nSamplesPerSec/1000,Config.iSampleRate,T("%d"),wf.nSamplesPerSec);
   }
  }
 }
}

// Bei Auswahl eines Joysticks à
static void FillButtons(HWND Wnd) {
 JOYCAPS jc;
 if (!joyGetDevCaps(Config.iJoystick,&jc,sizeof jc)) {
  TCHAR s[64],*p=s;
  HWND w=GetDlgItem(Wnd,0x52);
  int j;
  ComboBox_ResetContent(w);
  for (j=0; j<(int)jc.wNumButtons; j++) {
   cbAddItem(w,j,Config.iJoyButton&0x1F,T("%d"),j+1);	// sind unter Windows 1-basiert
  }
//verwirrt!  EnableWindow(w,jc.wNumButtons>1);
  LoadString(ghInstance,21,s,elemof(s));
  w=GetDlgItem(Wnd,0x53);
  ComboBox_ResetContent(w);
  for (j=0; j<8; j++) {
   if (j<3 && j<=(int)jc.wNumAxes || j>=3 && jc.wCaps&1<<(j-3))	// ab JOYCAPS_HASZ
     cbAddItem(w,j,Config.iJoyButton>>5,NULL,p);
   p+=lstrlen(p)+1;
  }
  EnableWindow(w,jc.wNumAxes);
 }
}

static void cbFillFromStringId(HWND Wnd, UINT id) {
 TCHAR buf[256], *p=buf;
 buf[LoadString(ghInstance,id,buf,elemof(buf)-1)+1]=0;	// doppelnullterminiert
 for(;*p;p+=lstrlen(p)+1) ComboBox_AddString(Wnd,p);
}

static void SetValue3Decimals(HWND Wnd, UINT id, long v) {
 int i,k;
 TCHAR s[16];
 i=wnsprintf(s,elemof(s)-1,T("%04d"),v)-3;	// Fⁿhrende Nullen erzeugen
 k=4; do s[i+k]=s[i+k-1]; while(--k);		// 3 Nachkommastellen und '\0' schieben
 s[i]=sDecimal[0];				// Dezimaltrenner einfⁿgen
 SetDlgItemText(Wnd,id,s);
}

// Zeigt Akku-Istspannung und Chiptemperatur
void FillHidRep4(HWND Wnd, const THid*Hid, PUCHAR report) {
 long v;
 HidP_GetScaledUsageValue(HidP_Input,0x84,0,0x30,&v,Hid->pd,report,Hid->caps.InputReportByteLength);
 SetValue3Decimals(Wnd,0x76,v);
 HidP_GetScaledUsageValue(HidP_Input,0x84,0,0x36,&v,Hid->pd,report,Hid->caps.InputReportByteLength);
 SetDlgItemInt(Wnd,0x77,v,TRUE);
}

// Zeigt Uhrzeit vom VorlaufempfΣnger
static void ShowHidRep2(HWND Wnd, const THid*Hid) {
 PUCHAR report=LocalAlloc(LPTR,Hid->caps.FeatureReportByteLength);
 if (report) {
  TCHAR s[32];
  SYSTEMTIME st;
  HidP_InitializeReportForID(HidP_Feature,2,Hid->pd,report,Hid->caps.FeatureReportByteLength);
  HidD_GetFeature(Hid->hDev,report,Hid->caps.FeatureReportByteLength);	// Uhrzeit
  st.wMilliseconds=report[1]*4; st.wSecond=report[2]; st.wMinute=report[3]; st.wHour=report[4];
  st.wDay=report[5]; st.wDayOfWeek=report[6]; st.wMonth=report[7]; st.wYear=report[8]+2000;
  if (SystemTimeToString(&st,DATE_SHORTDATE,0,s,elemof(s))) {
   SetDlgItemText(Wnd,0x75,s);
// TODO: PC-Uhr stellen, aber nur 1x!
  }else SetDlgItemText(Wnd,0x75,T("--"));
  LocalFree(report);
 }
}

// Zeigt unverΣnderliche Setup-Sachen
static void ShowHidRep3(HWND Wnd, const THid*Hid) {
 PUCHAR report=LocalAlloc(LPTR,Hid->caps.FeatureReportByteLength);
 if (report) {
  long v;
  HidP_InitializeReportForID(HidP_Feature,3,Hid->pd,report,Hid->caps.FeatureReportByteLength);
  HidD_GetFeature(Hid->hDev,report,Hid->caps.FeatureReportByteLength);	// Setup-Sachen
  HidP_GetUsageValue(HidP_Feature,0xFF00,0,0x35,&v,Hid->pd,report,Hid->caps.FeatureReportByteLength);
  SendDlgItemMessage(Wnd,0x72,CB_SETCURSEL,v,0);	// Akkutyp
  HidP_GetScaledUsageValue(HidP_Feature,0xFF00,0,0x36,&v,Hid->pd,report,Hid->caps.FeatureReportByteLength);
  SetValue3Decimals(Wnd,0x73,v);		// Ladeschluss
  HidP_GetScaledUsageValue(HidP_Feature,0xFF00,0,0x37,&v,Hid->pd,report,Hid->caps.FeatureReportByteLength);
  SetValue3Decimals(Wnd,0x74,v);		// Entladeschluss
  LocalFree(report);
 }
}

// HID-Feature-Reports abfragen und einsetzen, wird bei Selektion aufgerufen.
static void FillHidInfos(HWND Wnd, const THid*Hid) {
 PUCHAR report=LocalAlloc(LPTR,GetMaxReport(&Hid->caps));
 if (report) {
  OVERLAPPED o;
  zeroOverlapped(&o);
  o.hEvent=CreateEvent(NULL,FALSE,FALSE,NULL);
  if (o.hEvent) {
   HidP_InitializeReportForID(HidP_Input,4,Hid->pd,report,Hid->caps.InputReportByteLength);
   GetInputReport(Hid->hDev,report,Hid->caps.InputReportByteLength,&o);
   CloseHandle(o.hEvent);
   FillHidRep4(Wnd,Hid,report);
  }
  LocalFree(report);
 }
 ShowHidRep2(Wnd,Hid);
 ShowHidRep3(Wnd,Hid);
}

// Hilfsfunktion fⁿr die Eingabefehlerprⁿfung
static int cbGetCurSel(HWND Wnd, UINT id, HWND *w) {
 return ComboBox_GetCurSel(*w=GetDlgItem(Wnd,id));
}
// Hilfsfunktion, bei Selektionswechsel
static INT_PTR cbGetCurItemData(HWND cb) {
 return ComboBox_GetItemData(cb,ComboBox_GetCurSel(cb));
}

// Per Timer aufgeschobene DeviceChange-Reaktionsroutine
static void OnDeviceChange(HWND Wnd) {
 HWND hCombo;
 bool one;
 UINT i,k;
 HANDLE devs;
 GUID hidGuid;
// serielle Schnittstellen listen (bei jedem WM_DEVICECHANGE bspw. fⁿr USB)
// Die SetupDi-Methode ist viel schneller als alle (256?) COM-Ports auszuprobieren,
// geht aber erst ab Win98 und Win2k. Win95 und NT4 bleiben au▀en vor.
// Es gibt GUID_DEVCLASS_PORTS -> listet serielle und parallele Schnittstellen, OHNE DIGCF_DEVICEINTERFACE
// und GUID_CLASS_COMPORT == GUID_DEVINTERFACE_COMPORT, nur serielle, MIT DIGCF_DEVICEINTERFACE
// ▄berraschung: Letzteres funktioniert nicht unter Win98SE! Was fⁿr ein heilloses Chaos.
// CM_Open_DevNode_Key() ist ersetzt durch SetupDiOpenDevRegKey().

// if (!bc || bc->dbcc_devicetype==DBT_DEVTYP_DEVICEINTERFACE && !CompareGuid(&bc->dbcc_classguid,&GUID_DEVINTERFACE_COMPORT)
 hCombo=GetDlgItem(Wnd,0x21);
 ComboBox_ResetContent(hCombo);
 one=false;
 devs=SetupDiGetClassDevs(&GUID_DEVCLASS_PORTS,NULL,0,DIGCF_PRESENT);
 if (devs!=INVALID_HANDLE_VALUE) {
  SP_DEVINFO_DATA devInfo;
  devInfo.cbSize=sizeof devInfo;
  for (i=0; SetupDiEnumDeviceInfo(devs,i,&devInfo); i++) {
   HKEY hKey;
   TCHAR s[16];
   DWORD slen=sizeof s;
   *s=0;
   if ((hKey=SetupDiOpenDevRegKey(devs,&devInfo,DICS_FLAG_GLOBAL,0,DIREG_DEV,KEY_READ))
     ==INVALID_HANDLE_VALUE) continue;
   RegQueryValueEx(hKey,T("PortName"),NULL,NULL,(LPBYTE)s,&slen);
   RegCloseKey(hKey);
   if (*s=='C') {	// FehlschlΣge und LPTx ausfiltern
    cbAddItem(hCombo,StrToInt(s+3)-1,Config.SerialNo,NULL,s);	// nullbasiert
    one=true;
   }
  }
  SetupDiDestroyDeviceInfoList(devs);
 }
// wine BUG: Workaround: SetupApi-Funktionen sind anscheinend komplett tot
#define MAXCOMSEARCH 2
 if (!one) {
  for (i=0; i<MAXCOMSEARCH; i++) {	// nullbasiert
   TCHAR ComName[8];
   wnsprintf(ComName,elemof(ComName),T("COM%u"),i+1);
   devs=CreateFile(ComName,0,0,0,OPEN_EXISTING,0,0);
   if (devs!=INVALID_HANDLE_VALUE) {
    CloseHandle(devs);
    cbAddItem(hCombo,i,Config.SerialNo,NULL,ComName);
    one=true;
   }
  }
 }

 EnableDlgItem(Wnd,0x10,one);
// Soundkarten (nur bei ─nderung der Anzahl auffrischen)
 hCombo=GetDlgItem(Wnd,0x41);
 k=waveInGetNumDevs();
 if (ComboBox_GetCount(hCombo)!=k) {
  ComboBox_ResetContent(hCombo);
  one=false;
  for (i=WAVE_MAPPER; (int)i<(int)k; i++) {
   WAVEINCAPS wc;
   if (!waveInGetDevCaps(i,&wc,sizeof wc)) {
    if (cbAddItem(hCombo,i,Config.iSoundCard,NULL,wc.szPname)) {
     FillSamplerates(Wnd);		// Sampleraten auffⁿllen lassen
    }
    one=true;
   }
  }
  EnableDlgItem(Wnd,0x12,one);
 }
// Joysticks (nur bei ─nderung der Anzahl auffrischen)
 hCombo=GetDlgItem(Wnd,0x51);
 k=joyGetNumDevs();
 if (ComboBox_GetCount(hCombo)!=k) {
  ComboBox_ResetContent(hCombo);
  one=false;
  for (i=0; i<k; i++) {
   JOYCAPS jc;
   if (!joyGetDevCaps(i,&jc,sizeof jc)) {
    if (cbAddItem(hCombo,i,Config.iJoystick,T("%d: %s"),i,jc.szPname)) {
     FillButtons(Wnd);
    }
    one=true;
   }
  }
  EnableDlgItem(Wnd,0x13,one);
 }
// UsbPrn
 hCombo=GetDlgItem(Wnd,0x61);
 ComboBox_ResetContent(hCombo);
 one=false;
 devs=SetupDiGetClassDevs(&GUID_DEVINTERFACE_USBPRINT,0,0,DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);
 if (devs!=INVALID_HANDLE_VALUE) {
  SP_DEVICE_INTERFACE_DATA devinterface;
  devinterface.cbSize=sizeof devinterface;
  for (i=0; SetupDiEnumDeviceInterfaces(devs,NULL,&GUID_DEVINTERFACE_USBPRINT,i,&devinterface); i++) {
   cbAddItem(hCombo,i,Config.iUsbPrn,T("%d"),i);
   one=true;
  }
  SetupDiDestroyDeviceInfoList(devs);
 }
 EnableDlgItem(Wnd,0x14,one);
// VorlaufempfΣnger
 hCombo=GetDlgItem(Wnd,0x71);
 ComboBox_ResetContent(hCombo);
 one=false;
 HidD_GetHidGuid(&hidGuid);
 devs=SetupDiGetClassDevs(&hidGuid,0,0,DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);
 if (devs!=INVALID_HANDLE_VALUE) {
  SP_DEVICE_INTERFACE_DATA devinterface;
  devinterface.cbSize=sizeof devinterface;
  for (i=0; SetupDiEnumDeviceInterfaces(devs,NULL,&hidGuid,i,&devinterface); i++) {
   THid Hid;
   union{
    SP_DEVICE_INTERFACE_DETAIL_DATA detail;
    TCHAR space[MAX_PATH+4];
    WCHAR ps[128];		// ProductString
    HIDD_ATTRIBUTES a;
   }u;
   SP_DEVINFO_DATA info;
   info.cbSize=sizeof info;
   u.detail.cbSize=sizeof u.detail;
   if (!SetupDiGetDeviceInterfaceDetail(devs,&devinterface,&u.detail,sizeof u,NULL,&info)) continue;
   Hid.hDev=CreateFile(u.detail.DevicePath,GENERIC_READ|GENERIC_WRITE,
     FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,FILE_FLAG_OVERLAPPED,NULL);
   if (Hid.hDev!=INVALID_HANDLE_VALUE) {
    HidD_GetAttributes(Hid.hDev,&u.a);
    if (*(DWORD*)&u.a.VendorID==0x27D916C0		// VOTI->V-USB->HID mit Seriennummer muss es sein
    && HidD_GetProductString(Hid.hDev,u.ps,elemof(u.ps))) {
#ifdef UNICODE
     PTSTR p=u.ps;
     int l=lstrlen(p)+1;
#else
     PTSTR p=u.space+128;	// Hoffnung: Keine ▄berlappung (64 Zeichen vom GerΣt maximal)
     int l=WideCharToMultiByte(CP_ACP,0,u.ps,-1,p,MAX_PATH-124,NULL,NULL);
#endif
     if (StrStr(p,T("FunkUsb"))) {	// Der Produktname muss "FunkUsb" enthalten
      SetupDiSetDeviceRegistryProperty(devs,&info,SPDRP_FRIENDLYNAME,
        (PBYTE)(p),l*sizeof(TCHAR));	// FriendlyName fⁿr GerΣte-Manager setzen
      if (HidD_GetPreparsedData(Hid.hDev,&Hid.pd)) {
       HidP_GetCaps(Hid.pd,&Hid.caps);
       if (Hid.caps.UsagePage>=8) {	// Joystick (Generic Desktop) hier ausfiltern
        if (cbAddItem(hCombo,i,Config.iUsbHid,T("%d: %s"),i,p)) {
	 FillHidInfos(Wnd,&Hid);
	}
        one=true;
       }
       HidD_FreePreparsedData(Hid.pd);
      }
     }
    }
    CloseHandle(Hid.hDev);
   }
  }
  SetupDiDestroyDeviceInfoList(devs);
 }
 EnableDlgItem(Wnd,0x15,one);
}

// Dialogfensterprozedur fⁿr Eigenschaftsseite
INT_PTR CALLBACK HardwareDlgProc(HWND Wnd,UINT Msg,WPARAM wParam,LPARAM lParam) {
 DefHelpProc(Wnd,Msg,lParam,101);
 switch (Msg) {
  case WM_INITDIALOG: {
// Eingabeleitungen in der Bit-Reihenfolge von 3F6h, Pin-Nummern: 9pol. SubD
   static const char SerialInLines[]=
    "CTS(8)\0DSR(6)\0RI(9)\0DCD(1)\0RxD(2)\0";
// Ausgabeleitungen in der Bit-Reihenfolge von 3F4h
   static const char SerialOutLines[]=
    "DTR(4)\0RTS(7)\0TxD(3)\0";
   CheckDlgButton(Wnd,0x10+Config.Where,TRUE);
   AttachUpDown(Wnd,0x43,0x44,10,Config.iSampleRate*500,FiltFreqHz(NULL));
   KillTimer(Wnd,0x43);
   ShowWhere(Wnd);
   cbFillA(GetDlgItem(Wnd,0x22),SerialInLines,Config.SerialLineIn);
   cbFillA(GetDlgItem(Wnd,0x23),SerialOutLines,Config.SerialLineOut);
   SendDlgItemMessage(Wnd,0x24,WM_SETFONT,(WPARAM)GdiObj.fnKursiv,FALSE);
   cbFillAddrs(GetDlgItem(Wnd,0x31));
   cbFillBits(GetDlgItem(Wnd,0x32),Config.ParallelLineIn);
   cbFillBits(GetDlgItem(Wnd,0x33),Config.ParallelLineOut);
   SendDlgItemMessage(Wnd,0x34,WM_SETFONT,(WPARAM)GdiObj.fnKursiv,FALSE);
   PrepareRedString(GetDlgItem(Wnd,0x35));
   SendDlgItemMessage(Wnd,0x45,WM_SETFONT,(WPARAM)GdiObj.fnKursiv,FALSE);
   cbFillBits(GetDlgItem(Wnd,0x62),(BYTE)(Config.iUsbInput|0x80));
   cbFillFromStringId(GetDlgItem(Wnd,0x72),22);	// Akkutyp
   OnDeviceChange(Wnd);
  }return TRUE;

  case WM_DEVICECHANGE: SetTimer(Wnd,0x10,1500,NULL); break;

  case WM_COMMAND: switch ((DWORD)wParam) {
   case MAKELONG(0x10,BN_CLICKED):
   case MAKELONG(0x11,BN_CLICKED):
   case MAKELONG(0x12,BN_CLICKED):
   case MAKELONG(0x13,BN_CLICKED):
   case MAKELONG(0x14,BN_CLICKED):
   case MAKELONG(0x15,BN_CLICKED): {
    BYTE o=Config.Where;
    bool scc,piep;
// BS_AUTORADIOBUTTON funktioniert nicht mit ausgegrauten Kn÷pfen.
// Das Unglⁿck passiert, wenn man bei verschwundener Hardware (etwa HID)
// eine andere (etwa COM) anwΣhlt: Der Checkmark (bei HID) bleibt stehen.
// Deshalb dieser Umweg.
    CheckRadioButton(Wnd,0x10,0x15,(int)wParam);
    Config.Where=(BYTE)wParam-0x10;
    scc=(o==2)!=(Config.Where==2);	// Soundkarten-Wechsel?
    piep=scc && Config.Piep
      && (AnimYes() || Empfang.DauerPiep);	// Piep-Wechsel?
    ShowWhere(Wnd);
// VorlaufempfΣnger-Uhr-Anzeige ein/ausschalten
    if (o==5) KillTimer(Wnd,0x71);
    if (Config.Where==5) SetTimer(Wnd,0x71,1000,NULL);
// Der Piep kann (auf manchen Soundkarten?) nicht gleichzeitg mit dem Empfang
// via Soundkarte laufen. Daher muss dieser entsprechend aktiviert/deaktiviert werden.
    if (Config.Where==2 && piep) DoneBeep();
// Ggf. Demodulator-Eigenschaftsseite hinzufⁿgen oder entfernen.
    if (scc && Config.Where==2) PropInsDelSheet(8,8);
    RestartEmpfang();
    if (scc && Config.Where!=2) PropInsDelSheet(-1,Decrypt?8:7);
    if (o==2 && piep) {
     InitBeep((BYTE)(10-Config.Piep));
     if (Empfang.Signal || Empfang.DauerPiep) StartBeep();
    }
   }break;

   case MAKELONG(0x21,CBN_SELCHANGE): {
    BYTE b=(BYTE)cbGetCurItemData((HWND)lParam);
    if (Config.SerialNo!=b) {
     Config.SerialNo=b;
     RestartEmpfang();
    }
   }break;
   case MAKELONG(0x22,CBN_SELCHANGE): {
    BYTE b=(BYTE)ComboBox_GetCurSel((HWND)lParam);
    if (Config.SerialLineIn!=b) {
     Config.SerialLineIn=b;
     RestartEmpfang();	// wirklich n÷tig?
    }
   }break;
   case MAKELONG(0x23,CBN_SELCHANGE): {
    BYTE b=(BYTE)ComboBox_GetCurSel((HWND)lParam);
    if (Config.SerialLineOut!=b) {
     Config.SerialLineOut=b;
     RestartEmpfang();	// wirklich n÷tig?
    }
   }break;

   case MAKELONG(0x31,CBN_SELCHANGE):	// OnParallelAddrChange(Wnd); break;	// sofort
   case MAKELONG(0x31,CBN_EDITCHANGE): {	// verz÷gert 
    if (!SetTimer(Wnd,31,200,NULL)) PostMessage(Wnd,WM_TIMER,31,0);
   }break;
   case MAKELONG(0x32,CBN_SELCHANGE): {
    BYTE b=(BYTE)cbGetCurItemData((HWND)lParam);
    if (Config.ParallelLineIn!=b) {
     Config.ParallelLineIn=b;
     RestartEmpfang();
    }
   }break; 
   case MAKELONG(0x33,CBN_SELCHANGE): {
    BYTE b=(BYTE)cbGetCurItemData((HWND)lParam);
    if (Config.ParallelLineOut!=b) {
     Config.ParallelLineOut=b;
     RestartEmpfang();
    }
   }break;

   case MAKELONG(0x41,CBN_SELCHANGE): {
    int b=(int)cbGetCurItemData((HWND)lParam);
    if (Config.iSoundCard!=(char)b) {
     Config.iSoundCard=(char)b;
     FillSamplerates(Wnd);
     RestartEmpfang();
    }
   }break;
   case MAKELONG(0x42,CBN_SELCHANGE):
   case MAKELONG(0x42,CBN_EDITCHANGE): {
    if (!SetTimer(Wnd,0x42,200,NULL)) PostMessage(Wnd,WM_TIMER,0x42,0);
   }break;
   case MAKELONG(0x43,EN_CHANGE): {
    if (!SetTimer(Wnd,0x43,200,NULL)) PostMessage(Wnd,WM_TIMER,0x43,0);
   }break;

   case MAKELONG(0x51,CBN_SELCHANGE): {
    int b=(int)cbGetCurItemData((HWND)lParam);
    if (Config.iJoystick!=(BYTE)b) {
     Config.iJoystick=(BYTE)b;
     FillButtons(Wnd);
     RestartEmpfang();
    }
   }break;
   case MAKELONG(0x52,CBN_SELCHANGE): {
    Config.iJoyButton=(BYTE)(Config.iJoyButton&0xE0|ComboBox_GetCurSel((HWND)lParam));	// wirkt sofort
   }break;
   case MAKELONG(0x53,CBN_SELCHANGE): {
    Config.iJoyButton=(BYTE)(Config.iJoyButton&0x1F|ComboBox_GetCurSel((HWND)lParam)<<5);
   }break;

   case MAKELONG(0x61,CBN_SELCHANGE): {
    BYTE b=(BYTE)ComboBox_GetCurSel((HWND)lParam);
    if (Config.iUsbPrn!=b) {
     Config.iUsbPrn=b;
     RestartEmpfang();
    }
   }break;
   case MAKELONG(0x62,CBN_SELCHANGE): {
    Config.iUsbInput=(BYTE)(ComboBox_GetCurSel((HWND)lParam)+013);	// oktal; wirkt sofort
   }break;

   case MAKELONG(0x71,CBN_SELCHANGE): {
    BYTE b=(BYTE)cbGetCurItemData((HWND)lParam);
    if (Config.iUsbHid!=b) {
     Config.iUsbHid=b;
     RestartEmpfang();
    }
   }break;

  }break;

  case WM_TIMER: if ((BYTE)wParam!=0x71) KillTimer(Wnd,wParam); switch ((BYTE)wParam) {
   case 0x10: OnDeviceChange(Wnd); break;
   case 0x31: OnParallelAddrChange(Wnd); break;
   case 0x42: {	// Abtastrate in Sa/s
    int i=GetDlgItemInt(Wnd,0x42,NULL,FALSE)/1000;
    if (i>=8 && i<256) {
     Config.iSampleRate=(BYTE)i;	// abspeichern in kSa/s
     SendDlgItemMessage(Wnd,0x44,UDM_SETRANGE32,10,i*500);
     RestartEmpfang();
    }
   }break;
   case 0x43: {
    int f;
    if (GetUpDownInt(Wnd,0x44,&f)) SetFiltFreq(f*100);	// Demodulatoroszillator äziehenô, AFC-Wert l÷schen
   }break;
//   case 0x71: ShowHidRep2(Wnd,Th.hCom,Th.pd,&Th.caps); break;
  }break;

  case WM_FUNKRECV: if ((BYTE)wParam==14) {
   FillHidRep4(Wnd,&gHid,(PUCHAR)lParam);
  }break;

  case WM_CTLCOLORSTATIC: if (GetDlgCtrlID((HWND)lParam)==0x35) {
   LRESULT br=DefWindowProc(Wnd,Msg,wParam,lParam);
   SetTextColor((HDC)wParam,RGB(192,0,0));		// dunkelrot
   SetBkMode((HDC)wParam,TRANSPARENT);
   return (BOOL)br;	// nicht "return (BOOL)GetSysColorBrush(COLOR_3DFACE);"	// Unter Win7 falsche Hintergrundfarbe
  }break;

  case WM_NOTIFY: {
   LPPSHNOTIFY psn=(LPPSHNOTIFY)lParam;
   UINT i,Problem=0;	// Problem = String-ID, die bei festgestelltem Problem angezeigt wird
   HWND w;		// Fehler-Dialogelement, wird anschlie▀end fokussiert und ggf. Text markiert
   switch (psn->hdr.code) {
    case PSN_SETACTIVE: {
     SendDlgItemMessage(Wnd,0x44,UDM_SETPOS32,0,Config.iFiltFreq<<1);
     if (Config.Where==5) SetTimer(Wnd,0x71,1000,NULL);
    }break;
    case PSN_KILLACTIVE: {
     switch (Config.Where) {
      case 0: {	// seriell
       if (cbGetCurSel(Wnd,0x21,&w)<0) {Problem=17; break;}
       if (cbGetCurSel(Wnd,0x22,&w)<0) {Problem=17; break;}
       if (cbGetCurSel(Wnd,0x23,&w)<0) {Problem=17; break;}
      }break;
      case 1: {	// parallel
       if (!OnParallelAddrChange(Wnd)) {
        Problem=18;
	w=GetDlgItem(Wnd,0x31);
	break;
       }
       if (cbGetCurSel(Wnd,0x32,&w)<0) {Problem=17; break;}
       if (cbGetCurSel(Wnd,0x33,&w)<0) {Problem=17; break;}
      }break;
      case 2: { // Soundkarte
       if (cbGetCurSel(Wnd,0x41,&w)<0) {Problem=17; break;}
       i=GetDlgItemInt(Wnd,0x42,NULL,FALSE)/1000u;	// Abtastrate
       if (i<8 || i>255) {w=GetDlgItem(Wnd,0x42); Problem=46; break;}
       if (!GetUpDownInt(Wnd,0x44,NULL)) {	// Filterfrequenz
        Edit_SetSel(w=GetDlgItem(Wnd,0x43),0,-1);
        Problem=46;
        break;
       }
      }break;
      case 3: { // Joystick
       if (cbGetCurSel(Wnd,0x51,&w)<0) {Problem=17; break;}
       if (cbGetCurSel(Wnd,0x52,&w)<0) {Problem=17; break;}
      }break;
      case 4: { // UsbPrn
       if (cbGetCurSel(Wnd,0x61,&w)<0) {Problem=17; break;}
       if (cbGetCurSel(Wnd,0x62,&w)<0) {Problem=17; break;}
      }break;
      case 5: { // VorlaufempfΣnger
       KillTimer(Wnd,0x71);
      }break;
      default: Problem=16; w=GetDlgItem(Wnd,0x10);
     }
    }break;
   }
   if (Problem) {
    SetFocus(w);
    MBox(Wnd,Problem,MB_OK);
    return SetDlgMsgResult(Wnd,WM_NOTIFY,TRUE);
   }
  }break;

 }
 return FALSE;
}
Detected encoding: OEM (CP437)1
Wrong umlauts? - Assume file is ANSI (CP1252) encoded