Source file: /~heha/mb-iwp/Teller.zip/Teller.cpp

#define WIN32_LEAN_AND_MEAN
#define _WIN32_WINNT 0x0501
#include <windows.h>
#include <windowsx.h>	// Makros
#include <setupapi.h>
#include <devguid.h>
#include <commctrl.h>
//#include <ddeml.h>
#include <stdio.h>
//#include <stdlib.h>
#include <tchar.h>
#include <richedit.h>

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

#define WM_CONTINUEINIT	(WM_USER+0x100)

HINSTANCE ghInstance;
HWND ghMainWnd;
HFONT gBigFont;
TCHAR StdMBoxTitle[64];

struct RECTS{short left,top,right,bottom;};

struct{
 RECTS winpos;
 char showCmd;
 BYTE ComNum;
 BYTE motornum;
 BYTE demotime;	// Stillstand in Sekunden
 BYTE speed;	// =4
 BYTE accel;	// =1
}config;

LPTSTR posinfo;	// LocalAlloc-String, tab-separiert


/****************
 * 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;
 }
 _vsntprintf(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));
}

/***********************
 * Motor positionieren *
 ***********************/

HANDLE hCom;

static void OpenCom(void) {
 TCHAR ComName[12];
 _sntprintf(ComName,elemof(ComName),T("\\\\.\\COM%u"),config.ComNum);
 hCom=CreateFile(ComName,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,0);
 if (hCom==INVALID_HANDLE_VALUE) {hCom=0; return;}
 DCB dcb;
 InitStruct(&dcb,sizeof(dcb));
 dcb.BaudRate=38400;
 dcb.ByteSize=8;
 dcb.fBinary=TRUE;
 SetCommState(hCom,&dcb);
 static const COMMTIMEOUTS to={0,0,100,0,0};
 SetCommTimeouts(hCom,(LPCOMMTIMEOUTS)&to);
}

enum MOVEHOW {MOVEABS=1, MOVEREL=5, SYNC=2};

static bool Move(MOVEHOW how, int pos=0, int speed=0, int accel=0) {
 if (!hCom) return false;
 PurgeComm(hCom,PURGE_TXCLEAR|PURGE_RXCLEAR);
#pragma pack(1)
 struct{
  BYTE cmd;
  BYTE len;
  BYTE how;
  char accel;
  short speed;
  long pos;
 }data={
  0x80+config.motornum,
  0x08,
  how,
  accel,
  speed,
  pos
 };
#pragma pack()
 DWORD dwBytes;
 if (WriteFile(hCom,&data,sizeof(data),&dwBytes,NULL)
 && dwBytes==sizeof(data)) {
  return true;
 }
 return false;
}
/*
static bool SetPos(int pos=0) {
#pragma pack(1)
 struct{
  BYTE cmd;
  BYTE len;
  BYTE ofs;
  long pos;
 }data={
  0x98+config.motornum,
  0x05,
  0x04,
  pos
 };
#pragma pack()
 DWORD dwBytes;
 if (WriteFile(hCom,&data,sizeof(data),&dwBytes,NULL)
 && dwBytes==sizeof(data)) {
  return true;
 }
 return false;
}
*/
static void InitCom(void) {
 OpenCom();
 if (hCom) {
 }else{
  MBox(ghMainWnd,(LPCTSTR)8,MB_OK|MB_ICONEXCLAMATION,config.ComNum);
  SendMessage(ghMainWnd,WM_COMMAND,0x40,0);	// sofort zur Konfiguration
 }
}

#define LNAN 0x80000000;

struct MOTORINFO {
 BYTE phase;
 char accel;
 short speed;
 long pos;
 bool Query();
};

// Abfrage der Position in Mikroschritt, 256 µSchritt = Halbschritt
bool MOTORINFO::Query() {
 if (hCom) {
  char c='A'+config.motornum;
  DWORD dwBytes;
  WriteFile(hCom,&c,1,&dwBytes,NULL);
  if (ReadFile(hCom,this,sizeof(*this),&dwBytes,NULL)
  && dwBytes==sizeof(*this)) {
   return true;
  }
 }
 return false;
}

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

