Hilfe bei der Umleitung der Konsolenausgabe...

Du kannst dem VS sagen, welche Argumente dem Programm beim Debuggen übergeben werden sollen. Dazu gehst Du im Menü auf Projekt->Eigenschften von <Projektname>. Im folgenden Dialog wählst Du Deine Debug-Konfiguration (die ist normalerweise schon ausgewählt, wenn sie die aktive Konfiguration ist). Im linken Teil wählst Du unter 'Konfigurationseigenschaften' den Punkt 'Debuggen' aus. Dann kannst Du im rechten Teil in der Zeile 'Befehlsargumente' Deine Parameter angeben. Die werden dann bei jedem Debug-Start Deinem Programm übergeben.
Zu Deinen Fragen:
1.: Das dürfte, wenn überhaupt, nur sehr schwierig machbar zu sein. Du bräuchtest ein HANDLE auf die Console und müßtest die Farbinformationen ihres Buffers abfragen. Du hast zwar das Prozesshandle, aber ob das auch das Konsolenhandle ist weiß ich nicht.
2.: Dazu müßtest Du eine zweite Pipe erzeugen und damit auch noch das InputHandle füttern:
Code:
 bOK = CreatePipe(&hReadOutPipe, &hWriteOutPipe, &sa, 1048576);
    if(!bOK)
    {
        //FEHLER
        dwError = GetLastError();
        bOK = FALSE;
        goto PROCESS_END_NOPIPE;
    }
 bOK = CreatePipe(&hReadInPipe, &hWriteInPipe, &sa, 1048576);
    if(!bOK)
    {
        //FEHLER
        dwError = GetLastError();
        bOK = FALSE;
        goto PROCESS_END_NOINPIPE;
    }
    //Pipes eintragen
    si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
    si.hStdInput = hReadInPipe; //Lese-Ende der Eingabe-Pipe an Prozess
    si.hStdOutput = hWriteOutPipe;                        //Schreib-Ende der Ausgabe-Pipe an Prozess übergeben
    si.hStdError = hWriteOutPipe;                        //Schreib-Ende der Ausgabe-Pipe an Prozess übergeben
    si.wShowWindow = SW_SHOWMINNOACTIVE;
Dann müßtest Du noch einen 2. Thread erzeugen, der auf gesendete Eingaben (mit recv) wartet und diese in das Schreib-Ende der Eingabe-Pipe schreibt. So bekommt der Prozess diese Eingaben.

Alles in Allem sieht das schon mehr aus wie ein Telnet- oder SSH-Terminal. Bei einem solchen könnte dann auch das mit der farbigen Ausgabe klappen.
 
Hab den Code eingefügt, doch im restlichem Code sind noch die alten Handles wie hReadPipe, hWritePipe. In welchen Handle soll ich die alten ändern z.b. hier:

Code:
 while(dwSizeLow = GetFileSize(hReadPipe, &dwSizeHigh))
Code:
if(bDoRead = ReadFile(hReadPipe, cReadLine, MAX_LINE_LENGTH - 1, &dwBytesRead, NULL))
Code:
PROCESS_END_NOPROC:
    CloseHandle(hWritePipe);
    CloseHandle(hReadPipe);
Das mit dem Thread hab ich nicht ganz so verstanden, soll dieser Thread vor der Funktion ProcessCommandLine() laufen und wie soll CreateProcess() rausfinden ob es um ein Out- oder Input handelt?

Alles in Allem sieht das schon mehr aus wie ein Telnet- oder SSH-Terminal. Bei einem solchen könnte dann auch das mit der farbigen Ausgabe klappen.
Anfangs wollte ich ein server erstellen, welcher im LAN befehle von client ausführt, doch es hat sich bißchen erweitert mit Pipes und vielleicht kommt noch eine Authentifizierung, jeder Client muss sich registrieren (MAC, Hostname,..) der sich mit dem Server in Verbindung setzt und vielleicht noch eine Passwort Abfrage... :)
Wie meintest du das mit der farbigen Ausgabe das es klappen könnte.
 
