Source file: /~heha/mb-iwp/Energiemessung/VIPsys3/vipsys3.zip/src/vipsys3.cpp

#include "vipsys3.h"
#include <windows.h>
#include <windowsx.h>
#include <shlwapi.h>
#include <commctrl.h>
#include <stdio.h>
#include <tchar.h>
#include <limits.h>
#include "analyzer.h"
#include <math.h>
#if _MSC_VER < 1400	// Visual C++ 6
__forceinline void __stosd(void*p, DWORD val, DWORD count) {
 _asm	mov	edi,p
 _asm	mov	eax,val
 _asm	mov	ecx,count
 _asm	rep stosd
}
__forceinline void __movsw(void*d, const void*s, DWORD count) {
 _asm	mov	edi,d
 _asm	mov	esi,s
 _asm	mov	ecx,count
 _asm	rep movsw
}
#else
# include <intrin.h>
#endif

/* Wunschliste:
 * Funktionierend
 * Log-Datei
 * Reboot überstehen
 */

HINSTANCE ghInst;
HWND ghMainWnd,ghStatus;
HICON ghIcons[2];
HANDLE hCom;
int Instance;
TConfig Config;

/**********************************
 * Zeichenobjekte und -funktionen *
 **********************************/
#define FONTNAME T("Arial")	// Name of standard font used

struct GDI{		// holds GDI resources needed for painting the client area
 HFONT fntBig,fntSmall;		// fonts
 HPEN penGrid;			// for table styling
 HBITMAP bmDblBuf;		// bitmap and DC, only used for double buffering
 HDC dcDblBuf;
 void Make(int,int);		// (Re)Creates fonts and doublebuffer bitmap for given client pixel size
 void Make();			// Same but fetches the client size beforehand
 void Delete();			// Releases all GDI resources, for minimized display
 static void ClientRect(RECT&);
 int xx;			// Spaltenbreite (Pixel)
 int yy;			// Zeilenhöhe (Pixel)
private:
 static HFONT MakeFont(int,int,int,LPCTSTR);
 static void DeleteObj(HGDIOBJ&);
}gGdi;

void GDI::DeleteObj(HGDIOBJ&obj) {
 if (obj && DeleteObject(obj)) obj=0;
}

void GDI::Delete() {
 if (dcDblBuf && DeleteDC(dcDblBuf)) dcDblBuf=0;	// kicks out selected pen, font, and bitmap
 HGDIOBJ *obj=(HGDIOBJ*)this;
 for (int i=0; i<4; i++) DeleteObj(obj[i]);
}

HFONT GDI::MakeFont(int x, int y, int weight, LPCTSTR name) {
 HFONT ret=CreateFont(y,x,0,0,weight,0,0,0,DEFAULT_CHARSET,0,0,0,0,name);
 return ret;
}

// Font re-initialization, called when
// * window size changes
// * measurement unit or prefix changes
// * appearance changes between scope and multimeter mode
void GDI::Make(int x, int y) {
 Delete();
 if (Config.doubleBuf) {
  HDC dc=GetDC(0);
  bmDblBuf=CreateCompatibleBitmap(dc,x,y);
  dcDblBuf=CreateCompatibleDC(dc);
  ReleaseDC(0,dc);
  SelectBitmap(dcDblBuf,bmDblBuf);
 }
 fntBig=MakeFont(x/30,MulDiv(y,2,8),0,FONTNAME);
 xx=x/5;
 yy=30;
 fntSmall=MakeFont(MulDiv(xx,10,256),yy,0,FONTNAME);
}

void GDI::ClientRect(RECT&r) {
 static const int Info[]={1,1,2,1,0};
 GetEffectiveClientRect(ghMainWnd,&r,(int*)Info);
}

void GDI::Make() {
 RECT r;
 ClientRect(r);
 Make(r.right-r.left,r.bottom-r.top);
}

TCHAR StdMBoxTitle[64];
// Assumption: The decimal character is never an MBCS character! Is this true?
WCHAR cDecimalW;		// The decimal delimiter
char cDecimalA;			// ANSI version
DWORD gDdeInst;
UINT CF_XlTable;		// Clipboard-Format