static INT_PTR CALLBACK HardwareDlgProc(HWND Wnd,UINT Msg,WPARAM wParam,LPARAM lParam) {
 switch (Msg) {
  case WM_INITDIALOG: {
   SetTimer(Wnd,1,1,NULL);
   SetDlgItemInt(Wnd,16,config.motornum,FALSE);
   SetDlgItemInt(Wnd,17,config.demotime,FALSE);
   SetDlgItemText(Wnd,22,posinfo);

  }return TRUE;

  case WM_DEVICECHANGE: SetTimer(Wnd,1,1500,NULL);

  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)
     && _stscanf(s,T("COM%u"),&num)==1) {	// LPTx ausfiltern
      int idx=ComboBox_AddString(hCombo,s);
      ComboBox_SetItemData(hCombo,idx,num);
      if (num==config.ComNum) ComboBox_SetCurSel(hCombo,idx);
     }
     RegCloseKey(hKey);
    }
    SetupDiDestroyDeviceInfoList(devs);
   }
  }break;

  case WM_COMMAND: switch (LOWORD(wParam)) {
   case 10: switch (HIWORD(wParam)) {
    case EN_CHANGE: SetTimer(Wnd,10,200,NULL); break;
   }break;
   
   case IDOK: {
    HWND hCombo=GetDlgItem(Wnd,13);
    int i=ComboBox_GetCurSel(hCombo);
    if (i<0) {
     MBox(Wnd,(LPCTSTR)2,MB_OK|MB_ICONEXCLAMATION);
     SetFocus(hCombo);
     break;
    }
    config.ComNum=(BYTE)ComboBox_GetItemData(hCombo,i);
    config.motornum=(BYTE)GetDlgItemInt(Wnd,16,NULL,FALSE);
    config.demotime=(BYTE)GetDlgItemInt(Wnd,17,NULL,FALSE);
    LocalFree(posinfo);
    HWND hEdit=GetDlgItem(Wnd,22);
    int len=GetWindowTextLength(hEdit)+1;
    posinfo=(LPTSTR)LocalAlloc(LMEM_FIXED,len*sizeof(TCHAR));
    GetWindowText(hEdit,posinfo,len);
   }nobreak;
   case IDCANCEL: EndDialog(Wnd,wParam); break;
  }break;

 }
 return FALSE;
}

/****************
 * Hauptfenster *
 ****************/
 
static void LoadConfig(void) {
 HKEY key;
 if (RegOpenKeyEx(HKEY_CURRENT_USER,T("Software\\h#s\\Teller"),
   0,KEY_QUERY_VALUE,&key)) return;
 DWORD size=sizeof(config);
 if (!RegQueryValueEx(key,T("Config"),NULL,NULL,(LPBYTE)&config,&size)) {
  WINDOWPLACEMENT wp;
  InitStruct(&wp,sizeof(wp));
  GetWindowPlacement(ghMainWnd,&wp);
  wp.rcNormalPosition.left=config.winpos.left;
  wp.rcNormalPosition.top=config.winpos.top;
  wp.rcNormalPosition.right=config.winpos.right;
  wp.rcNormalPosition.bottom=config.winpos.bottom;
  wp.showCmd=config.showCmd;
  SetWindowPlacement(ghMainWnd,&wp);
 }
 size=0;
 RegQueryValueEx(key,T("PosInfo"),NULL,NULL,NULL,&size);
 posinfo=(LPTSTR)LocalAlloc(LMEM_FIXED,size);
 if (size) {
  RegQueryValueEx(key,T("PosInfo"),NULL,NULL,(LPBYTE)posinfo,&size);
 }
 RegCloseKey(key);
}

static void FillListbox() {
 HWND lb=GetDlgItem(ghMainWnd,17);
 ListBox_ResetContent(lb);
 LPTSTR p=posinfo;
 while (p && *p) {
  LPTSTR q=_tcschr(p,'\n');
  LPTSTR n;
  if (!q) {
   q=p+_tcslen(p);
   n=q;
  }else n=q+1;
  if (*--q!='\r') q++;
  if (q>p+1) {	// keine Leerzeilen
   TCHAR save=*q;
   *q=0;
   ListBox_AddString(lb,p);
   *q=save;
  }
  p=n;
 }
}

