Search code examples
processlazarus

Input and output pipe in Lazarus TProcess


I would like to make a terminal with a Lazarus GUI application. But I'm in trouble. And I hope someone can help me, please.

Question1: The Chinese and other special chars cannot display normally, I would like to know how to fix this problem. (code)Class of the thread and "run" button on click event

screenshot

Question2: I want to know how to input some command into the console. I tried to start a Windows cmd, and use "winver" command. But when I click the button, nothing happened.

The send command button


Solution

  • Winver is not console but a GUI program. To run a program with output into memo, use the following code, which retrieves version using the cmd.exe "ver" command. You can try to use this template for the first question too.

        unit mainprocesstomemo;
        
        {$mode delphi}{$H+}
        
        interface
        
        uses
          Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, Process, Pipes;
        
        Type
          { TForm1 }
        
          TForm1 = class(TForm)
            Button1: TButton;
            Memo1: TMemo;
            procedure Button1Click(Sender: TObject);
          private
          public
            procedure ProcessEvent(Sender,Context : TObject;Status:TRunCommandEventCode;const Message:string);
          end;
        
        var
          Form1: TForm1;
        
        implementation
        
        {$R *.lfm}
        
        { TProcessMemo }
        Type
        
         TProcessToMemo = class(TProcess)
                                    public
                                    fmemo : Tmemo;
                                    bytesprocessed : integer;
                                    fstringsadded : integer;
                                    function ReadInputStream(p:TInputPipeStream;var BytesRead:integer;var DataLength:integer;var Data:string;MaxLoops:integer=10):boolean;override;
                                  end;
        
        
        
        function RunCommandMemo(const exename:TProcessString;const commands:array of TProcessString;out outputstring:string; Options : TProcessOptions = [];SWOptions:TShowWindowOptions=swoNone;memo:TMemo=nil;runrefresh : TOnRunCommandEvent=nil ):boolean;
        Var
            p : TProcessToMemo;
            i,
            exitstatus : integer;
            ErrorString : String;
        begin
          p:=TProcessToMemo.create(nil);
          if Options<>[] then
            P.Options:=Options - [poRunSuspended,poWaitOnExit];
          p.options:=p.options+[poRunIdle];
        
          P.ShowWindow:=SwOptions;
          p.Executable:=exename;
          if high(commands)>=0 then
           for i:=low(commands) to high(commands) do
             p.Parameters.add(commands[i]);
          p.fmemo:=memo;
          p.OnRunCommandEvent:=runrefresh;
          try
            result:=p.RunCommandLoop(outputstring,errorstring,exitstatus)=0;
          finally
            p.free;
          end;
          if exitstatus<>0 then result:=false;
        end;
        
        { TForm1 }
        
        procedure TForm1.Button1Click(Sender: TObject);
        var s : string;
        begin
        //RunCommandMemo('testit',[],s,[],swonone,memo1,ProcessEvent);
          RunCommandMemo('cmd.exe',['/w','/c','ver'],s,[],swonone,memo1,ProcessEvent);
        end;
        
        procedure TForm1.ProcessEvent(Sender, Context: TObject;
          Status: TRunCommandEventCode; const Message: string);
        begin
          if status in [RunCommandIdle, RunCommandFinished] then
            begin
              if status =RunCommandFinished then
                begin
                  memo1.lines.add(' process finished');
                end;
              if tprocesstomemo(sender).fstringsadded>0 then
               begin
                 tprocesstomemo(sender).fstringsadded:=0;
        //         memo1.lines.add('Handle:'+inttostr(tprocesstomemo(sender).ProcessHandle));
                 memo1.refresh;
               end;
              sleep(10);
              application.ProcessMessages;
            end;
        end;
        
        { TProcessToMemo }
        
        
        function TProcessToMemo.ReadInputStream(p:TInputPipeStream;var BytesRead:integer;var DataLength:integer;var Data:string;MaxLoops:integer=10):boolean;
        var lfpos : integer;
            crcorrectedpos:integer;
            stradded : integer;
            newstr : string;
        begin
          Result:=inherited ReadInputStream(p, BytesRead, DataLength, data, MaxLoops);
          if (result) and (bytesread>bytesprocessed)then
            begin
              stradded:=0;
              lfpos:=pos(#10,data,bytesprocessed+1);
              while (lfpos<>0) and (lfpos<=bytesread) do
                begin
                  crcorrectedpos:=lfpos;
                  if (crcorrectedpos>0) and (data[crcorrectedpos-1]=#13) then
                     dec(crcorrectedpos);
                  newstr:=copy(data,bytesprocessed+1,crcorrectedpos-bytesprocessed-1);
                  fmemo.lines.add(newstr);
                   inc(stradded);
                  bytesprocessed:=lfpos;
                  lfpos:=pos(#10,data,bytesprocessed+1);
                end;
              inc(fstringsadded,stradded); // check idle event.
            end;
        end;
        
        end.
    

    I don't know minecraft server, and many external programs might do weird things to the console. But a simple combination of programs to test with is here http://www.stack.nl/~marcov/files/processmemodemo.zip