#ifdef UNICODE
#define MyLoadStringW(id,buf,len) LoadString(ghInst,id,buf,len)
#else
int MyLoadStringW(UINT id, LPWSTR buf, int len) {
 HRSRC r=FindResource(ghInst,MAKEINTRESOURCE((id>>4)+1),RT_STRING);
 PCWSTR p=(PCWSTR)LoadResource(ghInst,r);
 if (!p) return 0;
 for (id&=15; id; id--) {
  p+=*p+1;
 }
 len--;
 if (len<0) return *p+1;	// Allokationsgröße in Zeichen
 if (len>*p) len=*p;
 p++;
 __movsw((WORD*)buf,(WORD*)p,len);	// inklusive etwaiger Nullen!
 buf[len]=0;
 return len;
}
#endif

static const int _NaN = _I32_MAX;	// "signaling NaN"
#define NaN (*(float*)&_NaN)

/*******************
 * Hilfsfunktionen *
 *******************/

void _fastcall InitStruct(LPVOID p, DWORD len) {
 __stosd((DWORD*)p,0,len>>2);
 *(DWORD*)p=len;
}

UINT GetCheckboxGroup(HWND Wnd, UINT u, UINT o) {
 UINT v,m;
 for (v=0,m=1; u<=o; u++,m+=m) if (IsDlgButtonChecked(Wnd,u)==1) v|=m;
 return v;
}

void SetCheckboxGroup(HWND Wnd, UINT u, UINT o, UINT v) {
 for (; u<=o; u++,v>>=1) CheckDlgButton(Wnd,u,v&1);
}

// Wandelt eine (Hex-)Ziffer [0-9A-Z] in eine Zahl 0..35, sonst >=36
static char OneDigit(char c) {
 if (c>=0x60) c-=0x20;		// Großbuchstaben machen
 c-='0';
 if ((unsigned char)c<10) return c;	// Ziffern
 c-='A'-'0';
 if (c<0) return c;		// ungültig
 c+=10;
 return c;			// Buchstaben oder ungültig
}

// Zahl aus String konvertieren, aber maximal <maxlen> Zeichen schlucken
// Doppelzeiger zeigt nachher auf nächstes, nicht mehr verarbeitetes Zeichen
int mystrtoul(const char*&s, int base, int maxlen) {
 int r=0;
 for(;maxlen;maxlen--) {
  char d=OneDigit(*s);
  if ((unsigned char)d>=base) break;
  r*=base;
  r+=d;
  s++;
 }
 return r;
}

// Erzeugt (C-mäßigen) String aus Byte-Kette
// Puffer dürfen sich nicht überlappen
static void escape(char *d, const char *s, int len) {
 char c;
 for (; len; len--, s++) {
  c=*s;
  switch (c) {
   case '\r': c='r'; break;
   case '\n': c='n'; break;
   case '\a': c='a'; break;
   case '\b': c='b'; break;
   case '\t': c='t'; break;
   case 27:   c='e'; break;
  }
  if (c!=*s || c=='\\' || c=='^') d+=wsprintfA(d,"\\%c",c);
  else if ((unsigned char)c<' ') d+=wsprintfA(d,"\\x%02X",(unsigned char)c);
  else *d++=c;
 }
 *d=0;
}

// Erzeugt Byte-Kette aus nullterminiertem String
// Verarbeitet Backslash-Kodes (C), ^-Kodes (Terminal)
// Puffer dürfen sich überlappen, max. 256 Bytes
static int unescape (char*d, const char *s) {
 char c;
 int n=0;			// Bytezähler
 for(;;){
  switch (c=*s++) {
   case 0: return n;
   case '\\': {
    switch (c=*s++) {		// nächstes Zeichen interpretieren
     case 0: return n;
     case 'r': c='\r'; break;
     case 'n': c='\n'; break;
     case 'a': c='\a'; break;
     case 'b': c='\b'; break;
     case 't': c='\t'; break;
     case 'e': c=27; break;	// Escape
     case 'x': c=(char)mystrtoul(s,16,2); break;
     default: if ('0'<=c && c<='7') {
      s--; c=(char)mystrtoul(s,8,3);
     } break;
     // Alle anderen Folgezeichen unverändert durchlassen
    }
   }break;
   case '^': c=*s++-'@';	// nächstes Zeichen - 0x40 (auch 0 erlaubt!)
  }
  *d++=c; n++;
 }
 return n;
}

static void hexlisting(char*d, const char*s, int l) {
 for (; l>0; l--) {
  d+=wsprintfA(d,"%02X ",(unsigned char)(*s++));
 }
}

