Source file: /~heha/mb-iwp/Thermometer/TempReqNT.zip/src/TempReqNT.cpp

#define WIN32_LEAN_AND_MEAN
#define _WIN32_WINNT 0x0501
#include <windows.h>
#include <windowsx.h>	// Makros
#include <setupapi.h>
#include <devguid.h>
#include <shlwapi.h>	// ohne msvcrt.dll
#include <commctrl.h>
#include <ddeml.h>

#define elemof(x) (sizeof(x)/sizeof((x)[0]))
#define T(x) TEXT(x)
#define nobreak

#ifdef DEBUG
# define _debug(x) DebugPrintf x
void _cdecl DebugPrintf(LPCTSTR s,...) {
 TCHAR buf[256];
 wvnsprintf(buf,elemof(buf),s,(va_list)(&s+1));
 OutputDebugString(buf);
}
#else
# define _debug(x)
#endif

#ifdef WIN32
# define Send_WM_Command(ToWnd,CtlId,NotifyCode,FromWnd)\
  SendMessage(ToWnd,WM_COMMAND,MAKELONG(CtlId,NotifyCode),(LPARAM)FromWnd);
#else	// Win16
# define Send_WM_Command(ToWnd,CtlId,NotifyCode,FromWnd)\
  SendMessage(ToWnd,WM_COMMAND,CtlId,MAKELONG((UINT)FromWnd,NotifyCode));
#endif

#ifdef UNICODE
# define CF_TXT CF_UNICODETEXT
#else
# define CF_TXT CF_TEXT
#endif

#define WM_CONTINUEINIT	(WM_USER+0x100)

HINSTANCE ghInstance;
HWND ghMainWnd;
HFONT gBigFont;
TCHAR StdMBoxTitle[64];
DWORD gComNum;
BOOL gShowSign;
TCHAR sDecimal[2];	// je nach Windows-Einstellung
DWORD gDdeInst;
HSZ hszService;
UINT CF_XlTable;	// Clipboard-Format
EXTERN_C int _fltused;	// Linker ruhig stellen
int _fltused;

/****************
 * aus WUTILS.C *
 ****************/

//Win32-typische Strukturen mit DWORD-Ausrichtung initialisieren
void _fastcall InitStruct(LPVOID p, UINT len) {
 LPUINT p2=(LPUINT)p;		// sonst hat C++ Verdauungsstörungen
 *p2=len; len/=sizeof(UINT); len--;
 if (len) do *++p2=0; while (--len);
}

int vMBox(HWND Wnd, LPCTSTR Text, UINT Type, va_list va) {
 TCHAR buf[256],buf2[256];
 if (!((DWORD_PTR)Text>>16)) {
  LoadString(ghInstance,(UINT)(DWORD_PTR)Text,buf2,elemof(buf2));
  Text=buf2;
 }
 wvnsprintf(buf,elemof(buf),Text,va);
 return MessageBox(Wnd,buf,StdMBoxTitle,Type);
}

int _cdecl MBox(HWND Wnd, LPCTSTR Text, UINT Type, ...) {
 return vMBox(Wnd,Text,Type,(va_list)(&Type+1));
}

/*************
 * Dialog(e) *
 *************/

