Pipes in Delphi for Command Prompt
cmd, command-line-arguments, console, delphi, pipe
Solution
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);
Problem
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!