static void SaveConfig(void) {
 HKEY key;
 if (RegCreateKeyEx(HKEY_CURRENT_USER,T("Software\\h#s\\Teller"),
   0,NULL,REG_OPTION_NON_VOLATILE,KEY_SET_VALUE,NULL,&key,NULL)) return;
 RegSetValue(key,NULL,REG_SZ,StdMBoxTitle,(_tcslen(StdMBoxTitle)+1)*sizeof(TCHAR));
 WINDOWPLACEMENT wp;
 InitStruct(&wp,sizeof(wp));
 GetWindowPlacement(ghMainWnd,&wp);
 config.winpos.left=(short)wp.rcNormalPosition.left;
 config.winpos.top=(short)wp.rcNormalPosition.top;
 config.winpos.right=(short)wp.rcNormalPosition.right;
 config.winpos.bottom=(short)wp.rcNormalPosition.bottom;
 config.showCmd=wp.showCmd;
 RegSetValueEx(key,T("Config"),0,REG_BINARY,(LPBYTE)&config,sizeof(config));
 if (posinfo) {
  RegSetValueEx(key,T("PosInfo"),0,REG_SZ,(LPBYTE)posinfo,(_tcslen(posinfo)+1)*sizeof(TCHAR));
 }
 RegCloseKey(key);
}

static int Countdown;

static void InitCountdown() {
 Countdown=config.demotime+1;
}

static SIZE prevsize;
#define PERIOD (400<<8)

static void ChangePos(UINT id, int x, int y, int w, int h) {
 HWND wnd=GetDlgItem(ghMainWnd,id);
 RECT r;
 GetWindowRect(wnd,&r);
 r.right-=r.left;
 r.bottom-=r.top;
 ScreenToClient(ghMainWnd,(LPPOINT)&r);
 MoveWindow(wnd,r.left+x,r.top+y,r.right+w,r.bottom+h,TRUE);
}