INT_PTR CALLBACK HardwareDlgProc(HWND Wnd,UINT Msg,WPARAM wParam,LPARAM lParam) {
 switch (Msg) {
  case WM_INITDIALOG: {
   SetTimer(Wnd,1,1,NULL);
   CheckDlgButton(Wnd,12,gShowSign);
  }return TRUE;

  case WM_DEVICECHANGE: SetTimer(Wnd,1,1500,NULL); break;
  
  case WM_TIMER: {
   KillTimer(Wnd,wParam);
// serielle Schnittstellen (neu) listen (bei jedem WM_DEVICECHANGE bspw. für USB)
   HWND hCombo=GetDlgItem(Wnd,13);
   ComboBox_ResetContent(hCombo);
   HANDLE devs=SetupDiGetClassDevs(&GUID_DEVCLASS_PORTS,NULL,0,DIGCF_PRESENT);
   if (devs!=INVALID_HANDLE_VALUE) {
    SP_DEVINFO_DATA devInfo;
    devInfo.cbSize=sizeof devInfo;
    for (DWORD i=0; SetupDiEnumDeviceInfo(devs,i,&devInfo); i++) {
     HKEY hKey;
     TCHAR s[16];
     DWORD slen=sizeof s;
     DWORD num;
     if ((hKey=SetupDiOpenDevRegKey(devs,&devInfo,DICS_FLAG_GLOBAL,0,DIREG_DEV,KEY_READ))
       ==INVALID_HANDLE_VALUE) continue;
     if (!RegQueryValueEx(hKey,T("PortName"),NULL,NULL,(LPBYTE)s,&slen)
     && s[0]=='C'		// LPTx ausfiltern
     && (num=StrToInt(s+3))) {	// muss !=0 sein
      int idx=ComboBox_AddString(hCombo,s);
      ComboBox_SetItemData(hCombo,idx,num);
      if (num==gComNum) ComboBox_SetCurSel(hCombo,idx);
     }
     RegCloseKey(hKey);
    }
    SetupDiDestroyDeviceInfoList(devs);
   }
  }break;

  case WM_COMMAND: switch (LOWORD(wParam)) {
   case IDOK: {
    HWND hCombo=GetDlgItem(Wnd,13);
    int i=ComboBox_GetCurSel(hCombo);
    if (i<0) {
     MBox(Wnd,(LPCTSTR)2,MB_OK);
     SetFocus(hCombo);
     break;
    }
    gComNum=(DWORD)ComboBox_GetItemData(hCombo,i);
    gShowSign=IsDlgButtonChecked(Wnd,12);
   }nobreak;
   case IDCANCEL: EndDialog(Wnd,wParam); break;
  }break;
 }
 return FALSE;
}

/*********************
 * Temperaturabfrage *
 *********************/
TCHAR gAnzeige[12];
double gValue;
HANDLE hCom;

void OpenCom(void) {
 TCHAR ComName[12];	// ohne Backslashes geht's nur bis COM9!
 wnsprintf(ComName,elemof(ComName),T("\\\\.\\COM%u"),gComNum);
 hCom=CreateFile(ComName,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,0);
 if (hCom==INVALID_HANDLE_VALUE) hCom=0;
}

static const union{
 __int64 i;
 double d;
}_NaN={-1};		// alle Bits 1
#define NaN _NaN.d

void QueryTemp(void) {
 TCHAR Anzeige[12];
 LoadString(ghInstance,3/*Fehler*/,Anzeige,elemof(Anzeige));
 gValue=NaN;	// ungültige Zahl zuweisen
 if (hCom) {
  DCB dcb;
  InitStruct(&dcb,sizeof(dcb));
  dcb.BaudRate=9600;
  dcb.ByteSize=7;
  dcb.Parity=ODDPARITY;
  dcb.StopBits=TWOSTOPBITS;
  dcb.fBinary=TRUE;
  dcb.fParity=TRUE;
  SetCommState(hCom,&dcb);
  static const COMMTIMEOUTS to={0,0,100,0,0};
  SetCommTimeouts(hCom,(LPCOMMTIMEOUTS)&to);
  PurgeComm(hCom,PURGE_TXCLEAR|PURGE_RXCLEAR);
// Hin müssen 9 Bytes folgenden Inhaltes:
  static const char WriteMsg[]={0x02,"00 1A05 "};
// Zurück kommt {0x06 "*xxxxcc"}
// mit xxxx = vzb. Temperatur in Hundertstel als Hexzahl
// und cc = Prüfcode (unbekannt, auch nach längerer Suche ungeknackt)
  char ReadMsg[8];
  DWORD bytes;
  if (WriteFile(hCom,WriteMsg,9,&bytes,NULL)
  && bytes==9
  && ReadFile(hCom,ReadMsg,8,&bytes,NULL)
  && bytes==8
  && ReadMsg[0]==0x06
  && ReadMsg[1]=='*') {
   ReadMsg[0]='0';
   ReadMsg[1]='x';
   ReadMsg[6]=0;
   int t;	// Temperatur in Hundertstel
   if (StrToIntExA(ReadMsg,STIF_SUPPORT_HEX,&t)) {
    t=(short)t;	// Vorzeichen aufblasen
    gValue=(double)t/100;	// gültige Zahl zuweisen
    if (-10000<=t && t<=20000) {
     TCHAR sign=' ';
     if (gShowSign && t>0) sign='+';	// falls gewünscht
     if (t<0) {
      sign='-';
      t=-t;
     }
     wnsprintf(Anzeige,elemof(Anzeige),T("%c%u%c%02u °C"),sign,t/100,sDecimal[0],t%100);
    }
   }
  }
 }
 if (lstrcmp(gAnzeige,Anzeige)) {
  lstrcpy(gAnzeige,Anzeige);
  if (IsIconic(ghMainWnd)) SetWindowText(ghMainWnd,gAnzeige);
  else InvalidateRect(ghMainWnd,NULL,FALSE);
  DdePostAdvise(gDdeInst,0,0);
 }
}

