Announcement

Collapse
No announcement yet.

CreateProcess - hStdOutput

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • CreateProcess - hStdOutput

    Hallo zusammen,<p>
    kann mir jemand weiterhelfen?<br>
    Ich rufe mit CreateProcess z.B das Programm "ipconfig.exe" auf.<br>
    Nun möchte ich den StdOutput in ein z.B. TMemo-Feld umleiten.<br>
    Wie bekomme ich das hin?<p>
    Gruß Andreas

  • #2
    Hallo,

    in meinem Buch <i>Delphi Win32-Lösungen</i> ist unter 1.6.3 (Seite 17 bis 20) ein Delphi-Programm zu finden, das den Output des im Eingabeaufforderungs-Fensters abgesetzten DIR-Aufrufs in eine TMemo-Instanz kopiert. Der Kern verbirgt sich hinter der Win32-API-Funktion <b>ReadConsoleOutput</b>

    Comment


    • #3
      Hallo Andreas<p>
      vielen Dank für die Antwort.<br>
      Diese Funktion hat 2 große Nachteile:<br>
      1. Ich bekomme nur den Inhalt des sichtbaren Bereichs (d.H. wenn mein "Dir" in Fenster weiterscollt, bekomme ich den Anfang nicht ausgelesen.<br>
      2. Ich kann das Fenster nicht verstecken. (Jedenfalls habe ich das nicht mit SW_Hide hinbekommen.<p>
      Ich dachte mehr an die Funktion "CreatePipe" zusammen mit "Readfile".
      Allerding bekomme ich das mit dem schreiben in den Buffer von "Readfile" nicht hin.<br>
      Können Sie mir da weiterhelfen?<p>
      Danke Andrea

      Comment


      • #4
        Hallo,

        auch dafür ist im Kapitel über die Interprocess-Kommunikation ein Beispiel im Buch. Allerdings geht das Beispiel davon aus, das der "Elternprozess" seine Handles an den von ihm gestarteten "Sohnprozess" vererbt:

        <pre>
        procedure TFormMain.FormCreate(Sender: TObject);
        var
        aSecurity : TSecurityAttributes;
        aParentProcess : THandle;
        aPipeReadTmp : THandle;
        aSI : TStartupInfo;
        aPI : TProcessInformation;
        begin
        with aSecurity do
        begin
        nLength := SizeOf(aSecurity);
        bInheritHandle := True;
        lpSecurityDescriptor := nil;
        end;
        Win32Check(CreatePipe(FPipeRead, FPipeWrite, @aSecurity, 0));
        Win32Check(SetStdHandle(STD_OUTPUT_HANDLE, FPipeWrite));
        // Read-Pipe soll nicht weitervererbbar sein
        aParentProcess := GetCurrentProcess();
        Win32Check(DuplicateHandle(aParentProcess, FPipeRead, aParentProcess,
        @aPipeReadTmp, 0, False,
        DUPLICATE_SAME_ACCESS));
        CloseHandle(FPipeRead);
        FPipeRead := aPipeReadTmp;
        // Childprozess starten
        GetStartupInfo(aSI);
        // Childprozess soll Handles vom Elternprozess erben
        if not CreateProcess(nil, PChar(cCHILDAPP), nil, nil, True,
        0, nil, nil, aSI, aPI) then
        raise EWin32Error.Create(cERRCREATEPROCESS);
        WaitForInputIdle(aPI.HProcess, Infinite);
        // Elternprozess kann sein Handle schliessen
        CloseHandle(FPipeWrite);
        // Lese-Thread starten
        with TAPReaderThrd.Create(FPipeRead) do
        FreeOnTerminate := True
        end;
        </pre>
        Da in diesem Fall beide Anwendungen gemeinsam entwickelt werden, hat man hier die volle Kontrolle über die Konfiguration.

        Was spricht gegen die direkte Umleitung in eine Datei, die dann von der eigenen Anwendung ausgelesen wird:

        1. BAT-Datei mit dem Inhalt <i>ipconfig.exe > ip.txt</i> erstellen. <br>
        2. BAT-Datei aufrufen <br>
        3. Inhalt von ip.txt einlese

        Comment


        • #5
          Hallo, <p>
          Genau das wollte ich umgehen. Ich wollte mein Delphiprogramm gerne in ein schreibgeschütztes Verzeichnis legen. Wenn das mit dem Auslesen aber nicht möglich ist, muss ich die Umlenkung in ein "offenes" Verzeichnis machen. Ist nicht so schön.<br>
          Gibt es wirklich keine Möglichkeit, die Ausgabe aus der DOS-Box auzulesen?<p>
          Gruß Andrea

          Comment


          • #6
            Hallo Andreas Kosch,<p>
            Ich habe eine Möglichkeit gefunden. Sie hat aber wieder den Nachteil, das ich mit "<b>AllocConsole</b>" arbeiten muß.<br>
            Ich habe folgende Änderungen gemacht:<br>
            1. cCHILDAPP = 'c:\winnt\system32\ipconfig.exe /all';<br>
            2. if AllocConsole then<br>
            <b>begin</b><br>
            Win32Check(CreatePipe(FPipeRead, FPipeWrite, @aSecurity, 0));<br>
            ...<br>
            <b>end;</b><br>
            3. Bevor der "Lese-Thread" gestartet wird, habe ich ein "ShowMessage" eingebaut. (Ohne diese "Wartezeit" funktioniert es irgendwie nicht)<p>
            Gibt es nun eine Möglichkeit dieses "AllocConsole" noch herauszunehmen?<br>
            Gruß Andreas

            Comment


            • #7
              Hallo,

              bevor ich hier im Win32-SDK nach Alternativen suche, würde ich zuerst versuchen, das Konsolenfenster über <b>SetWindowPos</b> aus dem sichtbaren Desktop-Bereich zu verschieben:
              <pre>
              SetWindowPos(hWndConsole,0,-200, -200, 0, 0, 0);
              </pre>
              Damit ist dieses Fenster für den Benutzer "unsichtbar", obwohl des für das Betriebssystem sichtbar ist

              Comment


              • #8
                Hallo Andreas,<p>
                Das ist eine gute Idee. Daran habe ich noch gar nicht gedacht.<br>
                Vielen Dank für alle Hilfe.<p>

                Gruß Andrea

                Comment


                • #9
                  Hi

                  Lässt sich das Consolenfenster NICHT unsichtbar machen ?

                  <pre>

                  SetWindowPos(Handle, 0,0,0,0,0, swp_NoMove or swp_NoSize or swp_NoZOrder or swp_HideWindow);

                  </pre>

                  Gruß Hage

                  Comment


                  • #10
                    @Andreas,

                    leider habe ich das Buch <br>
                    <p>
                    "Delphi Win32-Lösungen ist unter 1.6.3 (Seite 17 bis 20) ein Delphi-Programm zu finden, das den Output des im Eingabeaufforderungs-Fensters abgesetzten DIR-Aufrufs in eine TMemo-Instanz kopiert."
                    und damit die Beschreibung nicht.</p>
                    <br>
                    Könnte man die Grundzüge hier dennoch erleutern? <br>
                    Gruss Stefa

                    Comment


                    • #11
                      Probier doch mal folgendes:

                      Example : Make a dir :
                      ----------------------
                      - if you want to get the result of a 'c:\dir /o:gen /l c:\windows\*.txt'
                      for example, you need to make a batch file
                      --the batch file : c:\mydir.bat
                      @echo off
                      dir /o:gen /l %1
                      rem eof
                      --in your code
                      DosCommand.CommandLine := 'c:\mydir.bat c:\windows\*.txt';
                      DosCommand.Execute;

                      Example : Format a disk (win 9x/Me) :
                      -------------------------
                      --a batch file : c:\myformat.bat
                      @echo off
                      format %1
                      rem eof

                      --in your code
                      var diskname: string;
                      --
                      DosCommand1.CommandLine := 'c:\myformat.bat a:';
                      DosCommand1.Execute; //launch format process
                      DosCommand1.SendLine('', True); //equivalent to press enter key
                      DiskName := 'test';
                      DosCommand1.SendLine(DiskName, True); //enter the name of the volume
                      ************************************************** *****************}

                      unit DosCommand;

                      interface

                      uses
                      Windows, Controls, Forms, Messages, SysUtils, Classes, ExtCtrls;

                      type
                      TOutputType = (otEntireLine, otBeginningOfLine); //to know if the newline is finished.
                      TTimeOutKind = (toSinceBeginning, toSinceLastOutput);

                      TProcessTimer = class(TTimer) //timer for stopping the process after XXX sec
                      private
                      FSinceBeginning: Integer;
                      FSinceLastOutput: Integer;
                      procedure MyTimer(Sender: TObject);
                      public
                      constructor Create(AOwner: TComponent); override;
                      procedure Beginning; //call this at the beginning of a process
                      procedure NewOutput; //call this when a new output is received
                      procedure Ending; //call this when the process is terminated
                      property SinceBeginning: Integer read FSinceBeginning;
                      property SinceLastOutput: Integer read FSinceLastOutput;
                      end;

                      TNewLineEvent = procedure(Sender: TObject; NewLine: string; OutputType: TOutputType) of object;
                      TTerminateEvent = procedure(Sender: TObject; ExitCode: integer) of object;
                      TErrorEvent = procedure(Sender: TObject; Msg: string) of object;
                      TTimeOutEvent = procedure(Sender: TObject; TimeOutKind: TTimeOutKind; var Kill: boolean) of object;

                      TDosThread = class(TThread) //the thread that is waiting for outputs through the pipe
                      private
                      FOwner: TObject;
                      FCommandLine: string;
                      FCurDir: string;
                      FLines: TStringList;
                      FOutputLines: TStrings;
                      FInputToOutput: Boolean;
                      FTimer: TProcessTimer;
                      FMaxTimeAfterBeginning: Integer;
                      FMaxTimeAfterLastOutput: Integer;
                      FOnNewLine: TNewLineEvent;
                      FOnTerminated: TTerminateEvent;
                      FOnStart: TNotifyEvent;
                      FOnTimeOut: TTimeOutEvent;
                      FOnError: TErrorEvent;
                      FPriority: Integer;
                      FHidden: boolean;
                      procedure FExecute;
                      protected
                      procedure Execute; override; //call this to create the process
                      public
                      InputLines: TstringList;
                      constructor Create(AOwner: TObject; ACmdLine, ACurDir: string; ALines: TStringList;
                      AOutLines: TStrings; ATimer: TProcessTimer; AMaxTimeB, AMaxTimeO: Integer; ANewLine: TNewLineEvent;
                      ATerminate: TTerminateEvent; AStart: TNotifyEvent;
                      AError: TErrorEvent;
                      OTimeOut: TTimeOutEvent; APriority: Integer; AIToO, AHidden: Boolean);
                      end;

                      TTPriority = (tpIdle, tpNormal, tpHigh, tpRealTime);
                      TDosCommand = class(TComponent) //the component to put on a form
                      private
                      FOwner: TComponent;
                      FCommandLine: string;
                      FCurDir: string;
                      FLines: TStringList;
                      FOutputLines: TStrings;
                      FInputToOutput: Boolean;
                      FOnNewLine: TNewLineEvent;
                      FOnStart: TNotifyEvent;
                      FOnTerminated: TTerminateEvent;
                      FOnError: TErrorEvent;

                      Comment

                      Working...
                      X