static void numberdecode(char *d, const char*s, char exp=-128) {
 WORD m=MAKEWORD(s[0],s[1]);
 if (exp==-128) exp=s[2];	// Komma-Position
 char format[8];
 if (exp<=0) {
  wsprintfA(format,"%%0%du",-exp+1);
  int l=wsprintfA(d,format,m);
  if (exp) {
   RtlMoveMemory(d+l+exp+1,d+l+exp,-exp+1);
   d[l+exp]=',';		// Komma einpflanzen
  }
 }else{
  wsprintfA(format,"%%u%%0%dd",exp);
  wsprintfA(d,format,m,0);	// Nullen anhängen lassen
 }
}

int _cdecl MBox(HWND Wnd, UINT id, UINT Type, ...) {
 TCHAR buf[256],fmt[256];
 LoadString(ghInst,id,fmt,elemof(fmt));
 _sntprintf(buf,elemof(buf),fmt,(va_list)(&Type+1));
 return MessageBox(Wnd,buf,StdMBoxTitle,Type);
}

#ifdef _M_IX86
EXTERN_C void _declspec(naked) _cdecl _fltused(void) {}
#endif

static bool LoadConfig() {
 bool found=false;
 HKEY k;
 if (RegOpenKey(HKEY_CURRENT_USER,T("Software\\h#s\\VipSys3"),&k)) return found;	// nothing saved ever
 DWORD len=sizeof(Config);
 TCHAR ConfName[16];
 _sntprintf(ConfName,elemof(ConfName),T("Config%d"),Instance);
 if (!RegQueryValueEx(k,ConfName,NULL,NULL,(LPBYTE)&Config,&len)) found=true;
 RegCloseKey(k);
 if (found) {
// Reposition window as saved
  WINDOWPLACEMENT wp;
  wp.length=sizeof(wp);
  GetWindowPlacement(ghMainWnd,&wp);
  Config.winpos>>wp.rcNormalPosition;
  wp.showCmd=SW_HIDE;
  SetWindowPlacement(ghMainWnd,&wp);
 }
 return found;
}

static void SaveConfig() {
 RegSetValue(HKEY_CURRENT_USER,T("Software\\h#s"),REG_SZ,T("haftmann#software"),17*sizeof(TCHAR));
 HKEY k;
 if (RegCreateKey(HKEY_CURRENT_USER,T("Software\\h#s\\VipSys3"),&k)) return;
 TCHAR s[64];
 int l=LoadString(ghInst,1,s,elemof(s));
 RegSetValue(k,NULL,REG_SZ,s,l*sizeof(TCHAR));
 TCHAR ConfName[16];
 _sntprintf(ConfName,elemof(ConfName),T("Config%d"),Instance);
// Retrieve Window placement info
 WINDOWPLACEMENT wp;
 wp.length=sizeof(wp);
 GetWindowPlacement(ghMainWnd,&wp);
 Config.winpos=wp.rcNormalPosition;
 Config.showCmd=(char)wp.showCmd;
// Store configuration information
 RegSetValueEx(k,ConfName,0,REG_BINARY,(LPBYTE)&Config,sizeof(Config));
 RegCloseKey(k);
}

static const DCB DefDcb={
 sizeof(DefDcb),9600,
 1,0,1,0,1,0,1,0,0,0,0,2,1,0,
 0,65535,65535,7,2,0,0x11,0x13,0,0,'\n',0};

static TCHAR stCom[]=T("COM%u");

OVERLAPPED gOvl;

void RestartEmpfang() {
 if (hCom) CloseHandle(hCom);
 TCHAR ComName[12];
 _sntprintf(ComName,elemof(ComName),T("\\\\.\\COM%u"),Config.serialNo+1);
 hCom=CreateFile(ComName,GENERIC_READ|GENERIC_WRITE,
   0,NULL,OPEN_EXISTING,FILE_FLAG_OVERLAPPED,0);
 if (hCom==INVALID_HANDLE_VALUE) hCom=0;
 if (hCom) {
  DCB dcb;
  DLGCONFIG::SetDCB(dcb,Config.serialCfg);
  SetCommState(hCom,&dcb);
  static const COMMTIMEOUTS to={MAXDWORD,0,0,10,100};
  SetCommTimeouts(hCom,(COMMTIMEOUTS*)&to);
  SetCommMask(hCom,EV_RXFLAG);
 }
 gOvl.hEvent=CreateEvent(NULL,FALSE,FALSE,NULL);
}