HDDEDATA CALLBACK DdeCallback(UINT uType,UINT uFmt,HCONV,HSZ,HSZ hsz2,
  HDDEDATA,ULONG_PTR,ULONG_PTR) {
 HDDEDATA ret=DDE_FNOTPROCESSED;
 switch (uType) {
  case XTYP_CONNECT: ret=(HDDEDATA)TRUE; break;

  case XTYP_WILDCONNECT: {		// Automatische Enumeration durch Logger
   HSZ pairs[3]={hszService,hszService};
   ret=DdeCreateDataHandle(gDdeInst,(LPBYTE)pairs,sizeof pairs,0,0,0,0);
  }break;
  
  case XTYP_ADVSTART: if (uFmt==CF_TEXT || uFmt==CF_UNICODETEXT || uFmt==CF_XlTable) ret=(HDDEDATA)TRUE; break;
  
  case XTYP_ADVREQ:
  case XTYP_REQUEST: {
  
   if (uFmt==CF_TEXT) {
#ifdef UNICODE
    CHAR buf[elemof(gAnzeige)];
    int len=WideCharToMultiByte(CP_ACP,0,gAnzeige,-1,buf,elemof(buf),NULL,NULL)+1;
    ret=DdeCreateDataHandle(gDdeInst,(LPBYTE)buf,len,0,hsz2,uFmt,0);
#else
    ret=DdeCreateDataHandle(gDdeInst,(LPBYTE)gAnzeige,lstrlen(gAnzeige)+1,0,hsz2,uFmt,0);
#endif
   }else if (uFmt==CF_UNICODETEXT) {
#ifdef UNICODE
    ret=DdeCreateDataHandle(gDdeInst,(LPBYTE)gAnzeige,(lstrlen(gAnzeige)+1)<<1,0,hsz2,uFmt,0);
#else
    WCHAR buf[elemof(gAnzeige)];
    int len=MultiByteToWideChar(CP_ACP,0,gAnzeige,-1,buf,elemof(buf))+1;
    ret=DdeCreateDataHandle(gDdeInst,(LPBYTE)buf,len<<1,0,hsz2,uFmt,0);
#endif
   }else if (uFmt==CF_XlTable) {
#pragma pack(2)
    struct{
     short tdtTable,dimensions,rows,cols;
     short tdtType,tdtSize;
     union{
      double tdtDouble;
      short tdtShort;
     };
    }XlTable={
     0x0010,	// Header
     4,		// 4 Bytes folgen
     1,		// 1 Spalte
     1,		// 1 Zeile
     1,		// DOUBLE
     sizeof(double),
     gValue};
#pragma pack()
    unsigned size=sizeof(XlTable);
    if (gValue!=gValue) {	// INF, NaN und ähnliches
     XlTable.tdtType=4;		// Error
     XlTable.tdtSize=sizeof(short);
     XlTable.tdtShort=42;	// "#NV" (nicht verfügbar)
     size-=6;
    }
    ret=DdeCreateDataHandle(gDdeInst,(LPBYTE)&XlTable,size,0,hsz2,uFmt,0);
   }
  }
 }
 return ret;
}


/****************
 * Hauptfenster *
 ****************/
 
void LoadConfig(void) {
 HKEY key;
 if (RegOpenKey(HKEY_CURRENT_USER,T("Software\\h#s\\TempReqNT"),&key)) return;
 WINDOWPLACEMENT wp;
 InitStruct(&wp,sizeof(wp));
 GetWindowPlacement(ghMainWnd,&wp);
 DWORD size=sizeof(wp.rcNormalPosition);
 RegQueryValueEx(key,T("WindowPos"),NULL,NULL,(LPBYTE)&wp.rcNormalPosition,&size);
 SetWindowPlacement(ghMainWnd,&wp);
 size=sizeof(gComNum);
 RegQueryValueEx(key,T("ComNum"),NULL,NULL,(LPBYTE)&gComNum,&size);
 size=sizeof(gShowSign);
 RegQueryValueEx(key,T("ShowSign"),NULL,NULL,(LPBYTE)&gShowSign,&size);
 RegCloseKey(key);
}
 
