我是我们公司的一名 Delphi 开发人员。我们需要一个函数来启动一个命令行可执行文件并获取其返回值。
我写的代码和我在网上找到的所有例子都是通过CreateProcess()
来实现的,但是我的老板拒绝了,告诉我一定有一个解决方案是通过ShellExecute()
来实现的。我在网上找不到任何使用ShellExecute()
的例子。所有的例子都使用CreateProcess()
。
下面是我交给老板的3个方法。他不喜欢ShellExecute_AndGetReturnValue()
。它被命名为“ShellExecute”,但它没有使用ShellExecute()
。
这三种方法都很好用,但第一种方法不是使用ShellExecute()
,而是使用CreateProcess()
。
那么,是否有可能解决/改变ShellExecute_AndGetReturnValue()
方法,使其使用ShellExecute()
而不是CreateProcess()
?我找到的所有示例,所有示例都使用CreateProcess()
。
function ShellExecute_AndGetReturnValue(FileName : string; Params : string = ''; Show : Integer = SW_HIDE; WorkingDir : string = '') : string;
const
READ_BUFFER_SIZE = 2048;
var
Security: TSecurityAttributes;
readableEndOfPipe, writeableEndOfPipe, readableErrorEndOfPipe, writeableErrorEndOfPipe: THandle;
start: TStartUpInfo;
ProcessInfo: TProcessInformation;
Buffer: PAnsiChar;
BytesRead: DWORD;
AppRunning: DWORD;
ResultStdOutput : string;
ResultErrOutput : string;
lpDirectory : PAnsiChar;
CmdLine : string;
begin
Result := '';
Security.nLength := SizeOf(TSecurityAttributes);
Security.bInheritHandle := True;
Security.lpSecurityDescriptor := nil;
if CreatePipe(readableEndOfPipe, writeableEndOfPipe, @Security, 0) then
begin
Buffer := AllocMem(READ_BUFFER_SIZE + 1);
FillChar(Start, Sizeof(Start), #0);
FillChar(ProcessInfo, SizeOf(ProcessInfo), #0);
start.cb := SizeOf(start);
start.dwFlags := start.dwFlags or STARTF_USESTDHANDLES;
start.hStdInput := GetStdHandle(STD_INPUT_HANDLE);
start.hStdOutput := writeableEndOfPipe;
CreatePipe(readableErrorEndOfPipe, writeableErrorEndOfPipe, @Security, 0);
start.hStdError := writeableErrorEndOfPipe;
start.hStdError := writeableEndOfPipe;
start.dwFlags := start.dwFlags + STARTF_USESHOWWINDOW;
start.wShowWindow := Show;
UniqueString(FileName);
CmdLine := '"' + FileName + '" ' + Params;
if WorkingDir <> '' then
begin
lpDirectory := PAnsiChar(WorkingDir);
end else
begin
lpDirectory := PAnsiChar(ExtractFilePath(FileName));
end;
if CreateProcess(nil, PChar(CmdLine), nil, nil, True, NORMAL_PRIORITY_CLASS, nil, lpDirectory, start, ProcessInfo) then
begin
repeat
Apprunning := WaitForSingleObject(ProcessInfo.hProcess, 100);
Application.ProcessMessages;
until (Apprunning <> WAIT_TIMEOUT);
ResultStdOutput := '';
ResultErrOutput := '';
//Must Close write Handles before reading (if the console application does not output anything)
CloseHandle(writeableEndOfPipe);
CloseHandle(writeableErrorEndOfPipe);
repeat
BytesRead := 0;
ReadFile(readableEndOfPipe, Buffer[0], READ_BUFFER_SIZE, BytesRead, nil);
Buffer[BytesRead]:= #0;
OemToAnsi(Buffer,Buffer);
ResultStdOutput := ResultStdOutput + String(Buffer);
until (BytesRead < READ_BUFFER_SIZE);
if start.hStdOutput <> start.hStdError then
begin
BytesRead := 0;
ReadFile(readableErrorEndOfPipe, Buffer[0], READ_BUFFER_SIZE, BytesRead, nil);
Buffer[BytesRead]:= #0;
OemToAnsi(Buffer,Buffer);
ResultErrOutput := ResultErrOutput + String(Buffer);
end;
end;
Result := ResultStdOutput;
FreeMem(Buffer);
CloseHandle(ProcessInfo.hProcess);
CloseHandle(ProcessInfo.hThread);
CloseHandle(readableEndOfPipe);
CloseHandle(readableErrorEndOfPipe);
end;
end;
procedure ShellExecute_NoWait(FileName : string; Params : string = ''; Action : string = 'open'; Show : Integer = SW_SHOWNORMAL; WorkingDir : string = '');
var
exInfo: TShellExecuteInfo;
Ph: DWORD;
begin
FillChar(exInfo, SizeOf(exInfo), 0);
with exInfo do
begin
cbSize := SizeOf(exInfo);
fMask := SEE_MASK_NOCLOSEPROCESS or SEE_MASK_FLAG_DDEWAIT;
Wnd := GetActiveWindow();
lpVerb := PAnsiChar(Action);
lpParameters := PChar(Params);
lpFile := PChar(FileName);
nShow := Show;
if WorkingDir <> '' then
begin
lpDirectory := PAnsiChar(WorkingDir);
end else
begin
lpDirectory := PAnsiChar(ExtractFilePath(FileName));
end;
end;
if ShellExecuteEx(@exInfo) then
begin
Ph := exInfo.HProcess;
CloseHandle(Ph);
end;
end;
procedure ShellExecute_AndWait(FileName : string; Params : string = ''; Action : string = 'open'; Show : Integer = SW_SHOWNORMAL; WorkingDir : string = '');
var
exInfo: TShellExecuteInfo;
Ph: DWORD;
begin
FillChar(exInfo, SizeOf(exInfo), 0);
with exInfo do
begin
cbSize := SizeOf(exInfo);
fMask := SEE_MASK_NOCLOSEPROCESS or SEE_MASK_FLAG_DDEWAIT;
Wnd := GetActiveWindow();
lpVerb := PAnsiChar(Action);
lpParameters := PChar(Params);
lpFile := PChar(FileName);
nShow := Show;
if WorkingDir <> '' then
begin
lpDirectory := PAnsiChar(WorkingDir);
end else
begin
lpDirectory := PAnsiChar(ExtractFilePath(FileName));
end;
end;
if ShellExecuteEx(@exInfo) then
begin
Ph := exInfo.HProcess;
while WaitForSingleObject(ExInfo.hProcess, 50) <> WAIT_OBJECT_0 do
begin
Application.ProcessMessages;
end;
CloseHandle(Ph);
end;
end;
2条答案
按热度按时间q9yhzks01#
你老板的任务不完全正确。问题是ShellExecute的通用解决方案-不是startcmd.exe,这个命令启动一个链接到这种类型文件的应用程序并启动它。所以,让它像你想要的那样工作-它需要大量的工作。还有一件事-你需要得到你的程序的工作结果还是你的程序的控制台输出?下面是从jcl库中修改部分源代码返回的返回代码:
31moq8wy2#
如果您需要指定输入/输出管道(以控制被调用进程的stdin和stdout),则不能使用ShellExecute。它只是不支持指定这些管道。ShellExecuteEx也不支持。
因此,如果你必须使用ShellExecute,你唯一的选择就是ShellExecute命令处理器(CMD.EXE),并要求它执行输入和输出的重定向。这将把你的重定向源和目标限制在磁盘上的物理文件上,因为这是CMD.EXE允许重定向的方式(〉StdOut〈StdIn)。
另外,您使用CreateProcess的方法是前进的方向。您的老板给予了什么理由让您 * 必须 * 使用ShellExecute?
如果您 * 不 * 需要重定向支持,则可以使用ShellExecuteEx,然后在成功执行之后,您可以在Info.hProcess(Info是传递给ShellExecuteEx的TShellExecuteInfo结构)中获取正在运行的进程的句柄。
然后,可以在GetExitCodeProcess中使用该值来确定进程是否仍在运行,或者它是否已终止(如果我正确理解了您对该表达式的使用,则您已检索到“返回值”-它实际上称为“ExitCode”,或者在批处理文件中称为“ERRORLEVEL”)。
不完整的代码:
上面的代码应该演示如何做到这一点...