File: VirtualCommandLine.pas

package info (click to toggle)
mysql-query-browser 1.1.6-1sarge0
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 36,320 kB
  • ctags: 24,680
  • sloc: pascal: 203,479; xml: 136,561; ansic: 47,502; cpp: 28,926; sh: 12,433; objc: 4,823; java: 1,849; php: 1,485; python: 1,225; sql: 1,128; makefile: 872
file content (239 lines) | stat: -rw-r--r-- 8,810 bytes parent folder | download
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.