我想运行用户在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/

10-10 01:10