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.


Anmelden zum Antworten