Programm starten und Ausgabe capturen



  • Hi,

    Ich suche eine Möglichkeit aus meinem Code ein anderes Program zu starten und dessen ausgabe (stdout) in einen Stream/String umuleiten.

    Falls es in C++ sowas nicht gibt, wär auch eine betriebsystemspezifische Lösung (windows - VS2005) willkommen.

    Hab schon versucht sowas zu finden, bin mir aber nicht sicher wie man sowas nennt.

    Vielen Dank im Voraus!
    Vlad.

    Edit:
    ich möchte die gesamte ausgabe. Das Programm kann also ruhig blockieren, solange das andere läuft.
    (ist nur ein winziges Kommandozeilen-tool)



  • "Capture console output" wäre ein gutes Stichwort. Plattformunabhängig geht das vermutlich nicht, und insbesondere unter Windows ist es nicht trivial; alle Details dazu kannst du hier nachlesen. Kurz zusammengefaßt: für Programme mit trivialer Konsolenausgabe ist das mit Pipes machbar, für alles andere (also Programme, die den Konsolenpuffer verändern) suche dir besser eine andere Lösung.

    Kürzlich hatte ich so etwas auch einmal benötigt und mir eine entsprechende Funktion selbst geschrieben - zwar in Delphi, aber da ich fast nur Windows-Funktionen verwende, sollte es sich leicht übertragen lassen. Wenns dir hilft, kann ich sie mal posten.



  • Danke,
    hab jetzt was mit popen gefunden.
    Das ist gar nicht so kompliziert.
    Was mich nur tierisch nervt, ist dass immer kurz ein Kommandozeilenfenster aufpoppt.

    werd mir mal deine lösung anschauen, vielleicht lässt sich das damit verhindern.

    si = _popen( ( string("si sandboxinfo -S ")+i_sandbox).c_str(), "r");
      if( si == NULL )
        throw string("Kann si.exe nicht starten!\n");
    
      string ret;
      while( !feof( si ) )
      {
        if( fgets( psBuffer, sizeof(psBuffer), si ) != NULL )
        {
          string temp(psBuffer);
        }
      }
      _pclose( si );
    


  • Würde mich nicht wundern, wenn deine Variante für Vollast sorgte. Das könnte man mit WaitForSingleObject() oder, wenn man ganz brutal ist, mit Sleep() vermeiden; keine Ahnung, ob es dafür ein POSIX-Äquivalent gibt.

    Um das Öffnen des Konsolenfensters zu unterbinden, müßtest du vermutlich direkt CreateProcess benutzen. Meine Variante macht es so:

    function PipeToString (hNamedPipe: THandle; PipeCodePage: ShortInt = CP_ACP): RawByteString;
    var
      BytesAvailable, BytesRead: Cardinal;
    begin
      Win32Check (PeekNamedPipe (hNamedPipe, nil, 0,
        nil, @BytesAvailable, nil));
      SetLength (Result, BytesAvailable);
      Win32Check (ReadFile (hNamedPipe, PAnsiChar (Result)^, BytesAvailable, BytesRead, nil));
      SetCodePage (Result, PipeCodePage, False);
    end;
    
    function CaptureConsoleOutput (Executable, Parameters: String;
      OutputCodePage: ShortInt = CP_OEMCP): RawByteString;
    var
      StartupInfo: TStartupInfo;
      ProcInfo: TProcessInformation;
      SecAttr: TSecurityAttributes;
      hReadOutputPipe: THandle;
    
      procedure InitVars;
      begin
        ZeroMemory (@SecAttr, SizeOf (SecAttr));
        ZeroMemory (@StartupInfo, SizeOf (StartupInfo));
        ZeroMemory (@ProcInfo, SizeOf (ProcInfo));
        hReadOutputPipe := 0;
    
        SecAttr.nLength := SizeOf (SecAttr);
        SecAttr.lpSecurityDescriptor := nil;
        SecAttr.bInheritHandle := True;
        StartupInfo.cb := SizeOf (StartupInfo);
        StartupInfo.dwFlags := STARTF_USESTDHANDLES;
      end;
    
      procedure SetUpPipes;
      begin
        Win32Check (CreatePipe (hReadOutputPipe, StartupInfo.hStdOutput, @SecAttr, 0));
        StartupInfo.hStdError := StartupInfo.hStdOutput;
      end;
    
      procedure FreeResources;
      begin
        CloseHandlesIfValid ([hReadOutputPipe, StartupInfo.hStdOutput,
          ProcInfo.hProcess, ProcInfo.hThread]);
      end;
    
      procedure FlushPipe;
      begin
        Result := Result + PipeToString (hReadOutputPipe, OutputCodePage);
      end;
    
    begin
      InitVars;
      try
        SetUpPipes;
        Win32Check (CreateProcess (nil, PChar (Format ('"%s" %s', [Executable, Parameters])),
          @SecAttr, @SecAttr, TRUE, CREATE_NO_WINDOW, nil, nil, StartupInfo, ProcInfo));
    
          // The pipe's buffer size is limited; the process will block if we don't fetch
          // the data from time to time.
        while WaitForSingleObject (ProcInfo.hProcess, 100) = WAIT_TIMEOUT do
          FlushPipe;
        FlushPipe;
      finally
        FreeResources;
      end;
    end;
    


  • ui, das ist aber umständlich.

    mal schauen. Wenns dafür kein consolenfester gibt.
    Hab leider noch keine Zeit gehabt das zu testen.

    Würde mich nicht wundern, wenn deine Variante für Vollast sorgte

    das programm läuft nicht mal ne sekunde. Das wird gestartet und gibt soforrt ein paar zeilen aus und beendet sich sofort wieder, von daher gibts da keine große Auslastung.



  • funktioniert bestens, Danke!



  • vlad_tepesch schrieb:

    ui, das ist aber umständlich.

    Umfangreicher Vor- und Nachbereitungsbedarf ist bekanntermaßen eine inhärente Eigenschaft der meisten Windows-Funktionen 😉



  • da hast du recht!
    jedes mal, das struktur anlegen, komplett nullenn und dann in mindestens ein member die größe reinschreiben.


Anmelden zum Antworten