5

Delphi を使用して、コマンド プロンプトにコマンドを送信しようとしました。ただし、CreateProcessメソッドを使用してそれを行うことができません。StdOutPipeWrite を変更しようとしましたが、CreateProcess は、CreateProcess からの最初のコマンドの後にコマンドを渡すことを許可していないようです。コマンド プロンプトと Delphi の間でコマンドとメッセージを送受信し続けるためにハンドルを利用する方法はありますか?

4

2 に答える 2

10

tek-tips.comの仲間のGlenn9999が、このテーマに関するすばらしいFAQを作成しました。彼がSOに参加しているかどうかはわかりませんが、彼はこれにすべての功績を認めるに値します。後で参照できるように、このページからコードをコピーしました。彼はパイプを使用してコンソールとデルファイの間の通信を行います。

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.

更新(2020年5月1日)

この回答は、Unicodeに対応していないDelphiバージョンでのみ機能します。最新のDelphiをお持ちの場合は、ここで動作するバージョンを見つけることができます

于 2012-05-15T13:01:50.247 に答える
-3

最初に用途を宣言します。

ShellAPI

次に、これを使用します。

ShellExecute(0, nil, 'cmd.exe', '/c **YOUR_COMMAND_HERE**', nil, HIDE_WINDOW);
于 2015-10-01T22:58:21.873 に答える