Visual C++ Mix
Hier sind einige Grundlagen, die ich sicherlich immer wieder brauchen werden, wenn ich mit C++ arbeite.
 
 
Grafik - MFC
* Minimalprogramm - CFrameWnd
* Minimalprogramm - CDialog
* Fensterevents
* Verbinden der grafischen Elemente mit einer Variablen
* MessageBox
* Focus setzen
* UpdateData und Nachteile
* Kontextmenü erstellen
* Neue Klasse einbinden
* Tabellenelement
* Highlighten von Tabellen
* Fensterschließen verhindern
* Baumstruktur erstellen
* MDI-Anwendung ohne ein erstes Fenster

* Grafische Oberfläche á la Visual Studio 

Funktional - C++
* Beispiel für das Schreiben in eine Datei
* Beispiel für das Lesen aus einer Datei
* Auf die Serielle Schnittstelle schreiben
* Serielle Schnittstelle: Read- und Write- Funktionen
* Timer
* Threads
* Buchstaben im ASCII-Code ausgeben
* Debugausgaben
* Pfad des gestarteten Programmes ermitteln
* Lokal- und Systemzeit
* CString in char* konvertieren 
* Verzeichnis auslesen
* Verhindern, dass ein Programm mehrmals gestartet wird
* Mit der Registry arbeiten

 

Downloads:
* Klasse für die serielle Schnittstelle
* Klasse für die Can-Bus-Schnittstelle
* weitere siehe unten



Minimalprogramm - CFrameWnd
neues Projekt    - Win32-Anwendung
Projekt    - Einstellungen    - MFC in einer dynamisch genutzten DLL verwenden
Datei CMain.h:


#include <afxwin.h>
class CMain : public CFrameWnd
{
public:
    CMain();
};
Datei CAnwendung.h:


#include <afxwin.h>
#include "CMain.h"
class CAnwendung : public CWinApp
{
public:
   virtual BOOL InitInstance();
};
CAnwendung anwendung;
Datei CMain.cpp:


#include "CMain.h"
CMain::CMain()
{
    RECT fenster ={550,350,700,500};
    Create(NULL,"Hi",
               WS_OVERLAPPEDWINDOW,fenster);
}
Datei CAnwendung.cpp:


#include "CAnwendung.h"
BOOL CAnwendung::InitInstance()
{
  m_pMainWnd = new CMain();
  m_pMainWnd->ShowWindow(SW_SHOW);
  m_pMainWnd->UpdateWindow();
  return TRUE;
}



Minimalprogramm - CDialog
neues Projekt    - Win32-Anwendung
Projekt    - Einstellungen    - MFC in einer dynamisch genutzten DLL verwenden
Man muß natürlich noch eine Ressource einfügen (Einfügen    - Ressource) und anschließend die entstandenen Dateien resource.h und xname.rc in das Projekt einbinden.
Datei CMain.h:


#include <afxwin.h>
#include "resource.h"
class CMain : public CDialog
{
public:
    CMain(UINT nIDTemplate, CWnd* pParentWnd=NULL):
                CDialog(nIDTemplate,pParentWnd){};
protected:
    BOOL OnInitDialog();
};
Datei CAnwendung.h:


#include <afxwin.h>
#include "CMain.h"
class CAnwendung : public CWinApp

public:
   virtual BOOL InitInstance();
};
CAnwendung anwendung;
Datei CMain.cpp:


#include "CMain.h"
BOOL CMotor::OnInitDialog()
{
    CDialog::OnInitDialog();
    return TRUE;
}
Datei CAnwendung.cpp:


#include "CAnwendung.h"
BOOL CAnwendung::InitInstance()
{
    CMain Main(IDD_MAIN);
    m_pMainWnd = &Main;
    Main.DoModal();
    return FALSE;
}


Fensterevents
 
class CX : public CDialog
{
    ...
protected:
    afx_msg void OnSenden();
    afx_msg int OnCreate(LPCREATESTRUCT lpCS);
    afx_msg void OnDestroy( );

DECLARE_MESSAGE_MAP()
};
 

#include "CX.h"
BEGIN_MESSAGE_MAP(CX,CDialog)
 ON_WM_CREATE()
 ON_WM_DESTROY()
 ON_BN_CLICKED(IDC_SENDEN,OnSenden)
