2012-05-15 2 views
4

나는 명령 프롬프트에 명령을 보내기 위해 delphi를 사용하려고했습니다. 그러나 CreateProcess 메서드를 사용하여 그렇게 할 수 없습니다. StdOutPipeWrite를 변경하려고 시도했지만 CreateProcess에서 전달 된 초기 명령 이후에 CreateProcess가 명령을 허용하지 않는 것으로 보입니다. 핸들을 사용하여 명령 프롬프트와 delphi를주고받는 명령과 메시지를 계속 보내고받을 수있는 방법이 있습니까?델파이를 통한 명령 프롬프트와의 통신

+1

당신은 당신이 cmd.exe를 인스턴스에 실행 할 명령을 전달하는/C 옵션을 필요로 수 CreateProcess로 시작합니다. 자세한 내용은 명령 창을 열고'help cmd'를 입력하십시오. 그러나이 명령은 하나의 명령 만 전달할 수 있습니다. 여러 명령을 실행해야하는 경우 일괄 처리 파일을 작성하여 실행 명령으로 전달할 수 있습니다. –

답변

9

내 동료 Glenn9999 tek-tips.com wrote a nice FAQ on this subject. 그 사람이 그 사람인지 잘 모르겠지만, 그는이 사람에 대한 모든 것을 인정받을 자격이 있습니다. 나중에 참조 할 수 있도록 해당 페이지의 코드를 복사했습니다. 그는 파이프를 사용하여 콘솔과 델파이 간의 통신을합니다.

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. 
-2

먼저 사용에 선언

ShellAPI 

그런 다음이 사용

ShellExecute(0, nil, 'cmd.exe', '/c **YOUR_COMMAND_HERE**', nil, HIDE_WINDOW); 
+1

이 질문은 초기 명령이 이미 성공적으로 전달되었다고 가정하므로이 항목이 더 이상 보이지 않습니다. –