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.