END_MESSAGE_MAP()

int CX::OnCreate(LPCREATESTRUCT lpCS)
{
    return 1;
}

void CX::OnDestroy()
{}

void CX::OnSenden() 
{}



Verbinden der grafischen Elemente mit einer Variablen

Diese Funktion im Header vereinbaren:
public:
    CString m_Text;
public:
   virtual void DoDataExchange(CDataExchange* pDX);

Dann in der CPP-Datei:
void CX::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    DDX_Text(pDX, IDC_TEXT, m_Text);
}

Und dann damit arbeiten:
UpdateData(true);
m_Text="Hejo";
UpdateData(false);



MessageBox
    AfxMessageBox("Hallo Welt");



Focus setzen
Soll der Focus gesetzt werden, benötigt man ein Control (Bsp CEdit:)
In der Header:
 
public:
    CEdit m_ControlText;
In der DoDataExchange:
 
void CX::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    DDX_Control(pDX, IDC_EINGABE, m_ControlText);
}
Und in der entsprechenden Funktion:
    m_ControlText.SetFocus();



UpdateData und Nachteile
UpdateData(true) bewirkt die Auffrischung des kompletten Fensters. Normalerweise stört das nicht, aber wenn zum Beispiel in einem Thread oder einer Timerfunktion ein einzelnes Element aktualisiert werden soll, kann es zu dem Phänomen kommen, daß der Cursor in einem anderen Eingabefeld ständig zur ersten Zeile springt, und zwar regelmäßig (echt ätzend).
Stattdessen kann folgendes gemacht werden:
    CString ausgabe
    m_Ausgabe.GetWindowText(ausgabe);
    ...
    m_Ausgabe.SetWindowText("hier");



Kontextmenü erstellen
Einfügen    - Resouce    - Menu

In der MainHeader einfügen:
...
#include "resource.h"
...
class CMain : public CFrameWnd
{
...
protected:
    afx_msg void OnRButtonDown(UINT nFlags,CPoint point);
...
DECLARE_MESSAGE_MAP()
};

Nun in der Main.cpp einfügen:

BEGIN_MESSAGE_MAP(CDumo,CFrameWnd)
    ON_WM_RBUTTONDOWN()
END_MESSAGE_MAP()
...
void CMain::OnRButtonDown(UINT nFlags,CPoint point)
{
    CPoint m_Point = point;
    ClientToScreen(&m_Point);
    CMenu *m_Kontextmenu = new CMenu();
    m_Kontextmenu->LoadMenu(IDR_KONTEXTMENU);
    CMenu *pMenu = m_Kontextmenu->GetSubMenu(0);
    UINT nID=(UINT)pMenu->TrackPopupMenu (TPM_LEFTALIGN|TPM_TOPALIGN|TPM_RETURNCMD|
                                                TPM_LEFTBUTTON,m_Point.x,m_Point.y,this,NULL);
    delete m_Kontextmenu;
    if (nID!=0)
       PostMessage(WM_COMMAND,nID,0);
}



Neue Klasse einbinden
Sie soll keine MFC-Klassen sein.
Einfügen    - neue Klasse    - Klassentyp: allgemeine Klasse    - Klassenname C... eingeben


Tabellenelement
1. Auf Formularfenster ein Listenelement ziehen, Namen geben
2. Bei Eigenschaften->Formate->Ansicht: Bericht
3. Im Klassenassistenten eine Variable vom Typ CListCtrl anlegen
4. In der cpp-Datei einfügen:
BOOL CTableClass::OnInitDialog()
{
  ...
  CRect rect;
  m_ctrlTable.GetClientRect(&rect);
  int nColInterval = rect.Width()/4;

  m_ctrlTable.InsertColumn(0,_T("Spalte 1"),LVCFMT_LEFT,nColInterval*2);
  m_ctrlTable.InsertColumn(1,_T("Spalte 2"),LVCFMT_LEFT,nColInterval);
  m_ctrlTable.InsertColumn(2,_T("Spalte 3"),LVCFMT_LEFT,nColInterval);

  m_ctrlTable.SetSelectionMark(1);

  m_ctrlTable.DeleteAllItems();
  int zeile = 0;
  InsertItem("Zeile 1",zeile);
    InsertColumn(zeile,1,"Hallo");
    InsertColumn(zeile,2,"du da!");
  InsertItem("Zeile 2",zeile);
  InsertItem("Zeile 3",zeile);
  InsertItem("Zeile 4",zeile);
  ...
}