int Send(const char *s, int l) {
 DWORD bw;
 WriteFile(hCom,s,l,&bw,&gOvl);
 GetOverlappedResult(hCom,&gOvl,&bw,TRUE);
 return bw;
}

int Recv(char *s, int l) {
 DWORD br,ev;
 WaitCommEvent(hCom,&ev,&gOvl);
 if (WaitForSingleObject(gOvl.hEvent,500)) {
  CancelIo(hCom);
 }
 ReadFile(hCom,s,l,&br,&gOvl);
 GetOverlappedResult(hCom,&gOvl,&br,TRUE);
 return br;
}

// Senden und Horchen mit Wiederholfunktion bei ESC K CR LF
int SendRecv(const char *s, int sl, char *r, int rl) {
 int l;
 DWORD tic=GetTickCount();
 do{
  if (Send(s,sl)!=sl) return -10;
  l=Recv(r,rl);
  if (l!=4) return l;
  if (*(DWORD*)r!=0x0A0D4B1B) return l;
 }while (GetTickCount()-tic<3000);	// wiederholen
 return -11;
}

// Senden, Horchen und Vergleichen des Empfangsstrings mit Sendestring
int SendRecv(const char *s, int sl) {
 if ((unsigned)sl>=8) return -12;
 char r[8];
 int l=SendRecv(s,sl,r,sizeof(r));
 if (l<0) return l;
 if (l!=sl) return -13;		// no match
 if (memcmp(s,r,l)) return -13;	// no match
 return l;
}
#if 0
// blockierende Datenabfrage bis "EOF" (Seite 136)
// liefert "Anzahl Bytes", oder negative Zahl für Fehlerkode
int Query(char*d, int dlen) {
 int expectblock=0;
 int len=0;
 static const char s[4]={0x1B,'O','\r','\n'};
 for(;;){
  char r[42];					// maximal 1+6+16*2+1+2 = 42 Bytes
  int l=SendRecv(s,4,r,sizeof(r));
  if (l<0) return l;
  int a;
  int e=string_decode(r,l,&a,d,dlen);
  if (e<0) return e;			// mit Fehler aussteigen
  if (!e && a==0x1000) return len;	// EOF-Block: alles fertig zusammengesetzt
  if (a!=expectblock) return -7;	// Fehler bei "Adresse"
  d+=e; len+=e;
  dlen-=e;				// verbleibender Platz
  expectblock++;
  if (dlen<0) {
   static const char s[4]={0x1B,'A','\r','\n'};	// vorzeitig abbrechen!
   SendRecv(s,4);
   return len;
  }
 }
}

LONG_PTR CALLBACK UnusedProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
 switch (Msg) {
  case WM_INITDIALOG: {
   SetDlgItemTextA(Wnd,10,"\\eA\\r\\n");
  }return TRUE;

  case WM_COMMAND: switch (LOWORD(wParam)) {
   case 1: {
    char t[20];
    GetDlgItemTextA(Wnd,10,t,elemof(t));
    char u[20];
    int l=unescape(u,t);
    char r[44];
    l=SendRecv(u,l,r,sizeof(r));
    char j[280];
    escape(j,r,l);
    SetDlgItemTextA(Wnd,11,j);
//    l=stringdecode(j,r,l);
    if (l>=0) {
     if (j[0] != (l-3)<<1) {l=-6; goto fehler;}
     hexlisting(r,j+1,l-1);
     SetDlgItemTextA(Wnd,12,r);
// Für ESC m 1 CR LF : (Seite 170)
     if (j[2]!=0x10) {
      char z[160];
      numberdecode(z,j+3);
      lstrcatA(z," V");
      SetDlgItemTextA(Wnd,15,z);
      numberdecode(z,j+6);
      lstrcatA(z," A");
      SetDlgItemTextA(Wnd,16,z);
      numberdecode(z,j+9);
      lstrcatA(z," kW");
      SetDlgItemTextA(Wnd,17,z);
      numberdecode(z,j+12,-3);
      lstrcatA(z," °");
      SetDlgItemTextA(Wnd,18,z);
     }
    }else fehler: SetDlgItemTextA(Wnd,12,NULL);
    SetDlgItemInt(Wnd,14,l,TRUE);
   }break;
   case 2: EndDialog(Wnd,wParam); break;
   case 5: {
    char b[800];
    HCURSOR oc=SetCursor(LoadCursor(0,IDC_WAIT));
    DWORD tic=GetTickCount();

    char t[20];
    GetDlgItemTextA(Wnd,10,t,elemof(t));
    char u[20];
    int l=unescape(u,t);
    char r[44];
    l=SendRecv(u,l,r,sizeof(r));
    char j[280];
    escape(j,r,l);
    SetDlgItemTextA(Wnd,11,j);

    l=Query(b,sizeof(b));
    tic=GetTickCount()-tic;
    SetCursor(oc);
    SetDlgItemInt(Wnd,14,l,TRUE);
    wsprintfA(t,"%u ms",tic);
    SetDlgItemTextA(Wnd,11,t);
    if (l>=0) {
     if (l>256) l=256;
     char r[1000];
     hexlisting(r,b,l);
     SetDlgItemTextA(Wnd,12,r);
    }
   }break;
  }
  
 }
 return FALSE;
}
#endif
double VAL3::toFloat() const{
 return w*pow(10.0F,e);
}