void SaveConfig(void) {
 HKEY key;
 if (RegCreateKey(HKEY_CURRENT_USER,T("Software\\h#s\\TempReqNT"),&key)) return;
 RegSetValueEx(key,NULL,0,REG_SZ,(PBYTE)StdMBoxTitle,(lstrlen(StdMBoxTitle)+1)*sizeof(TCHAR));
 WINDOWPLACEMENT wp;
 InitStruct(&wp,sizeof(wp));
 GetWindowPlacement(ghMainWnd,&wp);
 RegSetValueEx(key,T("WindowPos"),0,REG_BINARY,(LPBYTE)&wp.rcNormalPosition,sizeof(wp.rcNormalPosition));
 RegSetValueEx(key,T("ComNum"),0,REG_DWORD,(LPBYTE)&gComNum,sizeof(DWORD));
 RegSetValueEx(key,T("ShowSign"),0,REG_DWORD,(LPBYTE)&gShowSign,sizeof(DWORD));
 RegCloseKey(key);
}

LRESULT CALLBACK MainWndProc(HWND Wnd,UINT Msg,WPARAM wParam,LPARAM lParam) {
 switch (Msg) {
  case WM_CREATE: {
   ghMainWnd=Wnd;
   HMENU SysMenu=GetSystemMenu(Wnd,FALSE);
   TCHAR s[64];
   LoadString(ghInstance,4,s,elemof(s));	// Menüpunkte ergänzen
   InsertMenu(SysMenu,SC_CLOSE,0,0x40,s);
   LoadString(ghInstance,5,s,elemof(s));
   InsertMenu(SysMenu,SC_CLOSE,0,0x50,s);
   SendMessage(Wnd,WM_WININICHANGE,0,0);
   SendMessage(Wnd,WM_QUERYOPEN,0,0);	// Titel setzen
   LoadConfig();
   PostMessage(Wnd,WM_CONTINUEINIT,0,0);
  }break;
  
  case WM_CONTINUEINIT: {
   if (gComNum) {
    OpenCom();
    QueryTemp();
   if (!hCom && MBox(Wnd,(LPCTSTR)6,MB_OKCANCEL,gComNum)==IDOK) Send_WM_Command(Wnd,0x40,0,0);
   }else Send_WM_Command(Wnd,0x40,0,0);		// beim ersten Start sofort zur Konfiguration
   SetTimer(Wnd,100,1000,NULL);
  }break;
  
  case WM_WININICHANGE: {
   GetProfileString(T("intl"),T("sDecimal"),T("."),sDecimal,elemof(sDecimal));
  }return 0;

  case WM_SIZE: {
   if (wParam==SIZE_MINIMIZED) {
    SetWindowText(Wnd,gAnzeige);
   }else{
    if (gBigFont) DeleteFont(gBigFont);
    gBigFont=CreateFont(-GET_Y_LPARAM(lParam),GET_X_LPARAM(lParam)/12,0,0,0,0,0,0,0,0,0,0,0,T("Arial"));
   }
  }break;
  
  case WM_PAINT: {
   PAINTSTRUCT ps;
   BeginPaint(Wnd,&ps);
   SendMessage(Wnd,WM_PRINTCLIENT,(WPARAM)ps.hdc,0);
   EndPaint(Wnd,&ps);
  }return 0;
  
  case WM_PRINTCLIENT: {
   RECT r;
   GetClientRect(Wnd,&r);
   SelectFont((HDC)wParam,gBigFont);
   SetTextAlign((HDC)wParam,TA_BOTTOM|TA_CENTER);
   ExtTextOut((HDC)wParam,r.right/2,r.bottom,ETO_OPAQUE,&r,gAnzeige,lstrlen(gAnzeige),NULL);
  }break;
  
  case WM_QUERYOPEN: {
   TCHAR s[64];
   LoadString(ghInstance,1,s,elemof(s));
   SetWindowText(Wnd,s);
  }break;
  
  case WM_COPY: {
   HGLOBAL hMem=GlobalAlloc(GMEM_MOVEABLE,(lstrlen(gAnzeige)+1)*sizeof(TCHAR));
   if (!hMem) break;
   if (OpenClipboard(Wnd) && EmptyClipboard()) {
    lstrcpy((LPTSTR)GlobalLock(hMem),gAnzeige);
    GlobalUnlock(hMem);
    SetClipboardData(CF_TXT,hMem);
    CloseClipboard();
   }else GlobalFree(hMem);
  }break;
  
  case WM_COMMAND:	// von TranslateAccelerator
  case WM_SYSCOMMAND: switch (wParam&0xFFF0) {
   case 0x40: if (DialogBox(ghInstance,MAKEINTRESOURCE(100),Wnd,HardwareDlgProc)==IDOK) {
    if (hCom) CloseHandle(hCom);
    OpenCom();
    QueryTemp();
   }break;
   case 0x50: SendMessage(Wnd,WM_COPY,0,0);
  }break;
  
  case WM_TIMER: QueryTemp(); break;	// Temperaturabfrage
  
  case WM_CLOSE: SendMessage(Wnd,WM_ENDSESSION,1,0); break;	// speichern auslösen!
  
  case WM_ENDSESSION: if (wParam) SaveConfig(); break;
  
  case WM_DESTROY: {
   if (hCom) CloseHandle(hCom);
   if (gBigFont) DeleteFont(gBigFont);
   PostQuitMessage(0);
  }break;
 }
  
 return DefWindowProc(Wnd,Msg,wParam,lParam);
}