int CTableClass::InsertItem(CString indexername, int &zeile)
{
  zeile = m_ctrlTable.GetItemCount();
  LVITEM lvi;
  lvi.mask = LVIF_TEXT;
  lvi.iItem = zeile;
  lvi.iSubItem = 0;
  lvi.pszText = (LPTSTR)(LPCTSTR)(indexername);
  m_ctrlTable.InsertItem(&lvi);

  //* jetzt die Subitems
  for (int i=1; i<m_ctrlTable.GetHeaderCtrl()->GetItemCount(); i++)
    m_ctrlTable.SetItemText(zeile,i,"---");
  return 0;
}

int CTableClass::InsertColumn(int zeile, int spalte, CString txt)
{
  //* jetzt die Subitems
  m_ctrlTable.SetItemText(zeile,spalte,txt);
  return 0;
}

int CTableClass::GetCellValue(int zeile, int spalte, CString &wert)
{
  TCHAR szBuffer[1024];
  DWORD cchBuf(1024);
  LVITEM lvi;
  lvi.iItem = zeile;
  lvi.iSubItem = spalte;
  lvi.mask = LVIF_TEXT;
  lvi.pszText = szBuffer;
  lvi.cchTextMax = cchBuf;
  m_ctrlTable.GetItem(&lvi); 
  wert = szBuffer;
  return 0;
}

download


Highlighten von Tabellen
1. Tabelle wie oben beschrieben erstellen
2. In der Headerdatei folgendes eintragen (rot):
class COutputTableDlg : public CDialog
{
  ...
  protected:
  // Generierte Nachrichtenzuordnungsfunktionen
  //{{AFX_MSG(COutputTableDlg)
  virtual BOOL OnInitDialog();
  //}}AFX_MSG
  afx_msg void OnCustomdrawMyList ( NMHDR* pNMHDR, LRESULT* pResult );
  DECLARE_MESSAGE_MAP()
};
3. In der cpp-Datei:
BEGIN_MESSAGE_MAP(COutputTableDlg, CDialog)
  //{{AFX_MSG_MAP(COutputTableDlg)
  //}}AFX_MSG_MAP
  ON_NOTIFY ( NM_CUSTOMDRAW, IDC__TABELLE, OnCustomdrawMyList )
END_MESSAGE_MAP()

...

void COutputTableDlg::OnCustomdrawMyList ( NMHDR* pNMHDR, LRESULT* pResult )
{
  NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>( pNMHDR );

  *pResult = CDRF_DODEFAULT;

  if ( CDDS_PREPAINT == pLVCD->nmcd.dwDrawStage )
  {
    *pResult = CDRF_NOTIFYITEMDRAW;
  }
  else if ( CDDS_ITEMPREPAINT == pLVCD->nmcd.dwDrawStage )
  {
    *pResult = CDRF_NOTIFYSUBITEMDRAW;
  }
  else if ( (CDDS_ITEMPREPAINT | CDDS_SUBITEM) == pLVCD->nmcd.dwDrawStage )
  {
    COLORREF clrNewTextColor, clrNewBkColor;
    int  nItem = static_cast<int>( pLVCD->nmcd.dwItemSpec );
    CString strTemp = m_ctrlTable.GetItemText(nItem,pLVCD->iSubItem);

    //  if(strTemp == "Hallo")
    if(nItem==1)
    {
      clrNewTextColor = RGB(255,0,0); // roter Text
      clrNewBkColor = RGB(255,255,0); // Hintergrund gelb
    }
    else
    {
      clrNewTextColor = RGB(0,0,0); //  schwarzer Text
      clrNewBkColor = RGB(255,255,255); // Hintergrund weiß
    }

    pLVCD->clrText = clrNewTextColor;
    pLVCD->clrTextBk = clrNewBkColor;

    *pResult = CDRF_DODEFAULT;
  }
}

