I have tried to use delphi to send commands to the command prompt. However, i am not able to do so as i used CreateProcess method to do it. I have tried to change the StdOutPipeWrite, however, the CreateProcess seems to not allow commands after the initial command from CreateProcess to be passed through. Is there any way to make use of the handle to continue to send and receive commands and messages to and fro the command prompt and delphi?
可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
回答1:
My fellow member Glenn9999 from tek-tips.com wrote a nice FAQ on this subject. I don't know if he's on SO, but he deserves all the credit for this one. I copied the code from that page here for future reference. He uses pipes to do the communication between console and delphi.
unit mcunit;
{ written by Glenn9999 @ tek-tips.com. Posted here 6/21/2011 }
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
monitor = class(TThread) // pipe monitoring thread for console output
private
TextString: String;
procedure UpdateCaption;
protected
procedure Execute; override;
end;
TForm1 = class(TForm)
CommandText: TMemo;
CommandRun: TComboBox;
Button2: TButton;
SaveDialog1: TSaveDialog;
procedure FormDestroy(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
cmdcount: integer;
end;
var
Form1: TForm1;
InputPipeRead, InputPipeWrite: THandle;
OutputPipeRead, OutputPipeWrite: THandle;
ErrorPipeRead, ErrorPipeWrite: THandle;
ProcessInfo : TProcessInformation;
myThread: monitor;
implementation
{$R *.DFM}
procedure WritePipeOut(OutputPipe: THandle; InString: string);
// writes Instring to the pipe handle described by OutputPipe
var
byteswritten: DWord;
begin
// most console programs require CR/LF after their input.
InString := InString + #13#10;
WriteFile(OutputPipe, Instring[1], Length(Instring), byteswritten, nil);
end;
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 char;
TextString: String;
BytesRead: Integer;
PipeSize: Integer;
begin
Result := '';
PipeSize := Sizeof(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;
procedure monitor.Execute;
{ monitor thread execution for console output. This must be threaded.
checks the error and output pipes for information every 40 ms, pulls the
data in and updates the memo on the form with the output }
var
BytesRem: DWord;
begin
while not Terminated do
begin
// read regular output stream and put on screen.
TextString := ReadPipeInput(OutputPipeRead, BytesRem);
if TextString <> '' then
Synchronize(UpdateCaption);
// now read error stream and put that on screen.
TextString := ReadPipeInput(ErrorPipeRead, BytesRem);
if TextString <> '' then
Synchronize(UpdateCaption);
sleep(40);
end;
end;
procedure monitor.UpdateCaption;
// synchronize procedure for monitor thread - updates memo on form.
begin
With Form1.CommandText.Lines do
Add(TextString);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
WritePipeOut(InputPipeWrite, 'EXIT'); // quit the CMD we started
MyThread.Terminate;
// close process handles
CloseHandle(ProcessInfo.hProcess);
CloseHandle(ProcessInfo.hThread);
// close pipe handles
CloseHandle(InputPipeRead);
CloseHandle(InputPipeWrite);
CloseHandle(OutputPipeRead);
CloseHandle(OutputPipeWrite);
CloseHandle(ErrorPipeRead);
CloseHandle(ErrorPipeWrite);
end;
procedure TForm1.Button2Click(Sender: TObject);
{ takes the input from the command edit box and processes it }
var
UpText: String;
begin
UpText := UpperCase(CommandRun.Text); // done to eliminate case-sensitivity
if UpText = 'CLR' then // clear the memo
begin
CommandText.Clear;
WritePipeOut(InputPipeWrite, #13);
end
else
if UpText = 'SAVELOG' then // save the memo box to a file.
begin
if SaveDialog1.Execute then
begin
CommandText.Lines.SaveToFile(SaveDialog1.FileName);
CommandText.Lines.Add('Log file saved.');
end
else
CommandText.Lines.Add('Log file not saved.');
end
// expand this, it needs to catch any variation where the command-interpreter
// is called. Any different ideas?
else
if UpText = 'CMD' then
inc(cmdcount)
else
if UpText = 'COMMAND' then
inc(cmdcount)
// terminate app if user types exit, else let alone
else
if UpText = 'EXIT' then
begin
if cmdcount = 1 then
Application.Terminate
else
dec(cmdcount);
end
else
WritePipeOut(InputPipeWrite, CommandRun.Text);
CommandRun.Items.Add(CommandRun.Text);
CommandRun.Text := '';
CommandRun.SetFocus;
end;
procedure TForm1.FormCreate(Sender: TObject);
{ upon form creation, this calls the command-interpreter, sets up the three
pipes to catch input and output, and starts a thread to monitor and show
the output of the command-interpreter }
var
DosApp: String;
DosSize: Integer;
Security : TSecurityAttributes;
start : TStartUpInfo;
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.cb := SizeOf(start) ;
start.hStdInput := InputPipeRead;
start.hStdOutput := OutputPipeWrite;
start.hStdError := ErrorPipeWrite;
start.dwFlags := STARTF_USESTDHANDLES + STARTF_USESHOWWINDOW;
start.wShowWindow := SW_HIDE;
if CreateProcess(nil, PChar(DosApp), @Security, @Security, true,
CREATE_NEW_CONSOLE or SYNCHRONIZE,
nil, nil, start, ProcessInfo) then
begin
MyThread := monitor.Create(false); // start monitor thread
MyThread.Priority := tpHigher;
end;
Button2.Enabled := true;
cmdcount := 1;
end;
end.
UPDATE (05/01/2020)
This answer only works on non unicode aware Delphi versions. You can find a working version here if you have a modern Delphi
回答2:
First declare on uses:
ShellAPI
Then use this:
ShellExecute(0, nil, 'cmd.exe', '/c **YOUR_COMMAND_HERE**', nil, HIDE_WINDOW);