In welchen Handle soll ich die alten ändern
Ich dachte, das erklärt sich aus der STARTUPINFO-Struktur. Die alten Pipe-Handles heißen jetzt hReadOutPipe und hWriteOutPipe.
soll dieser Thread vor der Funktion ProcessCommandLine() laufen
Du solltest den Thread starten, nachdem der Prozess erfolgreich gestartet wurde, aber bevor Du die ersten Daten aus der Prozess-Pipe liest.
Wie meintest du das mit der farbigen Ausgabe das es klappen könnte.
Ein Telnet-Server könnte sich auf dem Rechner für das gestartete Tool verhalten wie eine normale Konsole und so auch die Anweisungen für die Textfarbe vom Tool bekommen und sie an den Client weiterleiten. Ob das mit dem Windows-Telnet funktioniert, weiß ich nicht.

Zu meinem vorherigen Post:
Ich habe gerade festrgestellt, daß das Konsolenhandle normalerweise das Standard-Output-Handle ist. Jetzt kommen einige wilde Vermutungen:
Vielleicht kannst Du ja die Farbinformationen aus dem hWriteOutPipe-Handle oder dem hReadOutPipe-Handle lesen (Mit 'GetConsoleScreenBufferInfo'). Wenn das klappt, dann könntest Du diese Informationen an den Client senden, der sie dann in der Anzeige seiner Konsole umsetzt. Dazu müßtest Du natürlich die Zeile mit dem 'send' so abändern, daß der Server weiß, was da gesendet wurde (Text oder Farbinformation). Du kannst also nicht mehr nur einfach den Lesepuffer senden, sondern ein struct, welches die Art der Daten, die Datenlänge und die Daten selber enthält.
Vielleicht mußt du dann auch die Zeichen einzeln an den Client senden und vorher immer die Buffer-Attribute abfragen, da in der CONSOLE_SCREEN_BUFFER_INFO-Struktur immer nur die aktuell eingestellten Attribute stehen, also die, die für das nächste auf der Konsole eingegebene Zeichen gelten. Dann könntest du natürlich immer zu jedem Zeichen die entsprechenden Attribute mitschicken. Aber, wie gesagt, das sind alles Vermutungen. Du kannst es gerne ausprobieren und Deine Ergebnisse hier posten.

Putty ist ein GNU-Telnet, die Binaries und den Sourcecode gibt es hier. Du kannst Dir ja mal den Sourcecode ansehen, vielleicht hilft Dir das weiter. Vielleicht verwirrt es Dich aber auch nur ;-)
 