4. Mit m_ctrlTable.RedrawWindow(); kann die Tabelle neu gezeichnet werden, falls sich irgendwas ändert.


Fensterschließen verhindern
Dialogfeldbasierende Anwendung:
void CCloseClass::OnClose() 
{
  if (m_bClose==false)
  {
    MessageBox("Das Programm wird nicht beendet!!","Nicht schließen",
                            MB_OK|MB_ICONWARNING);
  }
  else CDialog::OnClose();
}
download
SDI-Anwendung:
//* Application Klasse *****************************************
int CSimulatorApp::SetClose(bool wert)
{
  m_bClose = wert;
  return 0;
}

bool CSimulatorApp::GetClose()
{
  return m_bClose;
}

//* View Klasse **********************************************
void CSimulatorView::OnProgrammStartstop() 
{
  theApp.SetClose(false);
  ...
}

//* Main Klasse **********************************************
void CMainFrame::OnClose() 
{
  if (theApp.GetClose()==false)
  {
    MessageBox("Das Programm wird nicht beendet!!","Nicht schließen",
                           MB_OK|MB_ICONWARNING);
  }
  else CFrameWnd::OnClose();
}



Baumstruktur erstellen
1. Element Struktur auf ein Dialogfeld setzen, Namen geben
2. Eigenschaften:
Formate Besitzt Schaltflächen ja
  Besitzt Linien ja
  Linien am Stamm ja
  Drag and Drop deaktivieren ja
Weitere Formate Bildlauf ja
  Quickinfo  ja
  Track select ja
3. Über Klassenassistenten eine Variable vom Typ CTreeCtrl erstellen
BOOL CStrukturTestDlg::OnInitDialog()
{
  ...
  TV_INSERTSTRUCT tvinsert;
  tvinsert.hParent = NULL;
  tvinsert.hInsertAfter = TVI_LAST;
  tvinsert.item.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_TEXT;
  tvinsert.item.hItem = NULL;
  tvinsert.item.state = 0;
  tvinsert.item.cchTextMax = 6;
  tvinsert.item.cChildren = 0;
  tvinsert.item.lParam = 0;

  //* oberste Ebene
  tvinsert.item.pszText = "Wurzel";
  HTREEITEM hWurzel = m_ctrlBaum.InsertItem(&tvinsert);

  //* zweite Ebene
  tvinsert.hParent = hWurzel;
  tvinsert.item.pszText = "Unterpunkt 1";
  HTREEITEM hUp1 = m_ctrlBaum.InsertItem(&tvinsert);
  tvinsert.hParent = hWurzel;
  tvinsert.item.pszText = "Unterpunkt 2";
  m_ctrlBaum.InsertItem(&tvinsert);

  //* dritte Ebene
  tvinsert.hParent = hUp1;
  tvinsert.item.pszText = "Noch ein Unterpunkt";
  m_ctrlBaum.InsertItem(&tvinsert);
  ... 
}

download

Um dynamisch per rechten Mausklick mit Kontextmenü etwas hinzufügen zu können, muß eine eigene Kindklasse von TreeCtrl geschaffen werden.
Das Wichtigste ganz kurz:

class CBaum: public CTreeCtrl
{
public:
  HTREEITEM m_hSelected;
  ...
protected:
  CMyDlg *m_pParent;
  ...
}
int CBaum::SetParent(CMyDlg *parent)
{
  m_pParent = parent;
  return 0;
}

void CBaum::OnRButtonDown(UINT nFlags, CPoint point)
{
  m_hSelected = HitTest(point,&nFlags);
  SelectedItem(m_hSelected);

  if (m_hSelected!=NULL)
  {
    SelectedItem(m_hSelected);
    //* Das Kontextmenü vom Originaldialogfenster wird aufgerufen!
    m_pParent->OnRightMouseDown(m_Selected,point);
  }

  CTreeCtrl::OnRButtonDown(nFlags, point);
}

P.S.: Aber irgendwie ist das nicht ganz sauber, spätestens nach dem 3. Hinzufügen von Elementen, hängt Windows sich auf ...


Docking Window
1. Download der Klassen
2. Folgende Veränderungen vornehmen:
 