double VAL2::toFloat(char e) const{
 return w*pow(10.0F,e);
}

int toStringW(double f, PWSTR s, int l) {
 return _snwprintf(s,l,L"%G",f);
}

STRING_ESCM escm_data;
DWORD time_used;

static void FillBack(HDC dc, int x1, int y1, int x2, int y2) {
 TRIVERTEX vert[]={
  {x1,y1,0xe000,0xe000,0xe000},
  {x1,y2,0xf000,0xf000,0xf000},
  {x2,y2,0xff00,0xff00,0xff00},
  {x2,y1,0xf000,0xf000,0xf000}};
 GRADIENT_TRIANGLE Tri[]={
  {0,1,2},
  {0,2,3}};
 GradientFill(dc,vert,elemof(vert),Tri,elemof(Tri),GRADIENT_FILL_TRIANGLE);
}

struct TH{		// table header cell item
 UINT sid;		// string resource index for text display
 LONG_PTR par;		// one free parameter for sprintf()
 char dde2[4];		// second part of DDE name (e.g. "-1")
};

struct TD{		// table body cell item
 double val,min,max;	// data for DDE
 char str[10];		// text for display, not localized (contains "." decimal point)
 char unitprefix;	// 0-based exponent of 10
 char colspan;		// colspan, 0 = unused column!
};

union COL{
 TH th;			// first row
 TD td;			// non-first rows
};

#pragma warning(disable:4200)
struct TR{
 UINT sid;		// string resource index for descriptive text display
 LONG_PTR par;		// one free parameter for sprintf()
 UINT nid;		// string resource index for name display
 char name[7];		// alternative name, as UTF-8 (unused)
 char ndeco;		// name decoration flags: average, peak
 char dde1[8];		// first part of DDE name (e.g. "Up" for U-peak), 7-bit ASCII only
 BYTE ncol;		// number of columns following, 0 = sub-divider ("Tariff Band")
 char unitcode;		// the unit code for common units, appended to each(?) <td> item
 char unit[8];		// the unit for uncommon units, as UTF-8 (unused)
 COL col[];
};

struct SPAN{		// handles COLSPAN
 unsigned ncol;		// number of columns of the table
 unsigned*w;			// triangular widths array
 static unsigned BF(unsigned x) {return x*(x+1)/2;};
 SPAN() {};
 SPAN(unsigned col) {w=new unsigned[BF(ncol=col)];};
 ~SPAN() {delete[]w;};
 unsigned&PW(unsigned x, unsigned spanx) const{
// possible bugs:
  if (x>=ncol) x=ncol-1;
  if (x+spanx>ncol) spanx=ncol-x;
  if (!spanx) spanx=1;
// calculate index
  return w[(BF(ncol+1-spanx)-1)+x];
 };
 void setmax(unsigned x, unsigned spanx, unsigned w) const{
  unsigned&pw=PW(x,spanx);
  if (pw<w) pw=w;
 };
 unsigned get(unsigned x, unsigned spanx) const{return PW(x,spanx);};
};

static PWSTR GenUnitW(PWSTR d, char unitprefix, char unitcode, char f) {
 return d;
}