Wenn ich jetzt denn geänderten Code ausführe, dann bekomme ich ein Fehler:
Code:
error C2094: Marke 'PROCESS_END_NOINPIPE' nicht definiert
Code:
BOOL ProcessCommandLine(char * pcCommandLine)
{
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    SECURITY_ATTRIBUTES sa;
    //HANDLE hReadPipe, hWritePipe;
    DWORD dwWaitResult = WAIT_TIMEOUT, dwBytesRead, dwExitCode, dwSizeLow, dwSizeHigh, dwError;
    char cReadLine[MAX_LINE_LENGTH];
    BOOL bDoRead = TRUE, bOK = TRUE;
    MSG AppMsg;
    HANDLE hReadOutPipe, hWriteOutPipe, hReadInPipe, hWriteInPipe;

    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);


    //Pipes erzeugen
    sa.nLength=sizeof(sa);
    sa.bInheritHandle=TRUE;
    sa.lpSecurityDescriptor=NULL;

    bOK = CreatePipe(&hReadOutPipe, &hWriteOutPipe, &sa, 1048576);
    if(!bOK)
    {
        //FEHLER
        dwError = GetLastError();
        bOK = FALSE;
        goto PROCESS_END_NOPIPE;
    }
    bOK = CreatePipe(&hReadInPipe, &hWriteInPipe, &sa, 1048576);
    if(!bOK)
    {
        //FEHLER
        dwError = GetLastError();
        bOK = FALSE;
        goto PROCESS_END_NOINPIPE;
    }
    //Pipes eintragen
    si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
    si.hStdInput = hReadInPipe; //Lese-Ende der Eingabe-Pipe an Prozess
    si.hStdOutput = hWriteOutPipe;                        //Schreib-Ende der Ausgabe-Pipe an Prozess übergeben
    si.hStdError = hWriteOutPipe;                        //Schreib-Ende der Ausgabe-Pipe an Prozess übergeben
    si.wShowWindow = SW_SHOWMINNOACTIVE;

    //Prozess starten
    bOK = CreateProcess(NULL,
                        pcCommandLine,
                        NULL,
                        NULL,
                        TRUE,
                        NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE,
                        NULL,
                        NULL,
                        &si,
                        &pi);

    if(!bOK)
    {
        //FEHLER
        dwError = GetLastError();
        goto PROCESS_END_NOPROC;
    }

    //Auf Ende warten und Messages lesen
    while(dwWaitResult != WAIT_OBJECT_0)
    {
        dwWaitResult = WaitForSingleObject(pi.hProcess, 10);
        while(dwSizeLow = GetFileSize(hReadOutPipe, &dwSizeHigh))
        {
            ZeroMemory(cReadLine, MAX_LINE_LENGTH);
            //Aus dem Leseende der Pipe die Progzessausgaben lesen
            if(bDoRead = ReadFile(hReadOutPipe, cReadLine, MAX_LINE_LENGTH - 1, &dwBytesRead, NULL))
            {
                if(dwBytesRead)
                {
                    //hier Programmausgabe verarbeiten (z.B. send)                    
                }
            }
            else
            {
                bOK = FALSE;
                dwError = GetLastError();
            }
        }
        //Applikations-Messagequeue verarbeiten
        if(PeekMessage(&AppMsg, 0, 0, 0, PM_NOREMOVE))
        {
            GetMessage(&AppMsg, 0, 0, 0);
            TranslateMessage(&AppMsg);
            DispatchMessage(&AppMsg);
        }
    }

    if(bOK)
    {
        bOK = GetExitCodeProcess(pi.hProcess, &dwExitCode);
        if(bOK)
        {
            if(dwExitCode != 0)
            {
                //Prozess kehrt mit Ergebnis != 0 zurück
                bOK = FALSE;
            }
        }
        else
        {
            //FEHLER im GetExitCodeProcess
            dwError = GetLastError();
            bOK = FALSE;
        }
    }

    //Close handles
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);

PROCESS_END_NOPROC:
    CloseHandle(hWriteOutPipe);
    CloseHandle(hReadOutPipe);

PROCESS_END_NOPIPE:
    return bOK;
}
 
Ich hatte nicht erwartet, Dir jede Anpassung explizit erklären zu müssen! Selber denken schadet nicht. :rolleyes:

Natürlich mußt du die erzeugten Handles der OutPipe schließen, wenn die Prozedur beendet wird oder wenn das Erzeugen der InPipe fehlschlägt:
Code:
 PROCESS_END_NOPROC:
    CloseHandle(hWriteInPipe);
    CloseHandle(hReadInPipe);

PROCESS_END_NOINPIPE:
    CloseHandle(hWriteOutPipe);
    CloseHandle(hReadOutPipe);
     return bOK;

 PROCESS_END_NOPIPE:
    return bOK;
Aber wie ich schon gesagt habe, wäre es besser, wenn Du die goto's ersetzen würdest, z.B. durch die Verwendung von Exceptions.
 
Wenn ich richtig verstanden habe, dann soll im Thread auf die auf die Eingabe vom Client gewartet werden, welche mit recv() empfangen wird.