a) MDI-Projekt erstellen
b) Downloaded Klassen ins Projekt mit einfügen
c) Neue Klasse (CMyBar) erstellen, die von SizingControlBar public erbt.
d) In Klasse CMainFrame eine protected Variable von CMyBar (m_wndMyBar) erstellen
e) Nun in Create() von CMainFrame eintragen:

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
  ...
  //* Testen, ob meine eigene Toolbar kreierbar ist ...
  if (!m_wndMyBar.Create(_T("My Bar"), this, CSize(80, 80),
        TRUE, 123))
  {
      TRACE0("Erstellen von MyBar fehlgeschlagen\n");
      return -1;      // erstellen fehlgeschlagen
  }

  //* Eigene Toolbar erstellen
  m_wndMyBar.SetBarStyle(m_wndMyBar.GetBarStyle() |
        CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);

  //* Docking erlauben!
  m_wndMyBar.EnableDocking(CBRS_ALIGN_ANY);
  DockControlBar(&m_wndMyBar, AFX_IDW_DOCKBAR_LEFT);

  //* Alten Status aus der Registry laden (z. B. Wo Leiste das letzte Mal hingeschoben wurde)
  m_wndMyBar.LoadState(_T("BarState1"));
  LoadBarState(_T("BarState1"));
  ...
  return 0;
}

f) Virtuelle Funktion OnDestroy() hinzufügen:

BOOL CMainFrame::DestroyWindow() 
{
  //* Status wird in die Registry geschrieben
  m_wndMyBar.SaveState(_T("BarState1"));
  SaveBarState(_T("BarState1"));

  return CFrameWnd::DestroyWindow();
}

g) Jetzt vielleicht noch im Menü einen Punkt angeben, ob dieses Fenster angezeigt werden soll oder nicht:

void CMainFrame::OnAnsichtMybar() 
{
  //* MyBar anzeigen oder eben nicht
  BOOL bShow = m_wndMyBar.IsVisible();
  ShowControlBar(&m_wndMyBar, !bShow, FALSE);
}

h) Dann noch den Menüpunkt, je nach Zustand checken:

void CMainFrame::OnUpdateAnsichtMybar(CCmdUI* pCmdUI) 
{
  //* Jetzt Menüpunkt anklicken, je nachdem, ob angezeigt oder nicht
  pCmdUI->Enable();
  pCmdUI->SetCheck(m_wndMyBar.IsVisible());
}



Funktionalität in ein Docking Window bringen
1. Neuen Dialog hinzufügen und ordentlich benamsen
2.



MDI-Anwendung ohne ein erstes Fenster
Unterstrichenes hinzufügen:
BOOL CTestApp::InitInstance()
{
  ...
  // Befehlszeile parsen, um zu prüfen auf Standard-Umgebungsbefehle DDE, Datei offen
  CCommandLineInfo cmdInfo;
  ParseCommandLine(cmdInfo);
  if (cmdInfo.m_nShellCommand==CCommandLineInfo::FileNew)
       cmdInfo.m_nShellCommand=CCommandLineInfo::FileNothing;
  ...
}


 
 
 
 
 



Beispiel für das Schreiben in eine Datei
 
HANDLE hFile;
hFile = CreateFile("datei.txt", GENERIC_WRITE,0,NULL,OPEN_ALWAYS,
                             FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
    TRACE("Datei konnte nicht geöffnet werden");
}
char   buff[225] = "Hey du Scherznase!";
DWORD  bytes;
WriteFile(hFile, buff, sizeof(buff), &bytes, NULL);
CloseHandle(hFile);
// DeleteFile("datei.txt");



Beispiel für das Lesen aus einer Datei
 
HANDLE hFile;
hFile = CreateFile("datei.txt", GENERIC_READ, 0,NULL,OPEN_ALWAYS,
                              FILE_ATTRIBUTE_NORMAL,NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
    TRACE("Datei konnte nicht geöffnet werden");
}
char   buff[225] = "";
DWORD  bytes;
ReadFile(hFile, buff, 700, &bytes, NULL);
CloseHandle(hFile);
// DeleteFile("datei.txt");



Auf die Serielle Schnittstelle schreiben
 