// obige Tabellenstruktur ausgeben
static void OutTable(HDC dc, TR*tr, int n){
// 0. Check whether table is valid
 if (!tr || n<2) return;
 int xx=tr[0].ncol;	// the header dictates the number of possible columns
 if (!xx) return;	// no data column
// 1. Check for needed column widths
 SPAN w(xx+2);
 for (int y=0; y<n; y++) {
  TR*tr=tr+y;		// TODO: Not so easy!
  WCHAR t[32],s[32];
  MyLoadStringW(tr->sid,t,elemof(t));
  int l=_snwprintf(s,elemof(s),t,tr->par);
  SIZE size;
  GetTextExtentPoint32W(dc,s,l,&size);
  w.setmax(0,1,size.cx);	// build maximum
  l=MyLoadStringW(tr->sid,t,elemof(t));
  GetTextExtentPoint32W(dc,t,l,&size);
  w.setmax(1,1,size.cx);	// build maximum
  for (int x=0; x<tr->ncol; x++) {
   if (x==xx) break;	// it's a bug in given table structure
   COL*col=tr->col+x;
   if (y) {
    MyLoadStringW(col->th.sid,t,elemof(t));
    l=_snwprintf(s,elemof(s),t,col->th.par);
   }else{
    l=_snwprintf(s,elemof(s),L"%S",col->td.str);
    l=int(GenUnitW(s+l,col->td.unitprefix,tr->unitcode,1)-s);
   }
   GetTextExtentPoint32W(dc,s,l,&size);
   w.setmax(2+x,col->td.colspan,size.cx);	// build maximum
  }
 }
// 2. Emit the table
}

static void OutLine(HDC dc, UINT id, double f[], int n) {
 POINT p;		// origin
 MoveToEx(dc,0,0,&p);
 WCHAR s[128];
 FillBack(dc,p.x,p.y,p.x+gGdi.xx,p.y+gGdi.yy);
 int l=MyLoadStringW(id,s,elemof(s));
 TextOutW(dc,p.x+1,p.y,s,l);
 if (n==12) {
  for (int i=0,k=0; i<4; i++) {
   l=toStringW(f[k++],s,elemof(s));
   StrCpyNW(s+l,L" / ",elemof(s)-l); l+=3;
   l+=toStringW(f[k++],s+l,elemof(s)-l);
   StrCpyNW(s+l,L" / ",elemof(s)-l); l+=3;
   l+=toStringW(f[k++],s+l,elemof(s)-l);
   FillBack(dc,p.x+gGdi.xx*(i+1),p.y,p.x+gGdi.xx*(i+2),p.y+gGdi.yy);
   TextOutW(dc,p.x+gGdi.xx*(i+1)+1,p.y,s,l);
  }
 }else{
  for (int i=0; i<4; i++) {
   l=toStringW(f[i],s,elemof(s));
   FillBack(dc,p.x+gGdi.xx*(i+1),p.y,p.x+gGdi.xx*(i+2),p.y+gGdi.yy);
   TextOutW(dc,p.x+gGdi.xx*(i+1)+1,p.y,s,l);
  }
 }
 MoveToEx(dc,p.x,p.y+gGdi.yy,NULL);	// next line
}

static void OutLine(HDC dc, UINT id, VAL3 v[4]) {
 double f[4];
 for (int i=0; i<4; i++) f[i]=v[i].toFloat();
 OutLine(dc,id,f,4);
}

static void OutLine(HDC dc, UINT id, VAL3 v[4], VAL3 a[4], VAL3 p[4]) {
 double f[12];
 for (int i=0,k=0; i<4; i++) {
  f[k++]=v[i].toFloat();
  f[k++]=a[i].toFloat();
  f[k++]=p[i].toFloat();
 }
 OutLine(dc,id,f,12);
}

static void OutLine(HDC dc, UINT id, VAL2 v[4], char e) {
 double f[4];
 for (int i=0; i<4; i++) f[i]=v[i].toFloat(e);
 OutLine(dc,id,f,4);
}

void HandlePaint(HDC dc,LPARAM options) {
 RECT r;
 GDI::ClientRect(r);
 if (options&PRF_ERASEBKGND) {
  HBRUSH brBack=GetSysColorBrush(COLOR_WINDOW);
  SelectBrush(dc,brBack);
  PatBlt(dc,0,0,r.right,r.bottom,PATCOPY);
 }
// if (w.hMutex && WaitForSingleObject(w.hMutex,200)) return;	// timeout - should never occur, SetData() is fast
// Value (with unit) sent by multimeter
// int slen;
// SIZE size;
// WCHAR buf[32];
 HFONT ofont=SelectFont(dc,gGdi.fntSmall);
 SetBkMode(dc,TRANSPARENT);
 OutLine(dc,19,escm_data.u);
 OutLine(dc,18,escm_data.i);
 OutLine(dc,20,escm_data.p,escm_data.pa,escm_data.pp);
 OutLine(dc,21,escm_data.s,escm_data.sa,escm_data.sp);
 OutLine(dc,22,escm_data.q,escm_data.qa,escm_data.qp);
 OutLine(dc,24,escm_data.c,-3);
// if (w.hMutex) ReleaseMutex(w.hMutex);
 SelectFont(dc,ofont);
}

