Author: Fernando Silva
When you want to execute some command you just call WinExec. But if you want to
execute some system command like 'dir *.*' or even execute an old DOS application,
WinExec by itself just doesn't give all you want.
Answer:
The solution here it's by running your commands trought the COMSPEC. This is,
COMPSPEC it's an environment variable that on Windows NT and 2000 returns the path
to CMD.EXE and on Windows 9x returns the path to COMMAND.COM.
While I tested all this article code with Windows 2000 it can probably run also in
Windows 9x (I think).
If you go to 'Command Prompt' and write the following you'll get a description of a
various number of options that you can use to run a command (or a batch of commands
in a single command line):
Starts a new instance of the Windows 2000 command interpreter
CMD [/A | /U] [/Q] [/D] [/E:ON | /E:OFF] [/F:ON | /F:OFF] [/V:ON | /V:OFF]
[[/S] [/C | /K] string]
/C Carries out the command specified by string and then terminates
/K Carries out the command specified by string but remains
/S Modifies the treatment of string after /C or /K (see below)
/Q Turns echo off
/D Disable execution of AutoRun commands from registry (see below)
/A Causes the output of internal commands to a pipe or file to be ANSI
/U Causes the output of internal commands to a pipe or file to be
Unicode
/T:fg Sets the foreground/background colors (see COLOR /? for more info)
/E:ON Enable command extensions (see below)
/E:OFF Disable command extensions (see below)
/F:ON Enable file and directory name completion characters (see below)
/F:OFF Disable file and directory name completion characters (see below)
/V:ON Enable delayed environment variable expansion using c as the
delimiter. For example, /V:ON would allow !var! to expand the
variable var at execution time. The var syntax expands variables
at input time, which is quite a different thing when inside of a FOR
loop.
/V:OFF Disable delayed environment expansion.
Note that multiple commands separated by the command separator '&&' are accepted
for string if surrounded by quotes. Also, for compatibility reasons, /X is the
same as /E:ON, /Y is the same as /E:OFF and /R is the same as /C. Any other
switches are ignored.
If /C or /K is specified, then the remainder of the command line after the switch
is processed as a command line, where the following logic is used to process quote
(") characters:
1. If all of the following conditions are met, then quote characters
on the command line are preserved:
- no /S switch
- exactly two quote characters
- no special characters between the two quote characters,
where special is one of: &<>()@^|
- there are one or more whitespace characters between the
the two quote characters
- the string between the two quote characters is the name
of an executable file.
2. Otherwise, old behavior is to see if the first character is
a quote character and if so, strip the leading character and
remove the last quote character on the command line, preserving
any text after the last quote character.
If /D was NOT specified on the command line, then when CMD.EXE starts, it
looks for the following REG_SZ/REG_EXPAND_SZ registry variables, and if
either or both are present, they are executed first.
HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor\AutoRun
and/or
HKEY_CURRENT_USER\Software\Microsoft\Command Processor\AutoRun
Command Extensions are enabled by default. You may also disable extensions for a
particular invocation by using the /E:OFF switch. You can enable or disable
extensions for all invocations of CMD.EXE on a machine and/or user logon session by
setting either or both of the following REG_DWORD values in the registry using
REGEDT32.EXE:
HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor\EnableExtensions
and/or
HKEY_CURRENT_USER\Software\Microsoft\Command Processor\EnableExtensions
to either 0x1 or 0x0. The user specific setting takes precedence over the machine
setting. The command line switches take precedence over the registry settings.
The command extensions involve changes and/or additions to the following
commands:
DEL or ERASE
COLOR
CD or CHDIR
MD or MKDIR
PROMPT
PUSHD
POPD
SET
SETLOCAL
ENDLOCAL
IF
FOR
CALL
SHIFT
GOTO
START (also includes changes to external command invocation)
ASSOC
FTYPE
To get specific details, type commandname /? to view the specifics.
Delayed environment variable expansion is NOT enabled by default. You can enable
or disable delayed environment variable expansion for a particular invocation of
CMD.EXE with the /V:ON or /V:OFF switch. You can enable or disable completion for
all invocations of CMD.EXE on a machine and/or user logon session by setting either
or both of the following REG_DWORD values in the registry using REGEDT32.EXE:
HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor\DelayedExpansion
and/or
HKEY_CURRENT_USER\Software\Microsoft\Command Processor\DelayedExpansion
to either 0x1 or 0x0. The user specific setting takes precedence over the machine
setting. The command line switches take precedence over the registry settings.
If delayed environment variable expansion is enabled, then the exclamation
character can be used to substitute the value of an environment variable
at execution time.
File and Directory name completion is NOT enabled by default. You can enable or
disable file name completion for a particular invocation of CMD.EXE with the /F:ON
or /F:OFF switch. You can enable or disable completion for all invocations of
CMD.EXE on a machine and/or user logon session by setting either or both of the
following REG_DWORD values in the registry using REGEDT32.EXE:
HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor\CompletionChar
HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor\PathCompletionChar
and/or
HKEY_CURRENT_USER\Software\Microsoft\Command Processor\CompletionChar
HKEY_CURRENT_USER\Software\Microsoft\Command Processor\PathCompletionChar
with the hex value of a control character to use for a particular function (e.g.
0x4 is Ctrl-D and 0x6 is Ctrl-F). The user specific settings take precedence over
the machine settings. The command line switches take precedence over the registry
settings.
If completion is enabled with the /F:ON switch, the two control characters used are
Ctrl-D for directory name completion and Ctrl-F for file name completion. To
disable a particular completion character in the registry, use the value for space
(0x20) as it is not a valid control character.
Completion is invoked when you type either of the two control characters. The
completion function takes the path string to the left of the cursor appends a wild
card character to it if none is already present and builds up a list of paths that
match. It then displays the first matching path. If no paths match, it just beeps
and leaves the display alone. Thereafter, repeated pressing of the same control
character will cycle through the list of matching paths. Pressing the Shift key
with the control character will move through the list backwards. If you edit the
line in any way and press the control character again, the saved list of matching
paths is discarded and a new one generated. The same occurs if you switch between
file and directory name completion. The only difference between the two control
characters is the file completion character matches both file and directory names,
while the directory completion character only matches directory names.
If file completion is used on any of the built in directory commands (CD, MD or RD)
then directory completion is assumed.
The completion code deals correctly with file names that contain spaces or other
special characters by placing quotes around the matching path.
Also, if you back up, then invoke completion from within a line, the text to the
right of the cursor at the point completion was invoked is discarded.
and it's this option that will let you run commands without the disadvantage of
using batch files.
With this article you'll also have an attached project sample containing an unit
(SysCommand.pas) that implements a small component. The best thing you can do it's
look at the attached sample, anyway here it is a small test sample:
1 var
2 FSysCommand: TSysCommand;
3
4 begin
5 FSysCommand := TSysCommand.Create(Self);
6 FSysCommand.WhenDone := cwdRemain;
7 FSysCommand.WindowTitle := 'This is a test';
8 FSysCommand.BkColor := cclBtWhite;
9 FSysCommand.FgColor := cclLtRed;
10 FSysCommand.CmdsList.Add('echo Now I will list your windows directory files.');
11 FSysCommand.CmdsList.Add('pause');
12 FSysCommand.CmdsList.Add('dir "c:\windows\*.*" /s');
13 FSysCommand.Execute;
14
15 as you can see, it's easy and simple to run a batch of commands throught the
16 Command Prompt.
17
18 = = SysCommand.pas = =
19
20 unit SysCommand;
21 // by Fernando J.A. Silva (magico@galaxycorp.com) 2001/08/24
22
23 interface
24 uses
25 Classes;
26
27 type
28 TCmdWhenDone = (
29 cwdClose { /C or /R },
30 // Carries out the command specified by string and then terminates
31 cwdRemain { /K } // Carries out the command specified by string but remains
32 );
33
34 TCmdAutoRunCmds = (
35 carDefault,
36 carDisable { /D } // Disable execution of AutoRun commands from registry
37 );
38 // if carDisable was NOT specified, then when the command is launched, it
39 // looks for the following REG_SZ/REG_EXPAND_SZ registry variables, and if
40 // either or both are present, they are executed first.
41 //
42 // HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor\AutoRun
43 // and/or
44 // HKEY_CURRENT_USER\Software\Microsoft\Command Processor\AutoRun
45
46 TCmdOutputType = (
47 cotANSI { /A },
48 // Causes the output of internal commands to a pipe or file to be ANSI
49 cotUnicode { /U } // Causes the output of internal commands to a pipe or file
50 to be Unicode
51 );
52
53 TCmdColor = (cclNone, cclBlack {0}, cclBlue {1}, cclGreen {2}, cclAqua {3}, cclRed
54 {4},
55 cclPurple {5}, cclYellow {6}, cclWhite {7}, cclGray {8}, cclLtBlue {9},
56 cclLtGreen {A}, cclLtAqua {B}, cclLtRed {C}, cclLtPurple {D}, cclLtYellow {E},
57 cclBtWhite {F});
58
59 TCmdExtensions = (
60 cexDefault,
61 cexON, {/E:ON or /X } // Enable command extensions
62 cexOFF {/E:OFF or /Y } // Disable command extensions
63 );
64 // Command Extensions are enabled by default. You may also disable extensions
65 // for a particular invocation by using the cexOFF. You can enable or disable
66 // extensions for all invocations of CMD.EXE on a machine and/or user logon
67 // session by setting either or both of the following REG_DWORD values in the
68 // registry using REGEDT32.EXE:
69 //
70 // HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor\EnableExtensions
71 // and/or
72 // HKEY_CURRENT_USER\Software\Microsoft\Command Processor\EnableExtensions
73 //
74 // to either 0x1 or 0x0. The user specific setting takes precedence over
75 // the machine setting. The command line switches take precedence over the
76 // registry settings.
77
78 TSysCommand = class(TComponent)
79 private
80 FOutputType: TCmdOutputType;
81 FAutoRunCmds: TCmdAutoRunCmds;
82 FCmdExtensions: TCmdExtensions;
83 FWhenDone: TCmdWhenDone;
84 FBkColor: TCmdColor;
85 FFgColor: TCmdColor;
86 FWindowTitle: string;
87
88 FCmdsList: TStringList;
89
90 function GetParamsString: string;
91 procedure GetInternalCmdsList(var AList: TStringList);
92 function GetCommandsString: string;
93 public
94 constructor Create(AOwner: TComponent); override;
95 destructor Destroy; override;
96 function Execute: Boolean;
97 published
98 property OutputType: TCmdOutputType read FOutputType write FOutputType;
99 property AutoRunCmds: TCmdAutoRunCmds read FAutoRunCmds write FAutoRunCmds;
100 property CmdExtensions: TCmdExtensions read FCmdExtensions write FCmdExtensions;
101 property WhenDone: TCmdWhenDone read FWhenDone write FWhenDone;
102 property BkColor: TCmdColor read FBkColor write FBkColor;
103 property FgColor: TCmdColor read FFgColor write FFgColor;
104 property WindowTitle: string read FWindowTitle write FWindowTitle;
105
106 property CmdsList: TStringList read FCmdsList write FCmdsList;
107 end;
108
109 implementation
110
111 uses
112 Windows,
113 SysUtils;
114
115 const
116 FStrWhenDone: array[0..1] of string = ('
117 FStrAutoRun: array[0..1] of string = ('', ' /D');
118 FStrOutType: array[0..1] of string = (' /A', ' /U');
119 FStrColors: array[0..16] of string = ('', '0', '1', '2', '3', '4', '5', '6', '7',
120 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F');
121 FStrUseExt: array[0..2] of string = ('', ' /E:ON', ' /E:OFF');
122
123 constructor TSysCommand.Create(AOwner: TComponent);
124 begin
125 inherited Create(AOwner);
126 FOutputType := cotANSI;
127 FAutoRunCmds := carDefault;
128 FCmdExtensions := cexDefault;
129 FWhenDone := cwdClose;
130 FBkColor := cclNone;
131 FFgColor := cclNone;
132 WindowTitle := '';
133 FCmdsList := TStringList.Create;
134 end;
135
136 destructor TSysCommand.Destroy;
137 begin
138 inherited Destroy;
139 FCmdsList.Destroy;
140 end;
141
142 function TSysCommand.GetParamsString: string;
143 // Returns a string containing the parameters to be passed to 'COMSPEC'
144 begin
145 Result := '';
146 Result := Result + FStrOutType[Ord(FOutputType)];
147 Result := Result + FStrAutoRun[Ord(FAutoRunCmds)];
148 Result := Result + FStrUseExt[Ord(FCmdExtensions)];
149 Result := Result + FStrWhenDone[Ord(FWhenDone)];
150 end;
151
152 procedure TSysCommand.GetInternalCmdsList(var AList: TStringList);
153 begin
154 // Clean string list
155 AList.Clear;
156 // Insert all commands
157 if FWindowTitle <> '' then
158 AList.Add('TITLE ' + FWindowTitle);
159 if (FBkColor <> cclNone) and (FFgColor <> cclNone) then
160 AList.Add('COLOR ' + FStrColors[Ord(FBkColor)] + FStrColors[Ord(FFgColor)]);
161 end;
162
163 function TSysCommand.GetCommandsString: string;
164 // Return a string with internal commands and user commands
165 var
166 FList: TStringList;
167 idx: Integer;
168 s: string;
169
170 begin
171 s := '';
172
173 // Get internal command list as string
174 FList := TStringList.Create;
175 GetInternalCmdsList(FList);
176 for idx := 0 to FList.Count - 1 do
177 s := s + FList.Strings[idx] + ' && ';
178 // Concat with user commands list
179 for idx := 0 to FCmdsList.Count - 1 do
180 s := s + FCmdsList.Strings[idx] + ' && ';
181 // Delete last &&
182 Delete(s, Length(s) - 3, 4);
183
184 // Return commands string
185 Result := '"' + s + '"';
186 end;
187
188 function TSysCommand.Execute: Boolean;
189 var
190 FCmd: string;
191 FExec: string;
192
193 begin
194 // get name and path of command processor
195 FCmd := GetEnvironmentVariable('COMSPEC');
196
197 FExec := FCmd + ' ' + GetParamsString + ' ' + GetCommandsString;
198 Result := WinExec(PChar(FExec), SW_SHOWNORMAL) > 31;
199 end;
200
201 end.
Note: The TSysCommand probably can be extended to support Linux Terminal Mode.
It would be a fine addition and a good reason to use this component in a application.
|