1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
|
unit VirtualCommandLine;
// Version 1.2.0
// The contents of this file are subject to the Mozilla Public License
// Version 1.1 (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the
// License at
//
// http://www.mozilla.org/MPL/
//
// Software distributed under the License is distributed on an
// " AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either expressed or
// implied. See the License for the specific language governing rights
// and limitations under the License.
//
//
// Alternatively, the contents of this file may be used under
// the terms of the GNU General Public License Version 2 or later
// (the "GPL"), in which case the provisions of the GPL are applicable
// instead of those above. If you wish to allow use of your version of
// this file only under the terms of the GPL and not to allow others to
// use your version of this file under the MPL, indicate your decision
// by deleting the provisions above and replace them with the notice and
// other provisions required by the GPL. If you do not delete the provisions
// above, a recipient may use your version of this file under either the
// MPL or the GPL.
//
// The initial developer of this code is Jim Kueneman <jimdk@mindspring.com>
//
//----------------------------------------------------------------------------
interface
{$include ..\Include\Compilers.inc}
{$include ..\Include\VSToolsAddIns.inc}
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, ImgList, ShlObj,
ShellAPI, ActiveX;
type
TCommandLinePipe = class(TComponent)
private
// | ------------------------------------------- |
// | hPipe2WriteDuplicate ----> hPipe2Read |
// Current Process | | Child Process
// | hPipe1ReadDuplicate <---- hPipe1Write |
// | ------------------------------------------- |
FhPipe2Read: THandle;
FhPipe1Write: THandle;
FhPipe1ReadDuplicate: THandle;
FhPipe2WriteDuplicate: THandle;
FMemStream: TMemoryStream;
protected
property hPipe1Write: THandle read FhPipe1Write write FhPipe1Write;
property hPipe1ReadDuplicate: THandle read FhPipe1ReadDuplicate write FhPipe1ReadDuplicate;
property hPipe2Read: THandle read FhPipe2Read write FhPipe2Read;
property hPipe2WriteDuplicate: THandle read FhPipe2WriteDuplicate write FhPipe2WriteDuplicate;
property MemStream: TMemoryStream read FMemStream write FMemStream;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure DOSCommand(Command: string);
procedure Initialize;
procedure ReadFrom(Stream: TMemoryStream);
function ReadResult: string;
procedure SendTo(Stream: TMemoryStream);
end;
implementation
{ TCommandLinePipe }
constructor TCommandLinePipe.Create(AOwner: TComponent);
begin
inherited;
MemStream := TMemoryStream.Create;
end;
destructor TCommandLinePipe.Destroy;
begin
MemStream.Free;
inherited;
end;
procedure TCommandLinePipe.Initialize;
var
hOldSTDOut, // Handle associated with the Pipe attached to
hPipe1Read, // (Current Process ---> Child Process
hOldSTDIn, // Handle associated with the Pipe attached to
hPipe2Write: THandle;
SecurityAttribs: TSecurityAttributes;
StartupInfo: TStartupInfo;
ProcessInformation: TProcessInformation;
begin
// The steps for redirecting child process's STDOUT:
// 1. Save current STDOUT, to be restored later.
// 2. Create anonymous pipe to be STDOUT for child process.
// 3. Set STDOUT of the parent process to be write handle to
// the pipe, so it is inherited by the child process.
// 4. Create a noninheritable duplicate of the read handle and
// close the inheritable read handle.
// Set the bInheritHandle flag so pipe handles are inherited.
SecurityAttribs.nLength := sizeof(SECURITY_ATTRIBUTES);
SecurityAttribs.bInheritHandle := True;
SecurityAttribs.lpSecurityDescriptor := Nil;
// Save the handle to the current STDOUT.
hOldSTDOut := GetStdHandle(STD_OUTPUT_HANDLE);
// Create a pipe for the child process's STDOUT.
// Current Process --- > Child Process
if not CreatePipe(hPipe1Read, FhPipe1Write, @SecurityAttribs, 0) then
windows.beep(100, 100);
// Set a write handle to the pipe to be STDOUT.
// The current processes STD Output now flows into the Pipe and will write
// to the child process
if not SetStdHandle(STD_OUTPUT_HANDLE, hPipe1Write) then
windows.beep(100, 100);
// Create noninheritable read handle and close the inheritable read handle.
// Create a new unique handle output side of the pipe
if not DuplicateHandle(GetCurrentProcess, hPipe1Read, GetCurrentProcess,
@hPipe1ReadDuplicate, 0, False, DUPLICATE_SAME_ACCESS)
then
windows.beep(100, 100);
CloseHandle(hPipe1Read);
// The steps for redirecting child process's STDIN:
// 1. Save current STDIN, to be restored later.
// 2. Create anonymous pipe to be STDIN for child process.
// 3. Set STDIN of the parent to be the read handle to the
// pipe, so it is inherited by the child process.
// 4. Create a noninheritable duplicate of the write handle,
// and close the inheritable write handle.
// Save the handle to the current STDIN.
hOldSTDIn := GetStdHandle(STD_INPUT_HANDLE);
// Create a pipe for the child process's STDOut to this processes StdIn.
if not CreatePipe(FhPipe2Read, hPipe2Write, @SecurityAttribs, 0) then
windows.beep(100, 100);
// The STDIn now reads from the second Pipe
// Current Process <--- Child Process
if not SetStdHandle(STD_INPUT_HANDLE, hPipe2Read) then
windows.beep(100, 100);
// Duplicate the write handle to the pipe so it is not inherited.
if not DuplicateHandle(GetCurrentProcess, hPipe2Write, GetCurrentProcess(),
@hPipe2WriteDuplicate, 0, False, DUPLICATE_SAME_ACCESS)
then
windows.beep(100, 100);
CloseHandle(hPipe2Write);
// Now the current process's std input and output are attached to the ends
// of two pipes. The other ends have had duplicate handles created and the
// originals closed. Now we can create the process and attach the other
// ends of the pipes to the process
// Create a command line process that inherits handles. That mean it simply
// uses the same STDIn, STDOut, and STDError handles that we assigned earlier
// by incrementing the count.
FillChar(StartupInfo, SizeOf(StartupInfo), #0);
StartupInfo.cb := SizeOf(StartupInfo);
FillChar(ProcessInformation, SizeOf(ProcessInformation), #0);
if not CreateProcess('C:\WINNT\system32\cmd.exe', '', nil, nil, True, 0, nil, nil, StartupInfo, ProcessInformation) then
windows.beep(100, 100);
// We now replace our original STDIn and STDOut handlers.
SetStdHandle(STD_OUTPUT_HANDLE, hOldSTDOut);
SetStdHandle(STD_INPUT_HANDLE, hOldSTDIn);
// Note what this gets us. The new process now has
// hPipeWriteOuput as the STDOut for the new process and
// hPipeReadInput as the STDIn for the new process while we
// have the other end of the pipes in hPipeReadOutputDuplicate and
// hPipeWriteInputDuplicate
// So we have this:
// | ------------------------------------------- |
// | hPipe2WriteDuplicate ----> hPipe2Read |
// Current Process | | Child Process
// | hPipe1ReadDuplicate <---- hPipe1Write |
// | ------------------------------------------- |
//
end;
procedure TCommandLinePipe.ReadFrom(Stream: TMemoryStream);
var
BytesInPipe: DWORD;
begin
if PeekNamedPipe(hPipe1ReadDuplicate, nil, 0, nil, @BytesInPipe, nil) then
begin
Stream.Size := BytesInPipe;
ReadFile(hPipe1ReadDuplicate, Stream.Memory^, BytesInPipe, BytesInPipe, nil);
end
end;
function TCommandLinePipe.ReadResult: string;
begin
ReadFrom(MemStream);
SetLength(Result, MemStream.Size);
Move(PChar(Result)^, MemStream.Memory^, MemStream.Size);
end;
procedure TCommandLinePipe.DOSCommand(Command: string);
var
Written: DWORD;
begin
if not WriteFile(hPipe2WriteDuplicate, PChar(Command)^, Length(Command), Written, nil) then
windows.beep(100, 100);
Exit;
MemStream.Size := Length(Command);
Move(PChar(Command)^, MemStream.Memory^, Length(Command));
SendTo(MemStream);
end;
procedure TCommandLinePipe.SendTo(Stream: TMemoryStream);
var
Written: DWORD;
begin
if not WriteFile(hPipe2WriteDuplicate, Stream.Memory^, Stream.Size, Written, nil) then
windows.beep(100, 100);
end;
end.
|