/*************************
 * Main window procedure *
 *************************/

static LONG_PTR CALLBACK MainWndProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
 switch (Msg) {
  case WM_CREATE: {
   ghMainWnd=Wnd;
   Config.serialCfg=0x58;
   TCHAR s[64];
   LoadString(ghInst,1,s,elemof(s));
   SetWindowText(Wnd,s);
   ghStatus=CreateStatusWindow(WS_VISIBLE|WS_CHILD,NULL,Wnd,2);

   SendMessage(Wnd,WM_WININICHANGE,0,0);

   Instance=_ttoi(PathGetArgs(GetCommandLine()));
// Kommandozeilen-Argument: Nummer der Konfiguration, 0-basiert
   LoadConfig();
   
   PostMessage(Wnd,WM_USER+100,0,0);
  }break;

  case WM_USER+100: {
   RestartEmpfang();
//   SetInitScreen();
//   if (w.conf.Make()) w.Start();
//   else PostMessage(Wnd,WM_COMMAND,0x40,0);
  }break;

  case WM_USER+22: {	// lParam = bits indicating what portion of information has changed
   if (lParam) {
    if (IsIconic(Wnd)) {
//     if (lParam&0x2000F) SetWindowTitle();	// check Readout[0] change, not else
    }else{
     if (lParam&0xA8888) gGdi.Make();
     if (gGdi.dcDblBuf) HandlePaint(gGdi.dcDblBuf,PRF_ERASEBKGND);
     InvalidateRect(Wnd,NULL,TRUE);
    }
   }
//   if (lParam&(1<<24) && Config.flashIcon) {
//    SendMessage(Wnd,WM_SETICON,ICON_SMALL,(LPARAM)ghIcons[1]);
//    SetTimer(Wnd,2,100,NULL);
//   }
  }break;

  case WM_TIMER: switch (wParam) {
   case 2: {
    KillTimer(Wnd,wParam);
    SendMessage(Wnd,WM_SETICON,ICON_SMALL,(LPARAM)ghIcons[0]);
   }break;
  }break;

  case WM_WININICHANGE: {
   TCHAR sDecimal[2];
   GetProfileString(T("intl"),T("sDecimal"),T("."),sDecimal,elemof(sDecimal));
#ifdef UNICODE
   cDecimalW=*sDecimal;
   WideCharToMultiByte(CP_ACP,0,sDecimal,1,&cDecimalA,1,".",NULL);
#else
   cDecimalA=*sDecimal;
   MultiByteToWideChar(CP_ACP,0,sDecimal,1,&cDecimalW,1);
#endif
  }return 0;

  case WM_SIZE: {
   if (ghStatus) SendMessage(ghStatus,Msg,wParam,lParam);
   if (wParam==SIZE_MINIMIZED) {
//    SetWindowTitle();
    gGdi.Delete();	// no GDI resources are needed, so free all!
   }else{
    gGdi.Make(GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam));
    if (gGdi.dcDblBuf) HandlePaint(gGdi.dcDblBuf,PRF_ERASEBKGND);
   }
  }break;

  case WM_PAINT: {
   PAINTSTRUCT ps;
   BeginPaint(Wnd,&ps);
   SendMessage(Wnd,WM_PRINTCLIENT,(WPARAM)ps.hdc,PRF_ERASEBKGND);
   EndPaint(Wnd,&ps);
  }return 0;

  case WM_PRINTCLIENT: {
   if (gGdi.dcDblBuf) {
    RECT r;
    GDI::ClientRect(r);
    BitBlt((HDC)wParam,0,0,r.right,r.bottom,gGdi.dcDblBuf,0,0,SRCCOPY);
   }else HandlePaint((HDC)wParam,lParam);
  }break;
  
  case WM_COMMAND: switch (LOWORD(wParam)) {	// from TranslateAccelerator
   case 9: SendMessage(Wnd,WM_CLOSE,0,0); break;
   case 99: MBox(Wnd,2,MB_OK); break;

   case 0x10: {
    HCURSOR oc=SetCursor(LoadCursor(0,IDC_WAIT));
    DWORD tic=GetTickCount();
    static const char esca[]={0x1b,'A','\r','\n'};
    int l=SendRecv(esca,4);
    if (l!=4) MessageBeep(0);
    static const char escm[]={0x1b,'M','\r','\n'};
    l=SendRecv(escm,4);
    if (l!=4) {
     MessageBeep(0);
     break;
    }
    string_recv(&escm_data,sizeof(escm_data),SendRecv,(BYTE*)RecordList0);
    tic=GetTickCount()-tic;
    SetCursor(oc);
    TCHAR t[64];
    _sntprintf(t,elemof(t),T("%u ms"),tic);
    SetWindowText(Wnd,t);
    InvalidateRect(Wnd,NULL,TRUE);
   }break;

   case 0x40: {
    DLGCONFIG Dlg={0,Config.serialNo,Config.serialCfg,Config.checkBits};
    if (DialogBoxParam(ghInst,MAKEINTRESOURCE(0x40),Wnd,(DLGPROC)Dlg.DlgProc,(LPARAM)&Dlg)==IDOK) {
     if (Config.serialNo!=Dlg.serialNo || Config.serialCfg!=Dlg.serialCfg) {
      Config.serialNo=Dlg.serialNo;
      Config.serialCfg=Dlg.serialCfg;
      RestartEmpfang();
     }
     Config.checkBits=Dlg.checkBits;
//    w.Stop();
//    SetInitScreen();
//    w.Start();
     SaveConfig();
    }
   }break;

//   case 0x50: CreateDialog(Wnd,WM_COPY,0,0); break;

   case 0x60: {
    DLGDATASEL Dlg={Config.sel.u};
    if (DialogBoxParam(ghInst,MAKEINTRESOURCE(0x60),Wnd,(DLGPROC)Dlg.DlgProc,(LPARAM)&Dlg)==IDOK) {
     Config.sel=Dlg.sel;
    }
   }break;
  }break;

  case WM_CLOSE: {
//   w.Stop();
   SaveConfig();
  }break;

  case WM_ENDSESSION: if (wParam) {
   SaveConfig();	// don't halt thread, instead, ensure fast Windows shutdown
  }break;

  case WM_DESTROY: {
   gGdi.Delete();
   PostQuitMessage(IDOK);
  }break;

 }
 return DefWindowProc(Wnd,Msg,wParam,lParam);
}

