Hallo, ich suche einen Sample Code für ein GUI App das über einen Thread Daten von der rs232 Schnittstelle empfangen und senden kann und das auch noch in C für Windows. Ich habe hier schon einiges gefunden aber eben nur Anwendungen für Konsolen Programme ohne Thread (Samples von Blackbird z.B.). In der Vergangenheit scheinen sich ja auch andere damit beschäftigt zu habe ev. hat jemand so ein App schon fertig gestellt. Das würde mir sehr helfen. Gruß Milbrot
Die "Samples" habe ich aus meinen GUI-Applikationen "extrahiert", damit sie übersichtlich bleiben. Denn Windows GUI-Apps sind etwas umfangreicher als eine Seite (z.B. 50 Zeilen). Und das Aussehen und die Funktion bestimmen auch den Code - ein allgemeingültiges Template gibt es nicht. MS hat das mit der MFC versucht. Ist aber nicht so richtig zum Standard geworden (zu recht, wie ich finde). Wenn die seriellen Daten ASCII-Zeichen sind, kann man die empfangenen Zeichen gut in einem Edit-Fenster darstellen. Im gleichen oder einem zweiten Edit-Fenster kann man seine Daten schreiben, die dann per <ENTER> oder Knopfdruck gesendet werden. Dazu gehören noch ein paar Menüs, die das Speichern, Drucken, Beenden, etc. zur Wahl stellen. Öffnen, Schließen und Eigenschaften der seriellen Schnittstelle müssen auch irgendwo, z.B. via Menü eingestellt werden können. Und eine Statusleiste oder ein Status-Feld sollte über den jeweiligen Zustand der seriellen Schnittstelle Auskunft geben. Üblich ist auch ein Info-Fenster und eine kleine Hilfe. Das war jetzt nur die Oberfläche, die Thread-Aufteilung im Hintergrund ist ein eigenes Thema. Ebenso die Frage, ob und wo das Programm sich was merken soll. Das alles nicht in einem File, sondern schön funktionell getrennt. Und Resourcen (z.B.: Icons, Menüs, etc.) gibt es auch noch. Ach, den Installer/Deleter hab' ich noch vergessen. Gehört zwar nicht unmittelbar zum Programm, könnte aber auch nützlich sein. Das Ganze nennt sich dann Terminal und die gibt es massenweise. Oder es soll ein an ein Projekt (z.B.: µC) angepaßtes Programm werden mit einigen Sonderfunktion, die kein Terminalprogramm bietet? Dafür gibt es aber kein "Template". Wenn Du also das "Design" Deiner GUI hast, dann kannst Du auch die einzelnen Teile "bauen". Im Anhang ein kleines Windowsprogramm, hier ohne Menü. Hat eine Editbox, einen Timer und eine Statusleiste. Blackbird
Das Programm ist ja eigentlich schon fertig verwendet aber derzeit eine Delphi DLL die für das senden und empfangen zuständig ist. Bei dem Empfänger handelt es sich um einen einem Dimmer mit AVR. Irgendwie ist mir das aber zu unflexibel und daher würde ich diesen Teil lieber ganz in mein Programm integrieren. Verwundert hat mich das ich bei der Suche nur auf Fragen zu dem Thema gestoßen bin aber eigentlich kein fertiges Projekt gefunden habe wo jemand so etwas schon mal realisiert hat. Projekte und Code für C++ gibt es dagegen anscheinend ausreichend. Wie dem auch sei beim durchsehen der gefundenen Code Schnipsel habe ich mir nun ein Konzept überlegt wie ich das am besten in mein Programm einbauen könnte. Mal sehen ob es auch so laufen wird wie gedacht. Milbrot
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
hi, such mal nach (mit z.b. google) 'mttty' (mit 3 't' - multi threaded tty) das ist ein terminal-programm (in c geschrieben, source code ist dabei) von m$ gweissermassen als beispiel, wie man die rs232 einer windows-box ansteuert und dabei 'overlapped i/o' benutzt. ach, hier ist es: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnfiles/html/msdn_serial.asp
Ein fertiges Programm sehr gut. Was spricht eigentlich dagegen einen Thread nicht mit CreateThread zu erzeugen sondern _beginthread zu verwenden? Alle Samples die ich gesehen habe verwenden CreateThread. Milbrot
Frag einfach Bill ;-) http://msdn2.microsoft.com/en-us/library/7t9ha0zh(VS.80).aspx "_beginthread and _beginthreadex are similar to the CreateThread function in the Win32 API but has these differences: * _beginthread and _beginthreadex let you pass multiple arguments to the thread. * They initialize certain C run-time library variables. This is important only if you use the C run-time library in your threads. * CreateThread helps provide control over security attributes. You can use this function to start a thread in a suspended state."
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.