Problem mit Shutdown des PipeServer



  • ich habe mir das Beispiel von msdn genommen. https://msdn.microsoft.com/en-us/library/windows/desktop/aa365588(v=vs.85).aspx. Es ist einen Pipe Server auf Console Basis. Ich habe mir aber einen WIN32 GUI erzeugt und darin verwende ich etwas abgeänderte Code. Ich erstelle beispielweise 20 Instance mit CreateNamedPipe, starte einen Thread, in Thread kommt ConnectNamedPipe, es funktioniert wie geplant, eine Verbindung kommt auf. Mein Problem ist, wenn ich aber GUI starte, für das Problem erstmal bis ConnectNamedPipe durch und wartet bis ein Client sich mit dem Server verbindet. Ohne Client zu starten, willst ich aber den GUI Server schließe, mit WM_DESTROY und PostQuitMessage. GUI ist dann nicht mehr zu sehen, aber der Prozess bleibt weiterhin bestehen. Ich habe auch versucht beim WM_DESTROY, DiscconectNamedPipe und CloseHandle auszuführen. Klappt leider nicht. Die Frage ist ganz einfach, wie cancel ich das Warten der Funktion "ConnectNamedPipe"? Ich habe auch mit OVERLAPPED Pipe Server ausprobiert, da ist aber das Problem dass nach dem Verbindungsaufbau WaitForMultiObjects nicht mehr auf meinen Event fürs Beenden reagiert.



  • NamedPipe Problem schrieb:

    Die Frage ist ganz einfach, wie cancel ich das Warten der Funktion "ConnectNamedPipe"?

    Wenn du OVERLAPPED IO verwendest kannst du z.B. CancelIoEx verwenden.

    NamedPipe Problem schrieb:

    Ich habe auch mit OVERLAPPED Pipe Server ausprobiert, da ist aber das Problem dass nach dem Verbindungsaufbau WaitForMultiObjects nicht mehr auf meinen Event fürs Beenden reagiert.

    Na dann machst du irgendwas falsch.
    Was kann ich so nicht sagen, ich sehe deinen Code ja nicht.



  • Hier ist mein Code. Der Prozess ist gestartet, es läuft bis ConnectNamedPipe, dann einfach GUI schliessen ohne Client verbunden zu haben. GUI ist dann nicht sichtbar, jedoch ist der Prozess weiter vorhanden und bleibt hängen. Auch dieser CancelIoEx hat es nichts gebracht.

    // main.cpp
    case WM_CREATE: {
    		g_zpsServer.waitForClient();
    } break;
    case WM_DESTROY:
                    g_zpsServer.disconnectAll();
    		PostQuitMessage( 0 );
    		break;
    
    // PipeServer.cpp
    void PipeServer::waitForClient() {
        NamedPipe sNamedPipe;
        ZeroMemory( &sNamedPipe, sizeof( NamedPipe ) );
    
    	NamedPipe* p = NULL;
        for ( int i = 0; i < 10; i++ ) {
            this->m_vNamedPipe.push_back( sNamedPipe );
    
            p = &this->m_vNamedPipe[ i ];
    
            p->hNamedPipe = CreateNamedPipe(
    									    g_szNamedPipe,
    									    PIPE_ACCESS_DUPLEX,
    									    PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
    									    PIPE_UNLIMITED_INSTANCES,
    									    dwBufferSize,
    									    dwBufferSize,
    									    NMPWAIT_USE_DEFAULT_WAIT,
    									    NULL
    									    );
        }
        this->startThread();
    }
    
    void PipeServer::startThread() {
        HANDLE hClientConnect           = CreateThread( NULL, 0, PipeServer::ClientConnect, static_cast< void* >( this ), 0, NULL );
        if ( this->m_hClientCommunication == NULL ) {
            this->m_hClientCommunication = CreateThread( NULL, 0, PipeServer::ClientCommunication, static_cast< void* >( this ), CREATE_SUSPENDED, NULL );
    
            if ( !this->m_hClientCommunication ) {
                this->disconnectAll();
            }
        }
    }
    
    void PipeServer::disconnectAll() {
        for( std::vector< NamedPipe >::iterator it = this->m_vNamedPipe.begin(); it < this->m_vNamedPipe.end(); it++ ) {
            if ( it->hNamedPipe != INVALID_HANDLE_VALUE ) {
                if ( it->bConnected ) {
    		        FlushFileBuffers( it->hNamedPipe );
                    DisconnectNamedPipe( it->hNamedPipe );
                } else {
                    CancelIoEx( it->hNamedPipe, NULL );
                }
                CloseHandle( it->hNamedPipe );
                it->hNamedPipe = INVALID_HANDLE_VALUE;
    	    }
        }
    }
    
    unsigned long __stdcall PipeServer::ClientConnect( void* pParam ) {
        PipeServer* pServer = static_cast< PipeServer* >( pParam );
    
        for( std::vector< NamedPipe >::iterator it = pServer->m_vNamedPipe.begin(); it < pServer->m_vNamedPipe.end(); it++ ) {
            if ( it->hNamedPipe == INVALID_HANDLE_VALUE ) {
    	        return( -1 );
            }
            if ( ConnectNamedPipe( it->hNamedPipe, NULL ) ) {
                ResumeThread( pServer->m_hClientCommunication );
            } else {
                switch( GetLastError() ) {
                case ERROR_PIPE_CONNECTED:  it->bConnected = true; break;
                default: break;
                }
            }
        }
        return( 0 );
    }
    
    unsigned long __stdcall PipeServer::ClientCommunication( void* pParam ) {
        PipeServer* pServer = static_cast< PipeServer* >( pParam );
        unsigned int uIndex = pServer->m_vNamedPipe.size() - 1;
        NamedPipe* p = &pServer->m_vNamedPipe[ uIndex ];
        if ( pServer->Send( *p ) ) {
    
        }
    
        unsigned long dwBytesTotal = 0;
        while ( true ) {
            for( std::vector< NamedPipe >::iterator it = pServer->m_vNamedPipe.begin(); it < pServer->m_vNamedPipe.end(); it++ ) {
                if ( PeekNamedPipe( it->hNamedPipe, NULL, 0, NULL, &dwBytesTotal, NULL ) && dwBytesTotal > 0 ) {
                    pServer->Receive( *it );
                }
            }
        }
    
    	return( 0 );
    }
    


  • Wenn der Prozess weiter läuft, dann klick mal auf Break-All im Debugger.
    Wo steht nun Dein Main-Thread?
    Schau den Callstack an...



  • CallStack von Main-Thread sieht wie gefolgt aus:

    ntdll.dll!775325fc()
    [Frames below may be incorrect and/or missing, no symbols loaded for ntdll.dll]
    KernelBase.dll!74e77b37()
    Test.exe!PipeServer::disconnectAll() Line 92 + 0x16 bytes
    Test.exe!MainProc(HWND__ *hWnd=0x000a1524, unsigned int msg=2, unsigned int wParam=0, long lParam=0) Line 159



  • Klick ich in Call Stack auf die Zeile disconnectAll(), zeigt es auf die CancelIoEx an.



  • Mal grundsätzlich:
    Warum rufst Du CancelIoEx auf, wenn es sowieso keine Verbindung gab?

    Blockierst Du irgendwo sonst einen Thread?
    Fürst Du SuspendThread irgendwo aus?
    Schau mal die anderen Threads an was die machen...

    Ansonsten verstehe ich Deinen Code in keiner Weise, warum erzeugst Du auf Vorrat Pipe mit CreateNamedPipe/ConnectNamedPipe
    Es ist doch einfach das nur bei Bedarf immer enmal auszuführen.
    Zudem solltest für ConnectNamedPipe overlapped IO verwenden.



  • Martin Richter schrieb:

    Mal grundsätzlich:
    Warum rufst Du CancelIoEx auf, wenn es sowieso keine Verbindung gab?

    Blockierst Du irgendwo sonst einen Thread?
    Fürst Du SuspendThread irgendwo aus?
    Schau mal die anderen Threads an was die machen...

    Ansonsten verstehe ich Deinen Code in keiner Weise, warum erzeugst Du auf Vorrat Pipe mit CreateNamedPipe/ConnectNamedPipe
    Es ist doch einfach das nur bei Bedarf immer enmal auszuführen.
    Zudem solltest für ConnectNamedPipe overlapped IO verwenden.

    Die Idee dahinter, ich habe gelesen, dass ein multithreaded Pipe Server einfacher zu implementieren ist. Ich möchte vorab 20 Pipe Instanzen erzeugen, weil es soll bis zu 20 Client nacheinander zum Server Verbindung aufbauen, nachdem Aufbau der Verbindung ist dann der zweiten Thread simultan für die Kommunikation zwischen Server und Clients zuständig.

    hustbaer schrieb:

    NamedPipe Problem schrieb:

    Die Frage ist ganz einfach, wie cancel ich das Warten der Funktion "ConnectNamedPipe"?

    Wenn du OVERLAPPED IO verwendest kannst du z.B. CancelIoEx verwenden.

    NamedPipe Problem schrieb:

    Ich habe auch mit OVERLAPPED Pipe Server ausprobiert, da ist aber das Problem dass nach dem Verbindungsaufbau WaitForMultiObjects nicht mehr auf meinen Event fürs Beenden reagiert.

    Na dann machst du irgendwas falsch.
    Was kann ich so nicht sagen, ich sehe deinen Code ja nicht.

    Ich rufe CancelIoEx, weil hustbaer mir den Rat gab, damit ConnectNamedPipe gecancelt wird. Weil momentan mit oder ohne dem Aufruf von CancelIoEx der Prozess bei beenden hängenbleibt. Ich wollte einfach nur sauber den Prozess beenden.



  • Ohne OVERLAPPED in der ConnectNamedPipe Struktur macht das aber keinen Sinn.
    Es genügt nicht einfach CancelIoEx aufzurufen ohne alles davor zu machen.
    Die Ursache liegt vermutlich genau hier. Du hast kein Overlapped IO also blockiert auch CancelIoEx.

    Es ist IMHO Unfug diese Threads on Demand zu erzeugen.

    Bau eine Schleife für CreateNamedPipe/ConnectNamedPipe.
    Dann erzeuge einen Thread und behandle diese Pipe.
    Dann zurück zur Schleife und warte auf den nächsten Connect.

    In meinen Augen ist das Ressourcenverschwendung.



  • Martin Richter schrieb:

    Ohne OVERLAPPED in der ConnectNamedPipe Struktur macht das aber keinen Sinn.
    Es genügt nicht einfach CancelIoEx aufzurufen ohne alles davor zu machen.
    Die Ursache liegt vermutlich genau hier. Du hast kein Overlapped IO also blockiert auch CancelIoEx.

    Es ist IMHO Unfug diese Threads on Demand zu erzeugen.

    Bau eine Schleife für CreateNamedPipe/ConnectNamedPipe.
    Dann erzeuge einen Thread und behandle diese Pipe.
    Dann zurück zur Schleife und warte auf den nächsten Connect.

    In meinen Augen ist das Ressourcenverschwendung.

    OK, herzlichen Danke für die Infos und Hinweis. Letzte Frage noch zu diesem Beispiel von msdn. Soll das heißen, dass wenn ConnectNamedPipe einmal aufgeführt ist, gibt es keine Möglichkeit mehr, den Prozess "sauber" ohne Hängen zu beenden, bis ein Client sich zum Server verbindet? Ist die einzige Lösung nur über OVERLAPPED Struktur lösbar?



  • Das weiß ich nicht. Aber da ConnectNamedPipe blockiert würde ich sagen, dass es ohne overlapped IO keinen vernünftigen Weg gibt.

    Keine Ahnung ob ein CloseHandle aus einem anderen Thread gehen wurde.
    Ist für mich auch eine vollkommen unsinnige Frage weil es eben genau für so etwas Overlapped IO gibt.