delphi 是否可以使用启动应用程序/进程时创建的TShellExecuteInfo记录来关闭同一应用程序/进程

fquxozlt  于 2023-03-18  发布在  Shell
关注(0)|答案(1)|浏览(175)

我正在使用以下代码从我的应用程序中打开另一个应用程序。

type TShellState = class
  SEInfo: TShellExecuteInfo;
  AppName: String;
  Constructor Create(pSEInfo: TShellExecuteInfo; pAppName: String);
end;

Var
  ShellStates: TObjectStack<TShellState>;

Constructor TShellstate.Create(pSEInfo: TShellExecuteInfo; pAppName: string);
begin
  inherited Create;
  SEInfo := pSEInfo;
  AppName := pAppName;
end;

//In Form Create
ShellStates := TObjectStack<TShellState>.create;
ShellStates.OwnsObjects := true;

function DoExecute(Const ExecuteFile: string;
                   Const ParamString: string;
                   Modal: Boolean;
                   UseLocalDir: Boolean = false;
                   NoCloseProc: Boolean = true): Boolean;

Var SEInfo: TShellExecuteInfo;
    ExitCode: DWORD;

begin
  FillChar(SEInfo, SizeOf(SEInfo), 0) ;
  SEInfo.cbSize := SizeOf(TShellExecuteInfo) ;
  with SEInfo do begin
    fMask := SEE_MASK_DEFAULT;
    Wnd := Application.Handle;
    lpFile := PChar(ExecuteFile) ;
    lpParameters := PChar(ParamString) ;
    if UseLocalDir then
      lpDirectory := PChar(ExtractFilePath(ExecuteFile));
    nShow := SW_SHOWNORMAL;
  end;

  Result := ShellExecuteEx(@SEInfo);
  if result then begin
    if Modal then begin
      if SEInfo.hProcess <> 0 then begin
        WaitForSingleObject(SEInfo.hProcess, INFINITE);
        CloseHandle(SEInfo.hProcess);
      end;
      Result := true;
    end
    else if NoCloseProc then
      ShellStates.Push(TShellState.Create(SEInfo, ExecuteFile));
  end;
end;

如果NoCloseProc为真,我将SEInfo记录放入堆栈。
当我关闭主程序时,我希望能够使用SEInfo中的字段来标识接收WM_CLOSE消息的窗口。

while ShellStates.Count > 0 do begin
    ShellState := ShellStates.Peek;
    SendMessage(ShellState.SEInfo.Wnd, WM_CLOSE,0,0);
    ShellStates.Pop;
  end

但是hProcess和Wnd都是0,所以看起来我必须使用文件名来定位窗口。有没有更直接的方法?我尝试使用FindWindow API调用SEInfo.lpFile,但是返回0。

xwmevbvl

xwmevbvl1#

当我关闭主程序时,我希望能够使用SEInfo中的字段来标识接收WM_CLOSE消息的窗口。
ShellExecuteEx()没有为此目的提供单个字段,因此您必须查找要与之交互的窗口。
还要注意的是,如果你试图退出一个派生的程序,发送WM_CLOSE可能并不总是能成功,想象一下一个文本编辑器,它提示用户保存挂起的更改,如果用户取消提示,编辑器会继续运行,所以,如果你想强制关闭,考虑发送WM_QUIT而不是WM_CLOSE。StackOverflow有几个与关闭外部进程相关的问题,例如:
How to gracefully terminate a process?
Win32 API For Shutting Down Another Process Elegantly?
How do I gracefully close another application?
仅举几例。
但是hProcess和Wnd都是0
Wnd字段是仅输入字段,它指定ShellExecute()在设置/执行指定操作时可能显示的任何UI对话框的所有者窗口。该字段不接收属于正在启动的进程的任何窗口。
温德
类型:HWND
可选。所有者窗口的句柄,用于显示和定位系统在执行此函数时可能生成的任何UI。
hProcess字段为0,因为您没有在fMask字段中指定SEE_MASK_NOCLOSEPROCESS标志:
f屏蔽
类型:ULONG
下列一个或多个值的组合,指示其他结构成员的内容和有效性:
| 价值|意义|
| - ------|- ------|
| 参见面罩关闭过程(0x00000040)|用于指示hProcess成员接收进程句柄。此句柄通常用于允许应用程序查明用ShellExecuteEx创建的进程何时终止。在某些情况下,例如通过DDE对话满足执行要求时,将不返回句柄。调用应用程序负责在不再需要句柄时将其关闭。|
所以看起来我必须用文件名来定位窗口。有没有更直接的方法?
使用SEE_MASK_NOCLOSEPROCESS标志时,可以使用EnumWindows()查找属于返回的hProcess字段所表示的进程的窗口。可以使用GetProcessId()hProcess字段获取ProcessID,然后让enumeration callback使用GetWindowThreadProcessId()比较窗口ProcessID。
我尝试对SEInfo.lpFile使用API调用,但返回0。
当然,因为大多数应用程序一开始都不会在窗口标题中显示文件名,而显示文件名的应用程序往往也会显示其他信息,比如应用程序名称。

相关问题