Von Klassen, Threads und FileIOCompletionRoutines ...



  • Hallo.

    Frage: Wie kann ich auf Membervariablen einer Klasse ComPort in einer

    **VOID CALLBACK FileIOCompletionRoutine(DWORD errorCode,DWORD dwNumberOfBytesTransfered,LPOVERLAPPED lpOverlapped)
    **

    zugreifen?

    Ich habe eine "Dreiecksbeziehung":

    1. Application
    2. Thread
    3. CompletionRoutine.

    Alle drei Punkte müssen in einer Klasse definiert sein.

    Waren 1. und 2. schon vorhanden, ist 3. echt hart!

    Hier der Header für 1. und 2. (ohne Completion, funktioniert):

    class ComPort
    {
    private:
    
    	HANDLE					m_hCom;
    	HANDLE					m_hReader, m_hWriter;	//threads
    	COMMCONFIG				m_cc, m_ccBackup;
    	COMMTIMEOUTS			m_cto, m_ctoBackup;
    	DWORD					m_idReader, m_idWriter;
    	DWORD					m_exitReader, m_exitWriter;
    	volatile BOOL			m_runReader, m_runWriter;
    	volatile LPVOID			m_bufferRead, m_bufferWrite;
    	volatile ULONG			m_numRead, m_numWrite;
    
    	static DWORD WINAPI		Reader(LPVOID parent);
    	static DWORD WINAPI		Writer(LPVOID parent);
    
    public:
    	ComPort(void);
    	BOOL				Open(VOID);
    	BOOL				Close(VOID);
    	BOOL				Write(LPVOID bufferWrite, USHORT *numWrite);
    	BOOL				Read(LPVOID bufferRead, USHORT *numRead);
    };
    

    Alles mögliche habe ich schon

    static
    

    gemacht:

    - Instanz der Klasse
    - die in der Completion-Routine benötigten Membervariablen
    - eine Woche gegoogelt ...

    Langsam habe ich kein Bock mehr auf den Scheiss, aber diese Zeilen im ReaderThread können gewaltig nach hinten losgehen (z.B. Kabel ab während der Übertragung).

    while (byteRead != 4) //Header vom Protokoll lesen, um PacketSize zu ermitteln
    {
        GetOverlappedResult(com->m_hCom,&ovl,&byteRead,false);
    }
    

    Die Completion-Routine soll mir die StateMachine weiterschalten und auf Checksum prüfen.

    Bin dankbar für jede konstruktive Antwort!

    Gruss

    Lars


  • Mod

    Du kannst alle OVERLAPPED Strukturen, die aktuell warten in eine klasseneigenen statisch Map verwalten. In der Map speicherst Du zusätzlich die Info welcher ComPort das war.

    Dann kann die FileIOCompletionRoutine Routine in die Map hineinsehen und das Objekt finden, der diese Operation auslöste.

    Es ist unsinnig alle Klassenmember statisch zu machen.



  • @Martin: Danke, dass Du mir Denkanstösse gegeben hast!

    C++ lernt man ein Leben lang!

    Jetzt ist alles in einer Klasse: ThreadFunc und FileIOCompletionRoutine!

    Der Header:

    class ComPort
    {
    private:
    
    	static HANDLE			m_hCom;
    	HANDLE					m_hReader, m_hWriter;	//threads
    	COMMCONFIG				m_cc, m_ccBackup;
    	COMMTIMEOUTS			m_cto, m_ctoBackup;
    	DWORD					m_idReader, m_idWriter;
    	DWORD					m_exitReader, m_exitWriter;
    	volatile BOOL			m_runReader, m_runWriter;
    	volatile LPVOID			m_bufferRead, m_bufferWrite;
    	volatile ULONG			m_numRead, m_numWrite;
    
    	static DWORD WINAPI		Reader(LPVOID parent);
    	static DWORD WINAPI		Writer(LPVOID parent);
    	static VOID CALLBACK	CompletionReader(DWORD error,DWORD transfered,OVERLAPPED* ovl);
    	static VOID CALLBACK	CompletionWriter(DWORD error,DWORD transfered,OVERLAPPED* ovl);
    
    public:
    	ComPort(void);
    	BOOL				Open(VOID);
    	BOOL				Close(VOID);
    	BOOL				Write(LPVOID bufferWrite, USHORT *numWrite);
    	BOOL				Read(LPVOID bufferRead, USHORT *numRead);
    };
    

    Im .cpp war es nur eine einzige Zeile direkt am Beginn!

    #include "this and that.h"
    
    //--------------------------------------------------------
    
    HANDLE ComPort::m_hCom = INVALID_HANDLE_VALUE; //<-Initialisierung einer statischen Member-Variablen!
    
    //--------------------------------------------------------
    
    /*!
        wie gehabt
    */
    

    UND WIE DAS LÄUFT!

    @Martin: Danke nochmal!!!

    Gruss

    Lars



  • Starcraft zocken!

    PS:

    Ich habs! Wie geil ... !



  • @chezzmatazz: Das ist der grösste Mist den ich seit langem gesehen habe.

    @Martin Richter: ich mache das so dass ich ne eigene struct von OVERLAPPED ableite, und da drin dann einen Zeiger auf die "outer" Class speichere. Im Callback dann einfach per static_cast auf meine Struct zurückcasten, und über den "outer" Pointer kommt man dann an das Objekt dran. IMO viel eleganter und vor allem viel performanter als eine statische Map.



  • Für Besseres bin ich immer offen.

    Aber ich versteh nicht, was Du meinst.



  • Erstmal wieso es IMO Mist ist: einfach deswegen, weil das eine static Member die ganze Klasse ad absurdum führt. Wenn du das Port Handle static machst kannst du nie mehr als eine Instanz der Klasse haben, zumindest nicht wenn die Instanzen unterschiedliche Ports verwenden sollen (und davon gehe ich mal aus). Wenn du nie mehr als eine Instanz der Klasse haben kannst, dann ist die Klasse für nix, dann könntest du genauso mit freien Funktionen + globalen Variablen (evtl. in einem Namespace) arbeiten.

    Wie man es besser machen kann: verwende nicht einfach "nackte" OVERLAPPED structs, sondern eine struct die von OVERLAPPED abgeleitet ist. In diese struct packst du dann den "this" Zeiger rein. Beispiel:

    class ComPort
    {
        // ...
    
        HANDLE m_hCom;
    
        struct MyOverlapped : OVERLAPPED
        {
            MyOverlapped(ComPort* instance) : m_instance(instance) { }
            ComPort* m_instance;
        };
    
        static void CALLBACK StaticReadCompletionRoutine(DWORD error, DWORD transfered, OVERLAPPED* ovl)
        {
            MyOverlapped* myOvl = static_cast<MyOverlapped*>(ovl);
            myOvl->m_instance->ReadCompletionRoutine(error, transferred, ovl);
        }
    
        void ReadCompletionRoutine(DWORD error, DWORD transfered, OVERLAPPED* ovl)
        {
            // hier hast du jetzt deinen "this" Zeiger und kannst auf m_hCom zugreifen, ohne dass es static sein muss
        }
    };
    

    Damit das funktioniert musst du natürlich überall wo du ursprünglich eine OVERLAPPED struct verwendet hast jetzt eine MyOverlapped struct verwenden, die du mit dem "this" Zeiger des entsprechenden Objekts initialisierst. In den Fällen in denen ich bisher sowas gebraucht habe ist das allerdings kein Problem gewesen, da sämtliche Aufrufe von Funktionen die eine OVERLAPPED struct erwarten sowieso innerhalb der Klasse erfolgt sind der auch die completion routine gehört.

    EDIT: Fehler korrigiert, die nicht-statische completion routine heisst natürlich "ReadCompletionRoutine", nicht nochmal "StaticReadCompletionRoutine" 🙂


  • Mod

    hustbaer schrieb:

    @Martin Richter: ich mache das so dass ich ne eigene struct von OVERLAPPED ableite, und da drin dann einen Zeiger auf die "outer" Class speichere. Im Callback dann einfach per static_cast auf meine Struct zurückcasten, und über den "outer" Pointer kommt man dann an das Objekt dran. IMO viel eleganter und vor allem viel performanter als eine statische Map.

    Jupp 100% korrekt!



  • Herzlichen Dank euch zweien für die Mühe!


Log in to reply