Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pipes in Delphi for Command Prompt

How can I get Delphi to pass a string to the input pipe to a CMD process. I am able to get an error pipe and output pipe functioning properly, unfortunately not the input pipe. The code I am using is taken from an online tutorial for piping. There were several errors in the original code causing problems when it was compiled. They have been fixed but I am left with problems when trying to pass input still.

Here is the code in the Form.Create event. I also have included the WritePipe and ReadPipe methods. WritePipe does not work, ReadPipe does work. Both WriteFile and ReadFile in the Pipe methods return a successful message, only the ReadPipe actually works however.

var
    DosApp: String;
    DosSize: Integer;
    Security : TSecurityAttributes;
    start : TStartUpInfo;
    byteswritten: DWord;
    WriteString : ansistring;
  begin
    CommandText.Clear;
    // get COMSPEC variable, this is the path of the command-interpreter
    SetLength(Dosapp, 255);
    DosSize := GetEnvironmentVariable('COMSPEC', @DosApp[1], 255);
    SetLength(Dosapp, DosSize);

  // create pipes
    With Security do
      begin
        nlength := SizeOf(TSecurityAttributes) ;
        binherithandle := true;
        lpsecuritydescriptor := nil;
      end;

    CreatePipe(InputPipeRead, InputPipeWrite, @Security, 0);
    CreatePipe(OutputPipeRead, OutputPipeWrite, @Security, 0);
    CreatePipe(ErrorPipeRead, ErrorPipeWrite, @Security, 0);

    // start command-interpreter
    FillChar(Start,Sizeof(Start),#0) ;

    //start.hStdInput := InputPipeRead;
    start.hStdOutput := OutputPipeWrite;
    start.hStdError :=  ErrorPipeWrite;

    start.dwFlags := STARTF_USESTDHANDLES + STARTF_USESHOWWINDOW;
    start.wShowWindow := SW_Show;//SW_HIDE;
    start.cb := SizeOf(start) ;

    if CreateProcess('', PChar(DosApp), @Security, @Security, true,
               CREATE_NEW_CONSOLE or SYNCHRONIZE, // CREATE_NO_WINDOW,
               nil, nil, start, ProcessInfo) then
      begin
        MyThread := MainUnit.monitor.Create;  // start monitor thread
        MyThread.Priority := tpHigher;
      end;

    Button1.Enabled := true;
    cmdcount := 1;

end;

Write Pipe:

procedure WritePipeOut(OutputPipe: THandle; InString: PWideChar);
// writes Instring to the pipe handle described by OutputPipe
  var
    count : integer;
    byteswritten: DWord;
    outputstring : PAnsiChar;
    TextBuffer: array[1..32767] of AnsiChar;// char;
    TextString: String;
  begin

// most console programs require CR/LF after their input.

    InString := PWideChar(InString + #13#10);

    WriteFile(InputPipeWrite, InString[1], Length(InString), byteswritten, nil);

  end;

Read Pipe:

function ReadPipeInput(InputPipe: THandle; var BytesRem: Integer): String;
  {
    reads console output from InputPipe.  Returns the input in function
    result.  Returns bytes of remaining information to BytesRem
  }
  var
    TextBuffer: array[1..32767] of AnsiChar;// char;
    TextString: String;
    BytesRead: Cardinal;
    PipeSize: Integer;
  begin
    Result := '';
    PipeSize := length(TextBuffer);
    // check if there is something to read in pipe
    PeekNamedPipe(InputPipe, nil, PipeSize, @BytesRead, @PipeSize, @BytesRem);
    if bytesread > 0 then
      begin
        ReadFile(InputPipe, TextBuffer, pipesize, bytesread, nil);
        // a requirement for Windows OS system components
        OemToChar(@TextBuffer, @TextBuffer);
        TextString := String(TextBuffer);
        SetLength(TextString, BytesRead);
        Result := TextString;
      end;
  end;

Further note; this is for use with the Java Debugger, which requires input in stages and so I do not believe there is any alternative method other than manipulating input directly to the JDB.

Any help is much appreciated!

like image 814
James_Hill Avatar asked Apr 25 '13 21:04

James_Hill


1 Answers

1) You should pass InputPipeRead as hStdInput into CreateProcess: uncomment your line start.hStdInput := InputPipeRead;

2) The WritePipeOut function has two errors: it writes a Unicode (UTF-16LE) string into a pipe, and it skips the first character (since it writes a memory area beginning at InString[1]). Instead of WriteFile(InputPipeWrite, InString[1], Length(InString),... you should write something like:

var AnsiBuf: AnsiString;
...
  AnsiBuf := String(InString) + #13#10;
  Write(InputPipeWrite, AnsiBuf[1], Length(AnsiBuf), byteswritten, nil);
like image 195
nullptr Avatar answered Nov 10 '22 15:11

nullptr