/******************************
 * Hauptprogramm (lächerlich) *
 ******************************/
 
int _stdcall WinMainCRTStartup(void) {
 ghInstance=GetModuleHandle(NULL);
 
 WNDCLASSEX wc;
 InitStruct(&wc,sizeof(wc));
  wc.style=CS_HREDRAW|CS_VREDRAW;
  wc.lpfnWndProc=MainWndProc;
  wc.hInstance=ghInstance;
  wc.hCursor=LoadCursor(0,IDC_ARROW);
  wc.hIcon=LoadIcon(ghInstance,MAKEINTRESOURCE(100));
  wc.lpszMenuName=MAKEINTRESOURCE(100);
  wc.lpszClassName=T("TempReqNT");

 RegisterClassEx(&wc);
 InitCommonControls();		// wichtig für Teletubbieoptik!
 LoadString(ghInstance,1,StdMBoxTitle,elemof(StdMBoxTitle));
 ghMainWnd=CreateWindowEx(WS_EX_ACCEPTFILES|WS_EX_OVERLAPPEDWINDOW,
   T("TempReqNT"),NULL,WS_OVERLAPPEDWINDOW,
   CW_USEDEFAULT,0,
   CW_USEDEFAULT,0,
   0,0,ghInstance,GetCommandLine());
 ShowWindow(ghMainWnd,SW_SHOWDEFAULT);
 
 CF_XlTable=RegisterClipboardFormat(T("XlTable"));	// Excel-Tabellenformat
 DdeInitialize(&gDdeInst,DdeCallback,APPCLASS_STANDARD
   |CBF_FAIL_POKES|CBF_FAIL_EXECUTES
   |CBF_SKIP_CONNECT_CONFIRMS|CBF_SKIP_REGISTRATIONS
   |CBF_SKIP_DISCONNECTS|CBF_SKIP_UNREGISTRATIONS,0);
 hszService=DdeCreateStringHandle(gDdeInst,T("TempReqNT"),CP_WINNEUTRAL);
 DdeNameService(gDdeInst,hszService,0,DNS_REGISTER|DNS_FILTERON);
 
 MSG Msg;
 HACCEL hAccel=LoadAccelerators(ghInstance,MAKEINTRESOURCE(100));
 while (GetMessage(&Msg,0,0,0)) {
  if (TranslateAccelerator(ghMainWnd,hAccel,&Msg)) continue;
  TranslateMessage(&Msg);
  DispatchMessage(&Msg);
 }
 DdeFreeStringHandle(gDdeInst,hszService);
 DdeUninitialize(gDdeInst);
 ExitProcess((UINT)Msg.wParam);
}
Detected encoding: ANSI (CP1252)4
Wrong umlauts? - Assume file is ANSI (CP1252) encoded