我想运行用户在INI文件中定义的命令。
命令可以是EXE文件,也可以是其他文件(例如DOC文件)和参数。
由于WinExec()可以处理参数(例如“ cmd /?”),而ShellExec()可以处理非EXE文件(例如“ Letter.doc”),因此我将两者结合使用。
我担心将来的Windows版本,因为WinExec()已弃用,甚至从16位时代开始就已弃用。
这是我当前的功能:
procedure RunCMD(cmdLine: string; WindowMode: integer);
procedure ShowWindowsErrorMessage(r: integer);
begin
MessageDlg(SysErrorMessage(r), mtError, [mbOK], 0);
end;
var
r, g: Cardinal;
begin
// We need a function which does following:
// 1. Replace the Environment strings, e.g. %SystemRoot% --> ExpandEnvStr
// 2. Runs EXE files with parameters (e.g. "cmd.exe /?") --> WinExec
// 3. Runs EXE files without path (e.g. "calc.exe") --> WinExec
// 4. Runs EXE files without extension (e.g. "calc") --> WinExec
// 5. Runs non-EXE files (e.g. "Letter.doc") --> ShellExecute
// 6. Commands with white spaces (e.g. "C:\Program Files\xyz.exe") must be enclosed in quotes.
cmdLine := ExpandEnvStr(cmdLine);
// TODO: Attention: WinExec() is deprecated, but there is no acceptable replacement
g := WinExec(PChar(cmdLine), WindowMode);
r := GetLastError;
if g = ERROR_BAD_FORMAT then
begin
// e.g. if the user tries to open a non-EXE file
ShellExecute(0, nil, PChar(cmdLine), '', '', WindowMode);
r := GetLastError;
end;
if r <> 0 then ShowWindowsErrorMessage(r);
end;
function ExpandEnvStr(const szInput: string): string;
// http://stackoverflow.com/a/2833147/3544341
const
MAXSIZE = 32768;
begin
SetLength(Result, MAXSIZE);
SetLength(Result, ExpandEnvironmentStrings(pchar(szInput),
@Result[1],length(Result)));
end;
Microsoft建议使用CreateProcess(),但我不接受将其作为WinExec()的真正替代。
例如,给出以下命令行:
"C:\Program Files\xyz.exe" /a /b /c
由于ShellExecute()和CreateProcess()需要严格分隔命令和参数,因此我必须自己解析此字符串。那真的是我唯一的方法吗?是否有人编写了具有此功能的公共可用代码?
附加说明:该过程不应附加到调用方。命令启动后,我的程序将关闭。
最佳答案
CreateProcess()
替代WinExec()
。该文档明确指出了很多。
顺便说一句,原始代码中的错误处理是完全错误的。您正在滥用GetLastError()
。实际上,WinExec()
和ShellExecute()
都不会报告以GetLastError()
开始的错误。因此,即使WinExec()
或ShellExecute()
成功(并且您甚至都没有检查ShellExecute()
是成功还是失败),也有可能报告来自早期API调用的随机错误。
尝试更多类似这样的方法:
procedure RunCMD(cmdLine: string; WindowMode: integer);
procedure ShowWindowsErrorMessage(r: integer);
var
sMsg: string;
begin
sMsg := SysErrorMessage(r);
if (sMsg = '') and (r = ERROR_BAD_EXE_FORMAT) then
sMsg := SysErrorMessage(ERROR_BAD_FORMAT);
MessageDlg(sMsg, mtError, [mbOK], 0);
end;
var
si: TStartupInfo;
pi: TProcessInformation;
sei: TShellExecuteInfo;
err: Integer;
begin
// We need a function which does following:
// 1. Replace the Environment strings, e.g. %SystemRoot% --> ExpandEnvStr
// 2. Runs EXE files with parameters (e.g. "cmd.exe /?") --> WinExec
// 3. Runs EXE files without path (e.g. "calc.exe") --> WinExec
// 4. Runs EXE files without extension (e.g. "calc") --> WinExec
// 5. Runs non-EXE files (e.g. "Letter.doc") --> ShellExecute
// 6. Commands with white spaces (e.g. "C:\Program Files\xyz.exe") must be enclosed in quotes.
cmdLine := ExpandEnvStr(cmdLine);
{$IFDEF UNICODE}
UniqueString(cmdLine);
{$ENDIF}
ZeroMemory(@si, sizeof(si));
si.cb := sizeof(si);
si.dwFlags := STARTF_USESHOWWINDOW;
si.wShowWindow := WindowMode;
if CreateProcess(nil, PChar(cmdLine), nil, nil, False, 0, nil, nil, si, pi) then
begin
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
Exit;
end;
err := GetLastError;
if (err = ERROR_BAD_EXE_FORMAT) or
(err = ERROR_BAD_FORMAT) then
begin
ZeroMemory(@sei, sizeof(sei));
sei.cbSize := sizeof(sei);
sei.lpFile := PChar(cmdLine);
sei.nShow := WindowMode;
if ShellExecuteEx(@sei) then Exit;
err := GetLastError;
end;
ShowWindowsErrorMessage(err);
end;
关于delphi - 是否可以替代WinExec()?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/32802679/