Du solltest den Thread starten, nachdem der Prozess erfolgreich gestartet wurde, aber bevor Du die ersten Daten aus der Prozess-Pipe liest.
Also in etwa hier
Code:
//Auf Ende warten und Messages lesen
    while(dwWaitResult != WAIT_OBJECT_0)
    {
        dwWaitResult = WaitForSingleObject(pi.hProcess, 10);
        while(dwSizeLow = GetFileSize(hReadOutPipe, &dwSizeHigh))
        {
//Thread
            hThread = CreateThread( NULL, 0, threadfunc, data, 0, 0 );

            ZeroMemory(cReadLine, MAX_LINE_LENGTH);
            //Aus dem Leseende der Pipe die Progzessausgaben lesen
            if(bDoRead = ReadFile(hReadOutPipe, cReadLine, MAX_LINE_LENGTH - 1, &dwBytesRead, NULL))
            {
Die Beispiel Thread Funktion:
Code:
unsigned long __stdcall threadfunc( void *arg )
{    
    //recv() Empfange eingelesene Daten
    return 0;
}
Aber dann müsste ich auch im Client Anpassungen vornehmen oder?
 
Das stimmt so nicht ganz. So wie Du es gemacht hast, würde ja bei jedem Durchlauf der Schleife ein neuer Thread gestartet werden.
Erstmal legst Du ein struct an, in dem Du das Handle, den Socket sowie eine bool-Variable unterbringst:
Code:
typedef struct t_ThreadData
{
    bool bEndThread;
    HANDLE hPipe;
    SOCKET s;
};
Dann startest Du den Thread, nach dem erfolgreichen Aufruf von CreateProcess, aber vor der Warteschleife:
Code:
    //Prozess starten
    bOK = CreateProcess(NULL,
                        pcCommandLine,
                        NULL,
                        NULL,
                        TRUE,
                        NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE,
                        NULL,
                        NULL,
                        &si,
                        &pi);

    if(!bOK)
    {
        //FEHLER
        dwError = GetLastError();
        goto PROCESS_END_NOPROC;
    }

    //Thread
    t_ThreadData ThreadData;
    ThreadData.bEndThread = false;
    ThreadData.hPipe = hWriteInPipe;
    ThreadData.s = s;
    hThread = CreateThread( NULL, 0, threadfunc, (void*)&ThreadData, 0, 0 );

    //Auf Ende warten und Messages lesen
    while(dwWaitResult != WAIT_OBJECT_0)
    {
        dwWaitResult = WaitForSingleObject(pi.hProcess, 10);
        while(dwSizeLow = GetFileSize(hReadOutPipe, &dwSizeHigh))
        {
            ZeroMemory(cReadLine, MAX_LINE_LENGTH);
            //Aus dem Leseende der Pipe die Progzessausgaben lesen
            if(bDoRead = ReadFile(hReadOutPipe, cReadLine, MAX_LINE_LENGTH - 1,    &dwBytesRead, NULL))
            {
    ...
    }
    //Nach der Schleife Thread beenden
    ThreadData.bEndThread = true;
Die Threadfunktion sollte dann etwa so aussehen:
Code:
 unsigned long __stdcall threadfunc( void *data)
{
    t_ThreadData *pThreadData = (t_ThreadData*)data;
    ...
    while(!pThreadData->bEndThread)
    {
        //recv(pThreadData->s, ... ) Empfange eingelesene Daten
        //WriteFile(pThreadData->hPipe, .....) empfangene Daten in die Pipe schreiben
    }
    return 0;
}
Den Zugriff auf bEndThread solltest Du allerdings besser mit einer Critical Section bzw. einem Mutex oder einer Semaphore absichern.
 
Hab soweit alles übernommen, hier zur Sicherheit der Thread:

Code:
unsigned long __stdcall threadfunc( void *data)
{
    t_ThreadData *pThreadData = (t_ThreadData*)data;
    
    char buffer[1024];
    DWORD dwBytesWrite;
    char cReadLine[MAX_LINE_LENGTH];

    while(!pThreadData->bEndThread)
    {
        //recv(pThreadData->s, ... ) Empfange eingelesene Daten
        //WriteFile(pThreadData->hPipe, .....) empfangene Daten in die Pipe schreiben
        recv(pThreadData->s, buffer, strlen(buffer), 0);
        WriteFile(pThreadData->hPipe, cReadLine, MAX_LINE_LENGTH - 1, &dwBytesWrite, NULL);
    
    }
    return 0;
}

Hab mir ein Programm test.exe erstellt:
Code:
string str;     
cout << "Eingabe: ";
cin >> str;
cout << "Ausgabe: ";
Doch sobald ich den server starte und mit dem client die test.exe starten will öffnet sich für diesen Prozess ein neues Fenster. Im Client sehe ich nur die Ausgabe "Eingabe: " von test.exe, aber ich kann nix eingeben.

Wie ich sehe wird die Thread Funktion immer ausgeführt auch wenn keine Input Eingabe verwendet wird oder?
 
Zuletzt bearbeitet:
recv(pThreadData->s, buffer, strlen(buffer), 0);
WriteFile(pThreadData->hPipe, cReadLine, MAX_LINE_LENGTH - 1, &dwBytesWrite, NULL);
Das kann doch so garnicht funktionieren! strlen funktioniert nur bei nullterminierten strings!
Code:
unsigned long __stdcall threadfunc( void *data)
{
    t_ThreadData *pThreadData = (t_ThreadData*)data;
    
    char buffer[1024];
    int iBytesReceived;
    DWORD dwBytesWritten;
    char cReadLine[MAX_LINE_LENGTH];

    while(!pThreadData->bEndThread)
    {
        //recv(pThreadData->s, ... ) Empfange eingelesene Daten
        //WriteFile(pThreadData->hPipe, .....) empfangene Daten in die Pipe schreiben
        iBytesReceived=          recv(pThreadData->s, buffer, 1024, 0);
        if(iBytesReceived == SOCKET_ERROR)
        {
            //FEHLER!
        }
        else if (iBytesReceived > 0)
        {
             WriteFile(pThreadData->hPipe, buffer, iBytesReceived, &dwBytesWritten, NULL);
        }
    }
    return 0;
}
Wie ich sehe wird die Thread Funktion immer ausgeführt auch wenn keine Input Eingabe verwendet wird oder?
Richtig. Der Server kann ja nicht vorher wissen, ob das gestartete Programm eine Eingabe erwartet oder nicht.

Doch sobald ich den server starte und mit dem client die test.exe starten will öffnet sich für diesen Prozess ein neues Fenster. Im Client sehe ich nur die Ausgabe "Eingabe: " von test.exe, aber ich kann nix eingeben.
Versuch's mal ohne den Parameter CREATE_NEW_CONSOLE beim CreateProcess. Daran könnte es liegen.
 
Es funktioniert immer noch nicht, hab wenigstens CREATE_NEW_CONSOLE entfernt, jetzt startet kein neues Fenster :)
Der Server soll ja die Eingabe von externen Programmen zum Client übergeben, nicht wahr? Aber wie soll der Client mit einem recv() empfangene Daten als eine Eingabe auslesen oder muss dieser noch angepasst werden
Wenn ich mich nicht irre, muss der Server dem Client sagen, dass ich von dir eine Eingabe brauche aber im Code vom Client wird nur ein recv() erwartet.

Client
Code:
//Eingabe...
//Sende Eingabe...
//Empfange Daten..
while((rc=select(0,&fdSetRead,NULL,NULL,&timeout))>0) 
            { 
                rc=recv(s,buf,1023,0); 
                // server hat die verbindung beendet ? 
                if(rc==0) 
                { 
                    printf("Server closed connection!\n"); 
                    return 1; 
                // fehler: beenden! 
                } 
                else if(rc==SOCKET_ERROR) 
                { 
                    printf("Error: recv failed: %d\n",WSAGetLastError()); 
                    return 1; 
                } 
                // empfangene daten ausgeben 
                buf[rc]='\0'; 
                cout << buf << endl;
            }

Wenns weiter nicht hilft, könnte ich die Binaries zum Testen schicken.
 

Neue Beiträge

Zurück