to skyweb:<br> 我昨天自己试了一下,也没弄明白。我对用管道技术操纵控制台程序研究得并不深,只是以前用<br> C++Builder写了一个UPX的Windows外壳,下面这个帖子中就是我的那段C++代码,我之所以这么关心<br> 你这个题目,是因为我对这方面挺感兴趣的<br> 下面是那个帖子中xianjun给我发过来的代码,由于工程文件损坏了,打不开,代码又不多,只有<br> 两个单元文件,我就都给你贴上来了,希望你早日成功!<br> <br>//--------------------------------------------------------------------------------------<br>unit Main;<br><br>interface<br><br>uses<br> Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,<br> StdCtrls, uRedirect, ComCtrls;<br><br>type<br> TForm1 = class(TForm)<br> Memo1: TRichEdit;<br> Edit1: TEdit;<br> procedure Edit1KeyPress(Sender: TObject; var Key: Char);<br> procedure FormClose(Sender: TObject; var Action: TCloseAction);<br> procedure FormShow(Sender: TObject);<br> private<br> { Private declarations }<br> FRedirector: TRedirector;<br> procedure NewData(Sender: TRedirector; Buffer: Pointer; BufferSize:<br> Integer);<br> public<br> { Public declarations }<br> end;<br><br>var<br> Form1: TForm1;<br><br>implementation<br><br>{$R *.DFM}<br><br>procedure TForm1.NewData(Sender: TRedirector; Buffer: Pointer;<br> BufferSize: Integer);<br>var<br> Temp: PChar;<br>begin<br> Temp := StrAlloc(BufferSize + 1);<br> try<br> StrLCopy(Temp, Buffer, BufferSize);<br> Temp[BufferSize] := #0;<br> Memo1.Lines.Add(string(Temp));<br> Memo1.Perform(WM_VSCROLL, SB_BOTTOM, 0);<br> finally<br> StrDispose(Temp);<br> end;<br>end;<br><br>procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);<br>var<br> cmd: string;<br>begin<br> if Key = #13 then<br> begin<br> cmd := Edit1.Text;<br> if cmd = 'NEWCMD' then<br> begin<br> if FRedirector <> nil then<br> FRedirector.SendText('exit');<br> FreeAndNil(FReDirector);<br> end;<br> if FRedirector = nil then<br> begin<br> FRedirector := TRedirector.Create;<br> FRedirector.KillOnDestroy := True;<br> FRedirector.OnData := NewData;<br> FRedirector.CommandLine := 'cmd';<br> FRedirector.Directory := 'C:/';<br> FRedirector.Execute;<br> FRedirector.SendText('请不要使用需要用户介入的DOS命令(如Edit)'#13#10'否则会死得很难看的! --- XJG在此忠告市民'#10#13);<br> end;<br> if UpperCase(cmd) <> 'EXIT' then<br> FRedirector.SendText(cmd + #10#13);<br> Edit1.SelectAll;<br> end;<br>end;<br><br>procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);<br>begin<br> if Assigned(FRedirector) then<br> FRedirector.Free;<br>end;<br><br>procedure TForm1.FormShow(Sender: TObject);<br>var<br> Key: Char;<br>begin<br> Key := #13;<br> Edit1KeyPress(Edit1, Key);<br> Edit1.Text := '请在此输入你要执行的DOS命令';<br>end;<br><br>end.<br><br>//--------------------------------------------------------------------------------------<br>//****************************************************************************<br>//* I have no idea who wrote this unit, if somebody knows drop me a line and *<br>//* I will credit accordingly. *<br>//* *<br>//* Unit was taken from http://www.delphidevelopers.com *<br>//****************************************************************************<br>{***************************************************************<br> *<br> * History: Modified by xjg.<br> * 参见:http://support.microsoft.com/support/kb/articles/Q190/3/51.ASP<br> *<br> ****************************************************************}<br><br>unit uRedirect;<br><br>interface<br><br>uses<br> Windows, SysUtils, Classes;<br><br>type<br> TRedirector = class;<br> TPriorityClass = (pcDefault, pcIdle, pcNormal, pcHigh, pcRealtime);<br> TDataEvent = procedure(Sender: TRedirector; buffer: pointer; Size: integer) of<br> object;<br><br> TPipeError = record<br> hRead,<br> hWrite: DWORD;<br> end;<br><br> TRedirector = class<br> private<br> FAvailable: integer;<br> procedure ReadStdOutput;<br> procedure ReadStdError;<br> procedure ProcessTerminated;<br> protected<br> FProcessInfo: TProcessInformation;<br> FExitCode: integer;<br> FExecutable: string;<br> FCommandline: string;<br> FDefaultErrorMode: boolean;<br> FStartSuspended: boolean;<br> FKillOnDestroy: boolean;<br> FDirectory: string;<br> FEnvironment: pointer;<br> FInitialPriority: TPriorityClass;<br> FPipeInput,<br> FPipeOutput,<br> FPipeError: TPipeError;<br> FThread: TThread;<br> FOnData,<br> FOnErrorData: TDataEvent;<br> FOnTerminated: TNotifyEvent;<br> FShowWindow: integer;<br> procedure Error(msg: string);<br> procedure WinError(msg: string);<br> procedure CreatePipes;<br> procedure ClosePipes;<br> function GetRunning: boolean;<br> function GetExitCode: integer;<br> function GetProcessID: integer;<br> function GetThreadID: integer;<br> function GetProcessHandle: integer;<br> procedure SetShowWindow(value: integer);<br> function GetThreadHandle: integer;<br> procedure SetExecutable(value: string);<br> function GetCommandLine: string;<br> procedure SetCommandLine(value: string);<br> procedure SetDefaultErrorMode(value: boolean);<br> procedure SetStartSuspended(value: boolean);<br> procedure SetInitialPriority(value: TPriorityClass);<br> procedure SetDirectory(value: string);<br> procedure SetEnvironment(value: pointer);<br> property ProcessHandle: integer read GetProcessHandle;<br> property ThreadHandle: integer read GetThreadHandle;<br> public<br> destructor Destroy; override;<br> procedure Terminate(dwExitCode: integer);<br> procedure Execute;<br> procedure SendData(Buffer: pointer; BufferSize: integer);<br> procedure SendText(s: string);<br> property Running: boolean read GetRunning;<br> property ExitCode: integer read GetExitCode;<br> property ProcessID: integer read GetProcessID;<br> property ThreadID: integer read GetThreadID;<br> property Environment: pointer read FEnvironment write SetEnvironment;<br> published<br> property KillOnDestroy: boolean read FKillOnDestroy write FKillOnDestroy;<br> property Executable: string read FExecutable write SetExecutable;<br> property CommandLine: string read GetCommandLine write SetCommandLine;<br> property ShowWindow: integer read FShowWindow write SetShowWindow default<br> SW_SHOWDEFAULT;<br> property DefaultErrorMode: boolean read FDefaultErrorMode write<br> SetDefaultErrorMode;<br> property StartSuspended: boolean read FStartSuspended write<br> SetStartSuspended;<br> property InitialPriority: TPriorityClass read FInitialPriority write<br> SetInitialPriority;<br> property Directory: string read FDirectory write SetDirectory;<br> property OnData: TDataEvent read FOnData write FOnData;<br> property OnErrorData: TDataEvent read FOnErrorData write FOnErrorData;<br> property OnTerminated: TNotifyEvent read FOnTerminated write FOnTerminated;<br> end;<br><br>implementation<br><br>const<br> DUPLICATE_CLOSE_SOURCE = 1;<br> DUPLICATE_SAME_ACCESS = 2;<br><br>type<br> TRedirectorThread = class(TThread)<br> protected<br> FRedirector: TRedirector;<br> procedure Execute; override;<br> constructor Create(ARedirector: TRedirector);<br> end;<br><br> ////////////////////////////////////////////////////////////////////////////////<br> // Misc. internal methods<br> ////////////////////////////////////////////////////////////////////////////////<br><br>procedure TRedirector.Error(msg: string);<br>begin<br> TerminateProcess(ProcessHandle, 0);<br> raise Exception.Create(msg);<br>end;<br><br>procedure TRedirector.WinError(msg: string);<br>begin<br> Error(msg + IntToStr(GetLastError));<br>end;<br><br>procedure TRedirector.CreatePipes;<br>var<br> SecAttr: TSecurityAttributes;<br>begin<br> SecAttr.nLength := SizeOf(SecAttr);<br> SecAttr.lpSecurityDescriptor := nil;<br> SecAttr.bInheritHandle := TRUE;<br><br> with FPipeInput do<br> begin<br> if not CreatePipe(hRead, hWrite, @SecAttr, 1024) then<br> WinError('Error on STDIN pipe creation : ');<br> if not DuplicateHandle(GetCurrentProcess, hRead, GetCurrentProcess,<br> @hRead, 0, TRUE, DUPLICATE_CLOSE_SOURCE or DUPLICATE_SAME_ACCESS) then<br> WinError('Error on STDIN pipe duplication : ');<br> end;<br> with FPipeOutput do<br> begin<br> if not CreatePipe(hRead, hWrite, @SecAttr, 1024) then<br> WinError('Error on STDOUT pipe creation : ');<br> if not DuplicateHandle(GetCurrentProcess, hWrite, GetCurrentProcess,<br> @hWrite, 0, TRUE, DUPLICATE_CLOSE_SOURCE or DUPLICATE_SAME_ACCESS) then<br> WinError('Error on STDOUT pipe duplication : ');<br> end;<br> with FPipeError do<br> begin<br> if not CreatePipe(hRead, hWrite, @SecAttr, 1024) then<br> WinError('Error on STDERR pipe creation : ');<br> if not DuplicateHandle(GetCurrentProcess, hWrite, GetCurrentProcess,<br> @hWrite, 0, TRUE, DUPLICATE_CLOSE_SOURCE or DUPLICATE_SAME_ACCESS) then<br> WinError('Error on STDERR pipe duplication : ');<br> end;<br>end;<br><br>procedure TRedirector.ClosePipes;<br>begin<br> with FPipeInput do<br> begin<br> if hRead <> 0 then<br> CloseHandle(hRead);<br> if hWrite <> 0 then<br> CloseHandle(hWrite);<br> hRead := 0;<br> hWrite := 0;<br> end;<br> with FPipeOutput do<br> begin<br> if hRead <> 0 then<br> CloseHandle(hRead);<br> if hWrite <> 0 then<br> CloseHandle(hWrite);<br> hRead := 0;<br> hWrite := 0;<br> end;<br> with FPipeError do<br> begin<br> if hRead <> 0 then<br> CloseHandle(hRead);<br> if hWrite <> 0 then<br> CloseHandle(hWrite);<br> hRead := 0;<br> hWrite := 0;<br> end;<br>end;<br><br>////////////////////////////////////////////////////////////////////////////////<br>// Property implementations<br>////////////////////////////////////////////////////////////////////////////////<br><br>function TRedirector.GetRunning: boolean;<br>begin<br> Result := ProcessHandle <> 0;<br>end;<br><br>function TRedirector.GetExitCode: integer;<br>begin<br> if Running then<br> Result := STILL_ACTIVE<br> else<br> Result := FExitCode;<br>end;<br><br>function TRedirector.GetProcessID: integer;<br>begin<br> Result := FProcessInfo.dwProcessID;<br>end;<br><br>function TRedirector.GetThreadID: integer;<br>begin<br> Result := FProcessInfo.dwThreadID;<br>end;<br><br>function TRedirector.GetProcessHandle: integer;<br>begin<br> Result := FProcessInfo.hProcess;<br>end;<br><br>function TRedirector.GetThreadHandle: integer;<br>begin<br> Result := FProcessInfo.hThread;<br>end;<br><br>procedure TRedirector.SetExecutable(value: string);<br>begin<br> if (ANSICompareText(value, Executable) = 0) or not Running then<br> FExecutable := value<br> else if Running then<br> Error('Cannot change Executable while process is active');<br>end;<br><br>procedure TRedirector.SetCommandLine(value: string);<br>begin<br> if (ANSICompareText(value, Commandline) = 0) or not Running then<br> FCommandline := value<br> else if Running then<br> Error('Cannot change Commandline while process is active');<br>end;<br><br>function TRedirector.GetCommandLine: string;<br>begin<br> Result := FExecutable;<br> if Result = '' then<br> Result := FCommandline<br> else<br> Result := FExecutable + ' ' + FCommandline;<br>end;<br><br>procedure TRedirector.SetDefaultErrorMode(value: boolean);<br>begin<br> if (value = DefaultErrorMode) or not Running then<br> FDefaultErrorMode := value<br> else if Running then<br> Error('Cannot change DefaultErrorMode while process is active');<br>end;<br><br>procedure TRedirector.SetStartSuspended(value: boolean);<br>begin<br> if (value = DefaultErrorMode) or not Running then<br> FStartSuspended := value<br> else if Running then<br> Error('Cannot change StartSuspended while process is active');<br>end;<br><br>procedure TRedirector.SetInitialPriority(value: TPriorityClass);<br>begin<br> if (value = InitialPriority) or not Running then<br> FInitialPriority := value<br> else if Running then<br> Error('Cannot change InititalPriority while process is active');<br>end;<br><br>procedure TRedirector.SetDirectory(value: string);<br>begin<br> if (ANSICompareText(value, Directory) = 0) or (not Running) then<br> FDirectory := value<br> else if Running then<br> Error('Cannot change Directory while process is active');<br>end;<br><br>procedure TRedirector.SetEnvironment(value: pointer);<br>begin<br> if (value = Environment) or not Running then<br> FEnvironment := value<br> else if Running then<br> Error('Cannot change Environment while process is active');<br>end;<br><br>procedure TRedirector.SetShowWindow(value: integer);<br>begin<br> if (value = ShowWindow) or not Running then<br> FShowWindow := value<br> else if Running then<br> Error('Cannot change ShowWindow while process is active');<br>end;<br><br>procedure TRedirector.ReadStdOutput;<br>var<br> BytesRead: DWORD;<br> buffer: pointer;<br>begin<br> GetMem(Buffer, FAvailable);<br> try<br> if not ReadFile(FPipeOutput.hRead, buffer^, FAvailable, BytesRead, nil) then<br> begin<br> FThread.Terminate;<br> WinError('Error reading STDOUT pipe : ');<br> end;<br> if Assigned(FOnData) then<br> begin<br> FOnData(Self, buffer, BytesRead);<br> end;<br> finally<br> FreeMem(buffer);<br> end;<br>end;<br><br>procedure TRedirector.ReadStdError;<br>var<br> BytesRead: DWORD;<br> buffer: pointer;<br>begin<br> GetMem(Buffer, FAvailable);<br> try<br> if not ReadFile(FPipeError.hRead, buffer^, FAvailable, BytesRead, nil) then<br> begin<br> FThread.Terminate;<br> WinError('Error reading STDERR pipe : ');<br> end;<br> if Assigned(FOnErrorData) then<br> begin<br> FOnErrorData(Self, buffer, BytesRead);<br> end;<br> finally<br> FreeMem(buffer);<br> end;<br>end;<br><br>procedure TRedirector.ProcessTerminated;<br>begin<br> FThread.Terminate;<br> if Assigned(FOnTerminated) then<br> FOnTerminated(Self);<br> ClosePipes;<br> CloseHandle(FProcessInfo.hProcess);<br> CloseHandle(FProcessInfo.hThread);<br> FillChar(FProcessInfo, SizeOf(FProcessInfo), 0);<br>end;<br><br>////////////////////////////////////////////////////////////////////////////////<br>// Public methods<br>////////////////////////////////////////////////////////////////////////////////<br><br>procedure TRedirector.Terminate(dwExitCode: integer);<br>begin<br> if Running then<br> TerminateProcess(ProcessHandle, dwExitCode)<br> else<br> Error('Cannot Terminate an inactive process');<br>end;<br><br>procedure TRedirector.Execute;<br>var<br> StartupInfo: TStartupInfo;<br> szExecutable,<br> szCommandline,<br> szDirectory: PChar;<br>begin<br> if Running then<br> Error('Process is already active');<br> if Trim(CommandLine) = '' then<br> Error('No commandline to run');<br> try<br> CreatePipes;<br><br> FillChar(StartupInfo, SizeOf(StartupInfo), 0);<br> StartupInfo.cb := SizeOf(StartupInfo);<br><br> StartupInfo.wShowWindow := FShowWindow;<br> StartupInfo.hStdInput := FPipeInput.hRead;<br> StartupInfo.hStdOutput := FPipeOutput.hWrite;<br> StartupInfo.hStdError := FPipeError.hWrite;<br> StartupInfo.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;<br><br> if Trim(Executable) = '' then<br> szExecutable := nil<br> else<br> szExecutable := PChar(FExecutable);<br> if Trim(Commandline) = '' then<br> szCommandline := nil<br> else<br> szCommandline := PChar(FCommandline);<br> if Trim(Directory) = '' then<br> szDirectory := nil<br> else<br> szDirectory := PChar(FDirectory);<br> if CreateProcess(<br> szExecutable,<br> szCommandline,<br> nil,<br> nil,<br> TRUE,<br> (CREATE_DEFAULT_ERROR_MODE and integer(FDefaultErrorMode))<br> or (CREATE_SUSPENDED and integer(FStartSuspended)),<br> Environment,<br> szDirectory,<br> StartupInfo,<br> FProcessInfo) then<br> begin<br> //WaitForSingleObject(FProcessInfo.hProcess, 5000);<br> FThread := TRedirectorThread.Create(Self);<br> end<br> else<br> WinError('Error creating process : ');<br> except<br> on Exception do<br> begin<br> ClosePipes;<br> CloseHandle(FProcessInfo.hProcess);<br> CloseHandle(FProcessInfo.hThread);<br> FillChar(FProcessInfo, SizeOf(FProcessInfo), 0);<br> raise;<br> end;<br> end;<br>end;<br><br>procedure TRedirector.SendData(Buffer: pointer; BufferSize: integer);<br>var<br> BytesWritten: DWORD;<br>begin<br> if not Running then<br> Error('Can''t send data to an inactive process');<br> if not WriteFile(FPipeInput.hWrite, Buffer^, BufferSize, BytesWritten, nil)<br> then<br> WinError('Error writing to STDIN pipe : ');<br>end;<br><br>procedure TRedirector.SendText(s: string);<br>begin<br> SendData(PChar(s), Length(s));<br>end;<br><br>destructor TRedirector.Destroy;<br>begin<br> if Running and KillOnDestroy then<br> begin<br> FOnTerminated := nil;<br> FThread.Terminate;<br> Terminate(0);<br> end;<br> inherited Destroy;<br>end;<br><br>constructor TRedirectorThread.Create(ARedirector: TRedirector);<br>begin<br> FRedirector := ARedirector;<br> inherited Create(FALSE);<br>end;<br><br>procedure TRedirectorThread.Execute;<br>var<br> Idle: boolean;<br>begin<br> FreeOnTerminate := TRUE;<br> while not Terminated do<br> begin<br> Idle := TRUE;<br> if PeekNamedPipe(FRedirector.FPipeOutput.hRead, nil, 0, nil,<br> @FRedirector.FAvailable, nil) and (FRedirector.FAvailable > 0) then<br> begin<br> Synchronize(FRedirector.ReadStdOutput);<br> Idle := FALSE;<br> end;<br> if PeekNamedPipe(FRedirector.FPipeError.hRead, nil, 0, nil,<br> @FRedirector.FAvailable, nil) and (FRedirector.FAvailable > 0) then<br> begin<br> Synchronize(FRedirector.ReadStdError);<br> Idle := FALSE;<br> end;<br> if Idle and (WaitForSingleObject(FRedirector.ProcessHandle,<br> 100) = WAIT_OBJECT_0) then<br> begin<br> if not Terminated then<br> Synchronize(FRedirector.ProcessTerminated);<br> end;<br> end;<br>end;<br><br>end.<br><br>//--------------------------------------------------------------------------------------<br>