在 Windows XP 上的 Delphi 应用程序中从命令行程序获取输出

Posted

技术标签:

【中文标题】在 Windows XP 上的 Delphi 应用程序中从命令行程序获取输出【英文标题】:Getting output from a command-line program in a Delphi application on Windows XP 【发布时间】:2013-10-01 22:02:29 【问题描述】:

我有一个用 Delphi XE2 编写的 VCL 应用程序,它需要执行一个命令行程序(也是用 Delphi XE2 编写的)并获取它输出的文本。我目前正在使用以下代码,该代码基于此处找到的代码:Getting output from a shell/dos app into a Delphi app

function GetDosOutput(ACommandLine : string; AWorkingDirectory : string): string;
var
  SecurityAttributes : TSecurityAttributes;
  StartupInfo : TStartupInfo;
  ProcessInformation: TProcessInformation;
  StdOutPipeRead, StdOutPipeWrite: THandle;
  WasOK: Boolean;
  Buffer: array[0..255] of AnsiChar;
  BytesRead: Cardinal;
  Handle: Boolean;
begin
  Result := '';
  SecurityAttributes.nLength := SizeOf(TSecurityAttributes);
  SecurityAttributes.bInheritHandle := True;
  SecurityAttributes.lpSecurityDescriptor := nil;
  CreatePipe(StdOutPipeRead, StdOutPipeWrite, @SecurityAttributes, 0);
  try
    FillChar(StartupInfo, SizeOf(TStartupInfo), 0);
    StartupInfo.cb := SizeOf(TStartupInfo);
    StartupInfo.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
    StartupInfo.wShowWindow := SW_HIDE;
    StartupInfo.hStdInput := StdOutPipeRead;
    StartupInfo.hStdOutput := StdOutPipeWrite;
    StartupInfo.hStdError := StdOutPipeWrite;
    FillChar(ProcessInformation, SizeOf(ProcessInformation), 0);
    Handle := CreateProcess(
      nil,
      PChar(ACommandLine),
      nil,
      nil,
      True,
      0,
      nil,
      PChar(AWorkingDirectory),
      StartupInfo,
      ProcessInformation
    );
    CloseHandle(StdOutPipeWrite);
    if Handle then
      try
        repeat
          WasOK := ReadFile(StdOutPipeRead, Buffer, 255, BytesRead, nil);
          if BytesRead > 0 then
          begin
            Buffer[BytesRead] := #0;
            Result := Result + Buffer;
          end;
        until not WasOK or (BytesRead = 0);
        WaitForSingleObject(ProcessInformation.hProcess, INFINITE);
      finally
        CloseHandle(ProcessInformation.hThread);
        CloseHandle(ProcessInformation.hProcess);
      end;
  finally
    CloseHandle(StdOutPipeRead);
  end;
end;

这适用于大多数版本的 Windows。不幸的是,最近我们注意到它不适用于 Windows XP。对 WaitForSingleObject 的调用永远不会返回。我尝试用较小的值(例如 15000)替换第二个参数 INFINITE ,但这似乎没有任何区别。在任务管理器中,我可以看到,在调用 GetDosOutput 之后,命令行程序实际上正在运行。如果我结束 VCL 应用程序,命令行程序 then 似乎成功地完成了它的工作(正如它输出我期望它输出的文件的事实所证明的那样)。我还注意到,如果我从 StartupInfo.dwFlags 中删除 STARTF_USESTDHANDLES,命令行程序会正常运行并且 WaitForSingleObject 会迅速返回;但是我显然无法获取程序返回的文本。

有没有人建议我如何在 Windows XP 上运行它?

【问题讨论】:

您可以在以下任务中找到正确的代码:***.com/questions/9119999/… 我喜欢阅读这篇文章:句柄:布尔 @DavidHeffernan 对于具有强类型背景的人来说,winAPI 的 BOOL 类型可能会令人困惑。 如果在读取管道之前等待程序完成,可能会出现死锁。最好在输出可用时读取它。在这方面,您来自 Delphi Dabbler 的代码更好。进一步调查程序似乎挂起的原因。使用调试器。 @RobKennedy,你说得对,正是这个似乎是众多衍生产品之一(也不是 100% 与 DelphiDabler 的 sn-p 相同)。无论如何,我建议停止鞭打死马并转向 JCL 助手(或相应的 JVCL 组件)。 【参考方案1】:

freepascal 中有一个非常有用的单元称为“进程”,它就是这样做的,并且已经完成了将它移植到Delphi的工作,以便您可以捕获Delphi 中的命令输出,使用简单的单行:

RunCommand()

或者您可以通过自己创建一个 TProcess 对象(RunCommand 只是包装)来捕获具有更高级功能的命令的输出。

项目在这里:

https://github.com/z505/TProcess-Delphi 一个简单的演示:https://github.com/z505/TProcess-Delphi/tree/master/demo-simple

如何将命令的输出,即“dir”(列出目录内容,著名的 MS DOS 命令)捕获到字符串中,然后将其添加到备忘录中:

uses 
  dprocess; 
// ...
var 
  output: ansistring;
begin
  RunCommand('cmd', ['/c', 'dir'], output, [poNoConsole]);
  memo1.Lines.Add(output);
end;

【讨论】:

你要对每个提到 ShellExecute 的问题都给出相同的答案吗?

以上是关于在 Windows XP 上的 Delphi 应用程序中从命令行程序获取输出的主要内容,如果未能解决你的问题,请参考以下文章

无法使用 Windows XP SP3 目标调试大型 delphi XE2 应用程序跨平台

Delphi 7:在 Win XP 或 Windows Server 2003 下编译有啥区别?

vmware上的Delphi7和XP测试程序,连接socket失败

Delphi 4 Pro 能否在 Windows XP(或更高版本)上可靠地安装和使用?

使用 DELPHI 与 Windows 上的两个应用程序通信

Delphi 多线程应用程序内置在 vista 中,无法在 XP 中运行