DCB dcb;
HANDLE hCom2;
BOOL fSuccess;
hCom2 = CreateFile("COM2", GENERIC_WRITE,0, NULL, OPEN_EXISTING, 0,NULL); 
if (hCom2 == INVALID_HANDLE_VALUE)
{
    TRACE("Fehler!");
}
fSuccess = GetCommState(hCom2, &dcb);
 if (!fSuccess) 
{
    TRACE("GetCommState macht Ärger");
}
dcb.BaudRate = CBR_2400;
dcb.ByteSize = 8; 
dcb.Parity = NOPARITY; 
dcb.StopBits = ONESTOPBIT;
fSuccess = SetCommState(hCom2, &dcb);
if (!fSuccess) 
{
    TRACE("SetCommState macht Ärger");
}
char   buff[225] = "Hey Pappnase!\n";
DWORD  bytes;
WriteFile(hCom2, buff, sizeof(buff), &bytes, NULL);
CloseHandle(hCom2);


Serielle Schnittstelle: Read- und Write- Funktionen

Readfunktion. Das Handle auf den Port wurde bereits in der Open-Funktion initialisiert.
 

//* Die gelesenen Daten werden in txt pr Referenz übergeben
//* return  0 - etwas gelesen, 1 - nix gelesen
int CComPort::ReadPort(CString &txt)
{
  DWORD bytes;
  ReadFile(m_hPort,txt.GetBufferSetLength(256),256,&bytes,NULL);
  txt.ReleaseBuffer(bytes);
  if (bytes==0) return 1;
  int pos=txt.Find((TCHAR)-52);
  if (pos>=0) txt=txt.Left(pos);
  return 0; 
}

Writefunktion. Das Handle auf den Port wurde bereits in der Open-Funktion initialisiert.
 

//* Der zu schreibende Text wird in txt übergeben
int CComPort::WritePort(CString txt)
{
  DWORD bytes;
  WriteFile(m_hPort,txt,txt.GetLength(),&bytes,NULL);
  return 0;
}


Timer
Einen Timer kann man setzen, wenn irgendwas immer wieder passieren soll, zum Beispiel wenn alle 10 ms geschaut werden soll, ob sich etwas an der Seriellen Schnittstelle getan hat.
Mit
    SetTimer(1,100,NULL);
wird ein Timer gesetzt. 1 ist die Nummer des Timers, man hat nämlich nur 16 zur Verfügung, 100 ist die Zeit in Millisekunden und NULL bedeutet, daß die Funktion OnTimer (ON_WM_TIMER) aufgerufen wird. Man kann auch alternativ eine Funktion angeben.
Und mit
    KillTimer(1);
wird der Timer wieder gelöscht.



Threads
Hier wird der Thread einmal im Konstruktor erzeugt und dann im Destruktor wieder gelöscht.
in der Headerdatei:
class CSteuerung 
{
public:
    void Threadfunktion();
    static UINT Threadfunktion(LPVOID pParam);
    ...
    CWinThread* m_pThread;
    enum { NOTHING, START, STOP, EXIT_THREAD } m_iThreadCommand;
    enum { EXITED, RUNNING, IDLE } m_iThreadStatus;
};

in der C++ Datei:

CSteuerung::CSteuerung()
{
    m_iThreadCommand = NOTHING;
    m_iThreadStatus = EXITED;
    m_pThread=AfxBeginThread(Threadfunktion,this,0,0,CREATE_SUSPENDED,0);
    m_pThread->m_bAutoDelete=FALSE;
    m_pThread->ResumeThread();
}

CSteuerung::~CSteuerung()
{
    if (m_pThread)
    {
        for (;;)
       {
           DWORD dwExitCode;
           m_iThreadCommand = EXIT_THREAD;
           if (!GetExitCodeThread(m_pThread->m_hThread,&dwExitCode) || dwExitCode!=STILL_ACTIVE)
          {  break;  }
          Sleep(10);
       }
       delete m_pThread;
    }
}