void CALLBACK WinMainCRTStartup() {
 ghInst=GetModuleHandle(NULL);
 InitCommonControls();

 ghIcons[0]=(HICON)LoadImage(ghInst,MAKEINTRESOURCE(1),IMAGE_ICON,16,16,0);
 ghIcons[1]=(HICON)LoadImage(ghInst,MAKEINTRESOURCE(2),IMAGE_ICON,16,16,0);

 WNDCLASSEX wc={
  sizeof(wc),
  CS_BYTEALIGNWINDOW|CS_DBLCLKS|CS_HREDRAW|CS_VREDRAW,
  MainWndProc,
  0,
  0,
  ghInst,
  LoadIcon(ghInst,MAKEINTRESOURCE(1)),
  LoadCursor(0,IDC_ARROW),
  0,
  MAKEINTRESOURCE(1),
  T("VIPSYS3"),
  ghIcons[0]};

 RegisterClassEx(&wc);

 CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,T("VIPSYS3"),NULL,
   WS_OVERLAPPEDWINDOW,
   CW_USEDEFAULT,0,400,400,
   0,0,ghInst,NULL);

 ShowWindow(ghMainWnd,SW_SHOWDEFAULT);
// ShowWindow(ghMainWnd,Config.showCmd);

 MSG Msg;
 HACCEL hAccel=LoadAccelerators(ghInst,MAKEINTRESOURCE(1));
 while (GetMessage(&Msg,0,0,0)) {
  if (TranslateAccelerator(ghMainWnd,hAccel,&Msg)) continue;
  TranslateMessage(&Msg);
  DispatchMessage(&Msg);
 }
// gHszList.Delete();
// DdeUninitialize(gDdeInst);
 ExitProcess((UINT)Msg.wParam);
}
Detected encoding: ANSI (CP1252)4
Wrong umlauts? - Assume file is ANSI (CP1252) encoded