eigene Kompo (Comm-Port) Absturz bei lesen im Thread
-
Ich poste als erstes mal den kompletten Code (Problembeschreibung am Ende):
//--------------------------------------------------------------------------- #ifndef CommH #define CommH //--------------------------------------------------------------------------- #include <SysUtils.hpp> #include <Classes.hpp> #include <ExtCtrls.hpp> #include "EventModeThread.h" //--------------------------------------------------------------------------- //enum nBaudRate {BR_110, BR_300, BR_600, BR_1200}; enum nByteSize {Bits4, Bits5, Bits6, Bits7, Bits8}; enum nStopBits {OneStopBit, OneAndHalfStopBits, TwoStopBit}; enum nParity {NoParity, OddParity, EvenParity, MarkParity, SpaceParity}; enum nRtsControl {RtsDisable, RtsEnable, RtsHandshake, RtsToggle}; enum nDtrControl {DtrDisable, DtrEnable, DtrHandshake}; //--------------------------------------------------------------------------- class PACKAGE CommSet : public TPersistent { friend class TComm; __published: __property DWORD BaudRate = {read=GetBaudRate, write=SetBaudRate}; __property nDtrControl fDtrControl = {read=GetfDtrControl, write=SetfDtrControl}; __property nRtsControl fRtsControl = {read=GetfRtsControl, write=SetfRtsControl}; __property nParity Parity = {read=GetParity, write=SetParity}; __property nStopBits StopBits = {read=GetStopBits, write=SetStopBits}; __property nByteSize ByteSize = {read=GetByteSize, write=SetByteSize}; private: DCB FDCB; DWORD F_BaudRate; //Current Baud Rate nDtrControl F_fDtrControl; //Data-Terminal-Ready Flow Control nRtsControl F_fRtsControl; //Request-To-Send Flow Control nParity F_Parity; //Specifies the Parity Scheme nStopBits F_StopBits; //Number of Stop Bits nByteSize F_ByteSize; //Number of Bits in Bytes transmitted and received public: __fastcall CommSet() { BaudRate = 9600; fDtrControl = DtrHandshake; fRtsControl = RtsHandshake; Parity = EvenParity; StopBits = OneStopBit; ByteSize = Bits8; //DCB-Strukutr initialisieren ZeroMemory(&FDCB, sizeof(FDCB)); FDCB.DCBlength = sizeof(FDCB); FDCB.BaudRate = F_BaudRate; //Verbindungsgeschwindigkeit FDCB.fBinary = 1; //immer 1, da in Win32 nur BinärMode möglich FDCB.fParity = 1; //Parityüberprüfung und Errorlogng einschalten (1) FDCB.fOutxCtsFlow = 1; //Output Flow Control für die CTS Leitung FDCB.fOutxDsrFlow = 1; //Output Flow Control für die DSR Leitung FDCB.fDtrControl = F_fDtrControl; //DTR Flow Control FDCB.fDsrSensitivity = 1; FDCB.fTXContinueOnXoff = 1; FDCB.fOutX = 1; FDCB.fInX = 1; FDCB.fErrorChar = 1; FDCB.fNull = 1; FDCB.fRtsControl = F_fRtsControl; FDCB.fAbortOnError = 1; FDCB.fDummy2 = 17; FDCB.wReserved = 0; FDCB.XonLim = 0; FDCB.XoffLim = 0; FDCB.ByteSize = F_ByteSize; FDCB.Parity = F_Parity; FDCB.StopBits = F_StopBits; FDCB.XonChar = 0; FDCB.XoffChar = 0; FDCB.ErrorChar = 0; FDCB.EofChar = 0; FDCB.EvtChar = 0; FDCB.wReserved1 = 0; } __fastcall ~CommSet(){} //__property DCB internDCB = {read=FDCB, write=FDCB}; protected: DWORD __fastcall GetBaudRate() {return F_BaudRate;} void __fastcall SetBaudRate(DWORD br) {F_BaudRate = br; FDCB.BaudRate = br;} nDtrControl __fastcall GetfDtrControl() {return F_fDtrControl;} void __fastcall SetfDtrControl(nDtrControl br){F_fDtrControl = br; FDCB.fDtrControl = br;} nRtsControl __fastcall GetfRtsControl() {return F_fRtsControl;} void __fastcall SetfRtsControl(nRtsControl br){F_fRtsControl = br; FDCB.fRtsControl = br;} nParity __fastcall GetParity() {return F_Parity;} void __fastcall SetParity(nParity br) {F_Parity = br; FDCB.Parity = br;} nStopBits __fastcall GetStopBits() {return F_StopBits;} void __fastcall SetStopBits(nStopBits br) {F_StopBits = br; FDCB.StopBits = br;} nByteSize __fastcall GetByteSize() {return F_ByteSize;} void __fastcall SetByteSize(nByteSize br) {F_ByteSize = br; FDCB.ByteSize = br+4;} }; //--------------------------------------------------------------------------- enum nRunModus {PollingMode, EventMode}; enum nPort {Com1, Com2, Com3, Com4, Com5, Com6, Com7, Com8}; //--------------------------------------------------------------------------- typedef void __fastcall (__closure *MyDataRead) (String Daten, bool &doDelete); typedef void __fastcall (__closure *MyBeforeOpen)(DCB &dcb); //--------------------------------------------------------------------------- class PACKAGE TComm : public TComponent { friend class TSerThread; __published: __property CommSet *CommDCB = {read=FDCB, write=FDCB}; __property nRunModus Mode = {read=F_Modus, write=F_Modus}; __property DWORD BufferInput = {read=F_InputBuffer, write=F_InputBuffer}; __property DWORD BufferOutput = {read=F_OutputBuffer, write=F_OutputBuffer}; __property nPort CommPort = {read=GetCommPort, write=SetCommPort}; __property DWORD PollingTime = {read=F_PollingTimeout, write=F_PollingTimeout}; //------------------------- __property MyDataRead OnDataRead = {read=F_MyDataRead, write=F_MyDataRead}; __property MyBeforeOpen BeforeOpen = {read=F_MyBeforeOpen, write=F_MyBeforeOpen}; private: CommSet *FDCB; nRunModus F_Modus; DWORD F_InputBuffer; DWORD F_OutputBuffer; nPort F_Port; DWORD F_PollingTimeout; //------------------------ HANDLE cID; bool F_Connected; String DatenBuffer; //------------------------ TTimer* PollingModeTimer; TSerThread *EventModeThread; //------------------------------------ MyDataRead F_MyDataRead; MyBeforeOpen F_MyBeforeOpen; public: __fastcall TComm(TComponent* Owner); __fastcall ~TComm(); bool __fastcall Connect(); bool __fastcall DisConnect(); bool __fastcall TComm::SendData(char* str_in); protected: nPort __fastcall GetCommPort() {return F_Port;} void __fastcall SetCommPort(nPort br) {if (CommExists(br)) F_Port = br; else { for (int x=0; x<9; x++) if (CommExists((nPort)x)){ F_Port = (nPort)x; return;}}} bool __fastcall OpenCommP(); bool __fastcall CloseCommP(); void __fastcall DataInBuffer(DWORD *InQueue, DWORD *OutQueue); void __fastcall PollingModeRead(TObject *Sender); bool __fastcall CommExists(nPort Port); }; //---------------------------------------------------------------------------
//--------------------------------------------------------------------------- #include <basepch.h> #pragma hdrstop #include "Comm.h" #include "stdio.h" #pragma package(smart_init) //--------------------------------------------------------------------------- // Mit ValidCtrCheck wird sichergestellt, daß die erzeugten Komponenten // keine rein virtuellen Funktionen besitzen. // static inline void ValidCtrCheck(TComm *) { new TComm(NULL); } //--------------------------------------------------------------------------- //::Construktor //--------------------------------------------------------------------------- __fastcall TComm::TComm(TComponent* Owner) : TComponent(Owner) { //DCB erstellen FDCB = NULL; FDCB = new CommSet; //Timer für DatenLesen im PollingMode erstellen und Behandlung zuweisen PollingModeTimer = NULL; //PollingModeTimer = new TTimer(NULL); //PollingModeTimer->OnTimer = PollingModeRead; //PollingModeTimer->Enabled = false; //Thread für EventMode erstellen EventModeThread = NULL; //EventModeThread = new TSerThread(true, this); //Initialisierung der Kompoeigenschaften (Buffergrößen, Leseintervall, etc.) BufferInput = 2048; BufferOutput = 2048; PollingTime = 1000; F_Connected = false; //initialisieren der restl. Variablen cID = NULL; DatenBuffer = ""; } //--------------------------------------------------------------------------- //::Destruktor //--------------------------------------------------------------------------- __fastcall TComm::~TComm() { //löschen der DCB-Struktur (Klasse) if (FDCB) delete FDCB; FDCB = NULL; //löschen des Timers vom PollingMode if (PollingModeTimer) delete PollingModeTimer; PollingModeTimer = NULL; //löschen des EventMode Thread's if (EventModeThread) EventModeThread->Terminate(); EventModeThread = NULL; } //--------------------------------------------------------------------------- //Com-Port öffnen //--------------------------------------------------------------------------- bool __fastcall TComm::OpenCommP() { //überprüfen ob der Comport überhaupt existiert if(!CommExists(F_Port)) return false; //ComPort String ermitteln String port = "Com"; switch(F_Port) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: port += AnsiString(F_Port+1); break; default: return false; }; //Öffnen der seriellen Schnittstelle cID = CreateFile( port.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, 0); //Sende und Empfangsbuffer setzen if (cID==INVALID_HANDLE_VALUE) return false; if (!SetupComm(cID, F_InputBuffer, F_OutputBuffer)) {CloseHandle(cID); return false;} //Ereignisbehandlunsmaske für EventMode setzen if (F_Modus == EventMode) if (!SetCommMask(cID, EV_RXCHAR | EV_TXEMPTY)) return false; //Initialisieren der seriellen Schnittstelle if (!SetCommState(cID, &CommDCB->FDCB)) { CloseHandle(cID); cID=INVALID_HANDLE_VALUE; return false; } //Abbrechen der Übertragung und Buffer leeren //PurgeComm(cID,PURGE_TXCLEAR); //PurgeComm(cID,PURGE_RXCLEAR); //Statusvariable setzen F_Connected = true; return true; } //--------------------------------------------------------------------------- bool __fastcall TComm::CloseCommP() { if (CloseHandle(cID)) F_Connected = false; return !F_Connected; } //--------------------------------------------------------------------------- void __fastcall TComm::DataInBuffer(DWORD *InQueue, DWORD *OutQueue) { COMSTAT ComStat; DWORD e; //löschen des Fehlerstatus, Abfragen der Bufferfülle if (ClearCommError(cID, &e, &ComStat)) { *InQueue = ComStat.cbInQue; *OutQueue = ComStat.cbOutQue; } else { *InQueue = 0; *OutQueue = 0; } } //--------------------------------------------------------------------------- namespace Comm { void __fastcall PACKAGE Register() { TComponentClass classes[1] = {__classid(TComm)}; RegisterComponents("Standard", classes, 0); } } //--------------------------------------------------------------------------- void __fastcall TComm::PollingModeRead(TObject *Sender) { DWORD InQue, OutQue; DWORD ToRead, HasRead; char* Daten = NULL; OVERLAPPED ov; ov.Internal=0; ov.InternalHigh=0; ov.Offset=0; ov.OffsetHigh=0; ov.hEvent=0; DataInBuffer(&InQue, &OutQue); if (InQue>0) { ToRead=InQue; Daten = new char[ToRead+1]; ReadFile(cID, Daten, ToRead, &HasRead, &ov); Daten[ToRead] = 0; if(!GetOverlappedResult(cID, &ov, &HasRead, true)) { if (Daten) delete [] Daten; return; } DatenBuffer = Daten; bool toDelete = true; if(F_MyDataRead)F_MyDataRead(DatenBuffer, toDelete); if (toDelete) DatenBuffer = ""; } if (Daten) delete [] Daten; } //--------------------------------------------------------------------------- //::Verbindung zum Com-Port herstellen //--------------------------------------------------------------------------- bool __fastcall TComm::Connect() { try { //Ereignisbehandlung OnBeforeOpen(...) aufrufen if(F_MyBeforeOpen)F_MyBeforeOpen(FDCB->FDCB); //Datenbuffer leeren DatenBuffer = ""; //falls noch keine Connection besteht if (!F_Connected) { //Comm-Port mit Daten öffnen if (!OpenCommP()) return false; //Je nach Modus die entsprechene Bearbeitung zum Daten lesen starten switch(F_Modus) { case PollingMode: //eventl. vorhandenen Timer löschen if (PollingModeTimer) delete PollingModeTimer; PollingModeTimer = NULL; //neuen Timer anlegen und starten PollingModeTimer = new TTimer(NULL); PollingModeTimer->OnTimer = PollingModeRead; PollingModeTimer->Interval = F_PollingTimeout; PollingModeTimer->Enabled = true; break; case EventMode: //eventl. vorhanden Thread löschen if (EventModeThread) EventModeThread->Terminate(); EventModeThread = NULL; //neuen Thread anlegen und starten EventModeThread = new TSerThread(false, this); break; default: return false; }; } else { //Comm-Port schliessen if (!CloseCommP()) return false; //Comm-Port mit Daten öffnen if (!OpenCommP()) return false; //Je nach Modus die entsprechene Bearbeitung zum Daten lesen starten switch(F_Modus) { case PollingMode: PollingModeTimer->Interval = F_PollingTimeout; PollingModeTimer->Enabled = true; break; case EventMode: //Code folgt break; default: return false; }; } } catch(...) { return false; } return true; } //--------------------------------------------------------------------------- //::Comm-Port überprüfen, ob vorhande //--------------------------------------------------------------------------- bool __fastcall TComm::CommExists(nPort Port) { char s[6]; DWORD i; COMMCONFIG CommConfig; strcpy(s, "COMx"); s[3] = (char) (0x31+Port); i = sizeof(CommConfig); return (GetDefaultCommConfig(s, &CommConfig, &i)); } //--------------------------------------------------------------------------- //::Daten versenden //--------------------------------------------------------------------------- bool __fastcall TComm::SendData(char* str_in) { try { DWORD BytesWritten, BytesTransfered; OVERLAPPED ov; ov.Internal=0; ov.InternalHigh=0; ov.Offset=0; ov.OffsetHigh=0; ov.hEvent=0; //Sendebuffer leeren (eventl. wieder entfernen, weil ich nicht weis ob das für alle Sendebuffer einzeln zählt) //PurgeComm(cID,PURGE_TXCLEAR); WriteFile(cID, str_in, strlen(str_in), &BytesWritten, &ov); //Overlapped bearbeiten (warten bis abgearbeitet) if(!GetOverlappedResult(cID, &ov, &BytesTransfered, true)) return false; } catch(...) {return false;} return true; } //---------------------------------------------------------------------------
Der Thread
class TComm; //--------------------------------------------------------------------------- class TSerThread : public TThread { private: TComm *CommClass; char *Buf; protected: void __fastcall Execute(); void __fastcall OnDataRead(); public: __fastcall TSerThread(bool CreateSuspended, TComm *Comm); __fastcall ~TSerThread(); }; //--------------------------------------------------------------------------- #endif //--------------------------------------------------------------------------- #include <vcl.h> #pragma hdrstop #include "EventModeThread.h" #pragma package(smart_init) //--------------------------------------------------------------------------- // Wichtig: Methoden und Eigenschaften von Objekten der VCL können nur // in Methoden verwendet werden, die Synchronize aufrufen, z.B.: // // Synchronize(UpdateCaption); // // wobei UpdateCaption so aussehen könnte: // // void __fastcall TSerThread::UpdateCaption() // { // Form1->Caption = "In Thread aktualisiert"; // } //--------------------------------------------------------------------------- __fastcall TSerThread::TSerThread(bool CreateSuspended, TComm *Comm) : TThread(CreateSuspended) { CommClass = Comm; Buf = new char[CommClass->F_InputBuffer]; } //--------------------------------------------------------------------------- _fastcall TSerThread::~TSerThread() { if (Buf) delete [] Buf; } //--------------------------------------------------------------------------- void __fastcall TSerThread::Execute() { //Thread soll sich nach Beednigung von Execute() selber zerstören FreeOnTerminate = true; DWORD InQue, OutQue; //EingabeBufferzeichenmenge / Ausgabebufferzeichenmenge DWORD ToRead, HasRead; //zu lesende Zeichen / gelesene Zeichen DWORD dwEvtMask; //Eventtype (nur Rückgabe) OVERLAPPED ov; ov.Internal=0; ov.InternalHigh=0; ov.Offset=0; ov.OffsetHigh=0; ov.hEvent= CreateEvent(NULL, FALSE, FALSE, NULL); while(!Terminated) //Hauptschleife { if (WaitCommEvent(CommClass->cID, &dwEvtMask, NULL)) //Ereignis aufgetreten { if (dwEvtMask & EV_TXEMPTY) { //The last character in the output buffer was sent } else if (dwEvtMask & EV_RXFLAG) { //The event character was received and placed in the input buffer. //The event character is specified in the device's DCB structure, //which is applied to a serial port by using the SetCommState function. } else if (dwEvtMask & EV_RXCHAR) //Daten wurde empfangen { //A character was received and placed in the input buffer. CommClass->DataInBuffer(&InQue, &OutQue); //Datenmenge ermitteln if (InQue>0) //Daten im Empfangsbuffer { ToRead = InQue; if (ToRead>CommClass->F_InputBuffer) ToRead=CommClass->F_InputBuffer; ReadFile(CommClass->cID, &Buf, ToRead, &HasRead, &ov); /*if(!GetOverlappedResult(CommClass->cID, &ov, &HasRead, true)) { if (Buf) delete [] Buf; return; } */ int i = 3; //nur zum test wegen Ausstieg Buf[HasRead]=0; //Ereignisbehandlungsmethode aufrufen Synchronize(OnDataRead); } } else if (dwEvtMask & EV_RLSD) { //The RLSD (receive-line-signal-detect) signal changed state. } else if (dwEvtMask & EV_RING) { //A ring indicator was detected. } else if (dwEvtMask & EV_ERR) { //A line-status error occurred. Line-status errors are CE_FRAME, CE_OVERRUN, and CE_RXPARITY. } else if (dwEvtMask & EV_DSR) { //The DSR (data-set-ready) signal changed state. } else if (dwEvtMask & EV_CTS) { //The CTS (clear-to-send) signal changed state. } else if (dwEvtMask & EV_BREAK) { //A break was detected on input. } } }; } //--------------------------------------------------------------------------- void __fastcall TSerThread::OnDataRead() { CommClass->DatenBuffer += Buf; bool toDelete = true; if(CommClass->F_MyDataRead)CommClass->F_MyDataRead(CommClass->DatenBuffer, toDelete); if (toDelete) CommClass->DatenBuffer = ""; } //---------------------------------------------------------------------------
Das Problem tritt auf wenn cih in der Komponente EventMode wähle (in diesem wird die ReadMethode im Thread ausgeführt, welche direkt auf Events der Comm-Schnittstelle reagiert.
Wenn ich also eine tesString absende, bekomme ich dieses event ausgelöst, dann kann ich dort drin mit F7 bis int i = 3; durchgehen, dort steigt er erst wegen Speicherzugriff aus und dannach bringt er noch die Fehlermeldung mit Systemcode 5.Ich schätze mal das ich wohl irgendwas mit dem Thread falsch mache, ist das erste mal das ich damit arbeite.
Vielleicht kann mir ja jemand helfen. Sonstige Verbesserungen werden auch gerne aufgegriffen.
So, ich habe jetzt extra nochmal die Fehlermeldungen rausgeschrieben.
Als erstes kommt:Speicherverletzung beim SChreiben von 74736550
dannach:
EAccessViolation in BORLNDMM.DLL beim lesen von 74736550
und zu guter letzt noch:
EOSError - Systemfehler - Code: 5
-
Lass mal das & vor Buf bei ReadFile weg.
-
Danke, das wars. Das hab ich einfach nicht mehr gesehen.
Falls jemand jetzt noch konstruktive Kritik zu meiner Koponente loswerden möchte, dann her damit.