BOOL CALLBACK MainDlgProc(HWND Wnd,UINT Msg,WPARAM wParam,LPARAM lParam) {
 switch (Msg) {
  case WM_INITDIALOG: {
   ghMainWnd=Wnd;
   RECT r;
   GetClientRect(Wnd,&r);
   prevsize.cx=r.right;
   prevsize.cy=r.bottom;
   gBigFont=CreateFont(48,0,0,0,0,0,0,0,0,0,0,0,0,T("Arial"));
   HWND riched=GetDlgItem(Wnd,16);
   SendMessage(riched,WM_SETFONT,(WPARAM)gBigFont,FALSE);
   PARAFORMAT2 pf;
   pf.cbSize=sizeof pf;
   pf.dwMask=PFM_ALIGNMENT;
   pf.wAlignment=PFA_CENTER;
   SendMessage(riched,EM_SETPARAFORMAT,0,(LPARAM)&pf);
   SendMessage(riched,EM_SETBKGNDCOLOR,0,RGB(192,192,192));
   SETTEXTEX st={ST_DEFAULT/*|ST_UNICODE*/,1200};
   SendMessage(riched,EM_SETTEXTEX,(WPARAM)&st,(LPARAM)L"{\\rtf RE50W by Jim Dunne Copyright (C) 2005\\par http://www.topjimmy.net/tjs \\par {\\field{\\*\\fldinst{HYPERLINK mailto:jim@dunnes.net }}{\\fldrslt{\\cf1\\ul jim@dunnes.net}}}}");

   LoadConfig();
   FillListbox();
   InitCountdown();
   PostMessage(Wnd,WM_CONTINUEINIT,0,0);
  }return TRUE;
  
  case WM_CONTINUEINIT: {
   if (config.ComNum) InitCom();
   else SendMessage(Wnd,WM_COMMAND,0x40,0);	// beim ersten Start sofort zur Konfiguration
   SetTimer(Wnd,100,100,NULL);
  }break;

  case WM_TIMER: {	// Position abfragen; Demo-Modus ablaufen lassen
   MOTORINFO info;
   if (info.Query()) {
    long p=info.pos;
/*
    if ((unsigned)p>=PERIOD) {
     if (p<0) p+=PERIOD;
     else p-=PERIOD;	// in Bereich 0..<400 Schritt bringen
     SetPos(p);
    }
*/
    SetDlgItemInt(Wnd,19,p>>8,TRUE);
    if (!(info.phase&0x0F) && IsDlgButtonChecked(Wnd,18) && !--Countdown) {
     HWND lb=GetDlgItem(Wnd,17);
     int n=ListBox_GetCount(lb);
     int i=ListBox_GetCurSel(lb);
     i++; if (i>=n) i=0;
     ListBox_SetCurSel(lb,i);
     SendMessage(Wnd,WM_COMMAND,MAKELONG(17,LBN_SELCHANGE),(LPARAM)lb);
     InitCountdown();
    }
   }
  }break;
  
  case WM_SIZE: {
   if (wParam==SIZE_RESTORED && prevsize.cx) {
    SIZE newsize={LOWORD(lParam),HIWORD(lParam)};
    int dx=newsize.cx-prevsize.cx;
    int dy=newsize.cy-prevsize.cy;
    ChangePos(16,0,0,dx,0);
    ChangePos(17,0,0,dx,dy);
    ChangePos(18,0,dy,dx,0);
    ChangePos(19,dx,dy,0,0);
    prevsize=newsize;
   }
  }break;
  
  case WM_COMMAND:
  switch (wParam) {
   case 2: EndDialog(Wnd,wParam); break;

   case MAKELONG(17,LBN_SELCHANGE): {
    int i=ListBox_GetCurSel((HWND)lParam);
    TCHAR s[256];
    ListBox_GetText((HWND)lParam,i,s);	// Pufferüberlauf droht!!
    LPTSTR tab=_tcschr(s,'\t');
    if (tab) {
     SetDlgItemText(Wnd,16,tab+1);	// Großen Text mitführen
    }
    long pos=_ttoi(s)<<8;
    Move(MOVEABS,pos,config.speed,config.accel);
   }break;

   case 18: {
    InitCountdown();
   }break;

   case 0x40: if (DialogBox(ghInstance,MAKEINTRESOURCE(100),Wnd,HardwareDlgProc)==IDOK) {
    if (hCom) CloseHandle(hCom);
    InitCom();
    FillListbox();
    SaveConfig();
   }break;
  }break;
  
  case WM_DESTROY: {
   SaveConfig();
   if (hCom) CloseHandle(hCom);
   if (gBigFont) DeleteFont(gBigFont);
  }break;

  case WM_ENDSESSION: if (wParam) SaveConfig(); break;
 }
  
 return FALSE;
}

/******************************
 * Hauptprogramm (lächerlich) *
 ******************************/
 
int _stdcall WinMainCRTStartup(void) {
 WNDCLASS wc={
  CS_DBLCLKS,
  DefDlgProc,
  0,
  DLGWINDOWEXTRA,
  ghInstance=GetModuleHandle(NULL),
  LoadIcon(ghInstance,MAKEINTRESOURCE(100)),
  LoadCursor(0,IDC_ARROW),
  (HBRUSH)(COLOR_BACKGROUND+1),
  NULL,
  T("Teller")
 };
 RegisterClass(&wc);
// static const INITCOMMONCONTROLSEX icc={sizeof(icc),ICC_WIN95_CLASSES|ICC_USEREX_CLASSES};
// InitCommonControlsEx((INITCOMMONCONTROLSEX*)&icc);
 InitCommonControls();		// wichtig für Teletubbieoptik!
// LoadLibrary(T("riched20.dll"));
 LoadLibrary(T("msftedit.dll"));
 LoadString(ghInstance,1,StdMBoxTitle,elemof(StdMBoxTitle));
 ExitProcess(DialogBox(0,MAKEINTRESOURCE(101),0,MainDlgProc));
}
Detected encoding: ANSI (CP1252)4
Wrong umlauts? - Assume file is ANSI (CP1252) encoded