int CSteuerung::StartMove()
{
    m_iThreadCommand = START;
    for (int i=0; i<100 && m_iThreadStatus!=RUNNING; ++i) Sleep(100);
    if (m_iThreadStatus!=RUNNING)
   {  // Timeout erkannt  }
    return 0;
}

UINT CSteuerung::Threadfunktion(LPVOID pParam)
{
    CSteuerung *steuerung = (CSteuerung*) pParam;
    steuerung->Threadfunktion();
    return 0;
}

void CSteuerung::Threadfunktion()
{
    m_iThreadStatus = IDLE;
    for (;;)
   {
       switch (m_iThreadCommand)
      {
      case EXIT_THREAD:
           goto exit_thread;
      case START:
              m_iThreadStatus = RUNNING;
              //Start();
              m_iThreadStatus = IDLE;
              m_iThreadCommand = NOTHING;
           break;
      case STOP:
              m_iThreadStatus = RUNNING;
              //Stop();
              m_iThreadStatus = IDLE;
              m_iThreadCommand = NOTHING;
           break;
      case NOTHING:
              //TesteMotor();
           break;
      }
      Sleep(10);
    } // Ende for (;;)
    exit_thread:
    m_iThreadStatus = EXITED;
    m_iThreadCommand = NOTHING;
}



Buchstaben im ASCII-Code ausgeben
 
  CString help;
  CString txt = "Hallo du da";
  for (int i=0; i<txt.GetLength(); i++)
  {
    CString bu;
    bu.Format("%i ",txt[i]);
    help = help + bu;
  }
  OutputDebugString(help);


Debugausgaben
1. Möglichkeit: TRACE("bla "+szOutput+" hier\n");
Das klappt aber leider nur im Debug-Modus
2. Möglichkeit: OutputDebugString("hier"); bzw. OutputDebugString(szOutput);
Das klappt immer. Bei der Release-Version werden die Ausgaben auf den offenen Debugger ausgegeben, z.B. das Visual Studio. Ist keiner vorhanden, geht die Ausgabe ins Nirwana.


Pfad des gestarteten Programmes ermitteln
 
  TCHAR path[_MAX_PATH];
  ::GetModuleFileName(NULL, path, _MAX_PATH);
  LPTSTR pszFileName = _tcsrchr(path, '\\') + 1;
  *pszFileName = '\0'; 
  CString m_szProgramPath = path;


Lokal- und Systemzeit
 
  SYSTEMTIME realtime;
  if (m_bLocalTime) GetLocalTime(&realtime);
  else GetSystemTime(&realtime);

  CString time;
  time.Format("%s, %02d.%02d.%04d %02d:%02d:%02d",wochentag,realtime.wDay,
              realtime.wMonth,realtime.wYear,realtime.wHour,realtime.wMinute,
              realtime.wSecond);



CString in char* konvertieren
Benötigt wird ein char*, mit Platz für 100 Zeichen, auf den schreibend zugegriffen werden soll:
 
    CString csText;
    LPTSTR  szText= csText.GetBuffer (100);
    _tcscpy (szText, _T("Hallo Welt!"));
    csText.ReleaseBuffer();


Verzeichnis auslesen
1. Möglichkeit
 
  WIN32_FIND_DATA w32fd;
  HANDLE hFind= FindFirstFile("C:\\*.*", &w32fd);
  if (hFind == INVALID_HANDLE_VALUE) return;
  do
  {
    cout << w32fd.cFileName << endl;
  }
  while (FindNextFile(hFind, &w32fd));
  FindClose(hFind);

2. Möglichkeit (MFC)
 

  CFileFind finder;
  BOOL bWorking = finder.FindFile("*.*");
  while (bWorking) 
  {
    bWorking = finder.FindNextFile();
    if (!finder.IsDirectory())
        cout << (LPCTSTR) finder.GetFileName() << endl;
  }


Verhindern, dass ein Programm mehrmals gestartet wird
Folgendes in die Funktion InitInstance() der Applikation schreiben:
 
BOOL CProgrammApp::InitInstance()
{
  ...
  HANDLE hMutex =  CreateMutex   (NULL, TRUE, "unverwechselbarer String");
  bool found=false;
  if (GetLastError() == ERROR_ALREADY_EXISTS) found = true;
  if (hMutex) ReleaseMutex (hMutex);
  if (found)
  {
      AfxMessageBox("Das Programm wurde bereits gestartet");
      return FALSE;
  }

  CProgrammDlg dlg;
  m_pMainWnd = &dlg;
  int nResponse = dlg.DoModal();
  ...
 }



Mit der Registry arbeiten
 
//* Funktion, um ein Datum und einen Wert in die Registry einzutragen
bool CTest::WriteRegistry(LPCTSTR szName, DWORD dwValue)
{
  HKEY hKey=NULL;                   //* Handle für eine Registrierung
  if (RegCreateKeyEx(HKEY_CURRENT_USER,          //* Verzeichnis in der Registry
                                    _T("Software\\Katrin\\Test"),      //* Schlüsseldatei
                                    0,                                                //* muß immer 0 sein!
                                   _T(""),                                       //* String der die Klasse klassifieziert?!
                                   REG_OPTION_NON_VOLATILE,     //* Die Angaben sollen nicht flüchtig
                                  KEY_WRITE,                               //* Zugriff
                                  NULL,                                       //* Handle kann nicht vererbt werden
                                  &hKey,                                      //* Handle, in das geschrieben werden soll,
                                 0                                                   //* Keine Disposition zurück
                                 )==ERROR_SUCCESS)
  {
    //* Jetzt einen Datum und ein spezifizierter Wert schreiben
    //* Wenn schreiben erfolgreich, dann wird ERROR_SUCCESS zurückgeliefert
    bool bResult=(RegSetValueEx(hKey,                              //* Handle auf die Registry
                                                     szName,                         //* Übergebenes Datum
                                                    0,                                      //* Muß 0 sein!
                                                   REG_DWORD_LITTLE_ENDIAN,     //* Typ
                                                  (LPBYTE)&dwValue,       //* Übergebener Wert
                                                 sizeof(dwValue)              //* Größe des Wertes
                                                  )==ERROR_SUCCESS);
    RegCloseKey(hKey);     //* Handle wieder schließen
    return bResult;
  }
  return false;
}

//* Funktion, um ein Datum und einen Wert aus der Registry auszulesen
bool CTest::ReadRegistry(LPCTSTR szName, DWORD &dwValue)
{
  HKEY hKey=NULL;   //* Handle auf einen Registerschlüssel
  //* Funktion öffnet einen spezifizierten Registerschlüssel
  if (RegOpenKeyEx(HKEY_CURRENT_USER,               //* Verzeichnis in der Registry
                                _T("SOFTWARE\\Katrin\\Test\\"),   //* Schlüsselverzeichnis
                                0,                                                      //* Muß 0 sein
                                KEY_READ,                                    //* Zugriffsart
                                &hKey                                           //* Handle übergeben
                               )==ERROR_SUCCESS)
  {
    DWORD dwType = 0;
    DWORD dwValTemp = 0;
    DWORD dwSize = sizeof(int); //* Maximale Größe eines Integers
    //*Funktion liest einen Wert eines Datums
    bool bResult = (RegQueryValueEx( hKey,    //* Handle
                                                            szName,  //* Names des Wertes
                                                           NULL,    //* Muß 0 sein
                                                           &dwType, //* Hier wird der Typ des Werts gespeichert
                                                           (LPBYTE)&dwValTemp, //* Hier wird der Wert gespeichert
                                                          &dwSize            //* Größe des zulesenden Wertes
                                                          )==ERROR_SUCCESS);
    RegCloseKey(hKey);    //* Handle schließen
    if (bResult)
    {
      if (dwSize==sizeof(dwValTemp)) //* gleiche Größe?
      {
          //* Jetzt das Speicherformat überprüfen:
          switch (dwType)
         {
         case REG_BINARY:
         case REG_DWORD_LITTLE_ENDIAN:
                 dwValue = dwValTemp; //* Alles ok, Gelesene übernehmen!
                return true;
        case REG_DWORD_BIG_ENDIAN:
                //* Oha, falsches Format, erst mal hin und her schieben!!
               dwValue = ((dwValTemp&0x000000FF)<<24)|((dwValTemp&0x0000FF00)<<8)|
                                 ((dwValTemp&0x00FF0000)>>8)|((dwValTemp&0xFF000000)>>24);
             return true;
        }
      }
    }
  }
  return false;
}

Der Aufruf erfolgt dann etwa so:
    WriteRegistry("hallo",1234);
    DWORD dwValue;
    ReadRegistry("hallo",dwValue);