Na wenn das Programm schon fertig ist .... ;)
Den Empfang ("ReadFile") legt man in einen eigenen Thread, z.B. so:
(hier fehlt noch die Einstellung aller COM-Parameter!)
HANDLE hCom = INVALID_HANDLE_VALUE;
//--------------------------------------------------------------------
// COMThread
//
// erstellt eine Queue, setzt danach den Event ("Queue ist ready").
// Schließt eine geöffneten COM-Port und öffnet den neuen (Nummer ist
// in der Strukur, auf die lpParam zeigt).
// liest die queue aus.
//
// Thread Funktion
// Liest eventgesteuert den Status von CTS, DSR, ... ein und
// sendet in dwEvtMask alle zum Event gehörenden Ereignisse
// zum Monitor-Thread.
//
//--------------------------------------------------------------------
DWORD WINAPI ComThread (LPVOID lpParam)
{
long lTime;
volatile PPARAMS pparams; // struct-Instanz
pparams = (PPARAMS) lpParam; // Zuweisung zu lpParam
HWND hWndEdit = pparams->hWndEdit; // Parameter auslesen
int iPortNum = pparams->iComPortNum; // ...
DWORD dwMonitorThreadId = pparams->dwMonitorThreadId; // ...
char szMsg[80];
char szPort[15];
static OVERLAPPED o;
// Maske für SetCommMask, die bestimmt, welche Ereignisse auftreten
können
DWORD dwEvtMaskIn = EV_CTS | EV_DSR | EV_BREAK | EV_RING | EV_RXCHAR |
EV_RLSD | EV_ERR | EV_RXFLAG | EV_TXEMPTY;
DWORD dwEvtMask = 0; // Maske, in die WaitCommEvent aktuelle Werte
schreibt
BOOL bRet;
CloseHandle (hCom); // "alten" COM-Port schließen
// Com Port öffnen
wsprintf (szPort, "COM%d", iPortNum);
hCom = CreateFile (szPort,
GENERIC_READ | GENERIC_WRITE,
0, // exclusive access
NULL, // no security attributes
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL);
if (hCom == INVALID_HANDLE_VALUE)
{
wsprintf (szMsg, "\r\nComThread: COM%d kann nicht geöffnet werden",
iPortNum);
AddNewText (hWndEdit, szMsg);
pparams->ThreadStoppen = TRUE;
}
else
{
if (!SetCommMask (hCom, dwEvtMaskIn))
AddNewText (hWndEdit, "\r\nComThread: SetCommMask
fehlgeschlagen");
// Create an event object for use in WaitCommEvent.
o.hEvent = CreateEvent (NULL, // no security attributes
FALSE, // auto reset event
FALSE, // not signaled
NULL); // no name
}
DWORD dwProcessId; // own process identifier
HANDLE hProcess; // handle to the own process
DWORD dwDesiredAccess = PROCESS_SET_INFORMATION; // access flag
BOOL bInheritHandle = TRUE; // handle inheritance flag
BOOL bReturn; // If the SetPriorityClass succeeds, then
nonzero
//WORD dwPriorityClass = REALTIME_PRIORITY_CLASS; // priority class
value
DWORD dwPriorityClass = HIGH_PRIORITY_CLASS;
// Setzt die Priorität "RealTime" für das Programm
dwProcessId = GetCurrentProcessId ();
hProcess = OpenProcess (dwDesiredAccess, bInheritHandle, dwProcessId);
bReturn = SetPriorityClass (hProcess, dwPriorityClass);
while (!pparams->ThreadStoppen) // solange weitermachen bis TRUE
{
WaitCommEvent (hCom, &dwEvtMask, &o); // EventMask "scharf machen"
// kommt der Event, ist auch die dwEvtMask geladen und es kann
weitergehen
if (WAIT_OBJECT_0 == WaitForSingleObject (o.hEvent, INFINITE)) //
warten bis Event
{
lTime = queryPrecisionTime (); // Differenzzeit zum letzten
Aufruf holen
// Message senden. In dwEvtMask können mehrere Ereignisse gesetzt
sein
bRet = PostThreadMessage (dwMonitorThreadId, (QS_ALLPOSTMESSAGE +
MSG_TIME),
(WPARAM)dwEvtMask , (LPARAM)lTime);
} // end of: if (WAIT_OBJECT_0 == ...
} // end of: while (...
CloseHandle (o.hEvent);
CloseHandle (hCom); // COM-Port schließen
return (0);
}
Dieser Thread wird aufgerufen und beendet, wenn man in der
Callback-Routine der WinMain folgenden Eintrag hat:
(hier sind Menü-Punkte, die angeklickt werden, die "Auslöser")
//######################################################################
case WM_COMMAND:
switch (LOWORD(wParam))
{
//######################################################################
case IDM_COMPORT1:
// Erstes Statusfeld beschreiben:
WriteStatusBarField (sBar, 0, "COM1 geöffnet");
CheckMenuItem (hMenu, IDM_COMPORT2, MF_UNCHECKED);
CheckMenuItem (hMenu, IDM_COMPORT1, MF_CHECKED);
// "alten" Thread stoppen
p.ThreadStoppen = TRUE;
CloseHandle (hComThread); // "alten" Thread stoppen
p.iComPortNum = 1;
// ComThread: Starten und Parameterstruct übergeben
// ThreadID wird nicht benötigt
p.ThreadStoppen = FALSE;
hComThread = CreateThread ( // Handle des Threads
NULL, // no security attributes
0, // use default stack size
ComThread, // thread function
&p, // argument to thread function
0, // use default creation flags
&dwComThreadId); // returns the thread identifier
if (hComThread == NULL)
AddNewText (hEdit, "\r\nCreateThread (ComThread)
fehlgeschlagen");
break;
//######################################################################
case IDM_COMPORT2:
// Erstes Statusfeld beschreiben:
WriteStatusBarField (sBar, 0, "COM2 geöffnet");
CheckMenuItem (hMenu, IDM_COMPORT1, MF_UNCHECKED);
CheckMenuItem (hMenu, IDM_COMPORT2, MF_CHECKED);
// "alten" Thread stoppen
p.ThreadStoppen = TRUE;
CloseHandle (hComThread); // "alten" Thread stoppen
p.iComPortNum = 2;
// ComThread: Starten und Parameterstruct übergeben
// ThreadID wird nicht benötigt
p.ThreadStoppen = FALSE;
hComThread = CreateThread ( // Handle des Threads
NULL, // no security attributes
0, // use default stack size
ComThread, // thread function
&p, // argument to thread function
0, // use default creation flags
&dwComThreadId); // returns the thread identifier
if (hComThread == NULL)
AddNewText (hEdit, "\r\nCreateThread (ComThread)
fehlgeschlagen");
break;
Da die Message-Queue der WinMain etwas langsam ist und dort auch alle
anderen Mesages auflaufen, habe ich die alle Events des ConThreads zu
einem "MonitorThread" genannten Thread geschickt. Dort kann je nach
Event dann das ReadFile stehen, die empfangenen Daten Komplettiert und
per SendMessage an die Callback-Routine des WinMain gesendet werden.
Oder man schreibt gleich in das Editfenster (habe ich hier mit
"AddNewText" gemacht).
Der Monitorthread wird im WM_CREATE gestartet:
...
// MonitorThread:
// Erstellt den Event für die Thread Queue
hEvent = CreateEvent (NULL, false, true, QEVENT);
// Thread starten und Editbox-Handle hEdit übergeben
// uMonThreadID für ComThread
hMonThread = CreateThread ( // Handle des Threads
NULL, // no security attributes
0, // use default stack size
MonitorThread, // thread function
&p, // argument to thread function
0, // use default creation flags
&dwMonThreadId); // returns the thread identifier
if (hMonThread == NULL)
AddNewText (hEdit, "\r\nCreateThread (MonitorThread)
fehlgeschlagen");
p.dwMonitorThreadId = dwMonThreadId; // merken, für ComThread
// wait for queue completition
dwState = WaitForSingleObject (hEvent, 100);
CloseHandle (hEvent);
...
Schreiben mit WriteFile kann man von überall her, nur sollte man vorher
testen, ob der COM-Port auch offen ist.
Das ist nuer eine von vielen Varianten, wie man über COM-Ports mit C
(und API) unter Windows(-GUI) schreiben und lesen kann.
Die Code-Schnipsel stammen aus einem Projekt, das nur die Events am
COM-Port erkennt und loggt. Wie ein langsamer Datenlogger oder Logic
Analyzer. Deshalb sind die Baudrate und die Timeouts hier nicht nötig.
Es sind auch ein paar Dinge drin (z.B.: Zeitstempel) die für eine
Terminalfunktion nicht gebraucht werden.
Fehlerbehandlung sollte auch sein. Das Schreiben kann auch in einem
eigenen Thread erfolgen, da man ja nie genau weiß, wann denn die Daten
nun genau weg sind. Und eine Message-Queue aa einem solchen
"WriteThread" dient dann als Puffer, wenn man viel in kurzer Zeit senden
muß.
Blackbird