Delphi - 优雅地关闭服务中创建的进程。 (使用 tprocess / createProcess)

发布于 2024-07-07 18:36:19 字数 870 浏览 12 评论 0原文

我有一个用 Delphi 编写的 Windows 服务,它运行许多程序。

在停止服务时,我还想关闭这些程序。 当服务最初编写时,这工作得很好,但我想我已经更新了 tProcess 组件,现在 - 从属程序没有被关闭。

in tProcess - 这是启动新进程的代码。

  if CreateProcess( nil , PChar( FProcess.Command ) , nil , nil , False ,
    NORMAL_PRIORITY_CLASS , nil , Directory ,
    StartupInfo , ProcessInfo ) then
    begin
      if FProcess.Wait then
        begin
          WaitForSingleObject( ProcessInfo.hProcess , Infinite );
          GetExitCodeProcess( ProcessInfo.hProcess , ExitCode );
          if Assigned( FProcess.FOnFinished ) then
            FProcess.FOnFinished( FProcess , ExitCode );
        end;
      CloseHandle( ProcessInfo.hProcess );
      CloseHandle( ProcessInfo.hThread );
    end;

由此调用的每个可执行文件都是 Windows GUI 程序(顶部有一个关闭按钮)。

当我停止服务时,我还想停止(而不是终止)我通过 createProcess 过程启动的程序。

你会怎么做?

I have a Windows Service written in Delphi which runs a number of programs.

On Stopping the service, I want to also close these programs. When the service was originally written, this worked fine, but I think I've updated the tProcess component and now - The subordinate programs are not being closed.

in tProcess - Here's the code which starts the new processes.

  if CreateProcess( nil , PChar( FProcess.Command ) , nil , nil , False ,
    NORMAL_PRIORITY_CLASS , nil , Directory ,
    StartupInfo , ProcessInfo ) then
    begin
      if FProcess.Wait then
        begin
          WaitForSingleObject( ProcessInfo.hProcess , Infinite );
          GetExitCodeProcess( ProcessInfo.hProcess , ExitCode );
          if Assigned( FProcess.FOnFinished ) then
            FProcess.FOnFinished( FProcess , ExitCode );
        end;
      CloseHandle( ProcessInfo.hProcess );
      CloseHandle( ProcessInfo.hThread );
    end;

Each of the executables called by this are Windows GUI Programs (With a close button at the top).

When I stop the service, I also want to stop (not kill) the programs I've started up via the createProcess procedure.

How would you do this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

一笔一画续写前缘 2024-07-14 18:36:20

停止进程的唯一通用方法是使用 TerminateProcess。 但这远非你能做到的最优雅的。 要优雅地关闭进程,您需要告诉进程您希望它停止,然后希望它服从。 但一般来说没有办法做到这一点。

对于 GUI 程序,告诉它您希望它停止运行的常用方法是关闭其主窗口。 不过,并没有“主窗口”的正式概念。 一个程序可以有零个或多个窗口,并且无法从外部知道您应该关闭哪个窗口以使程序停止工作。

您可以使用 EnumWindows 循环浏览所有窗口并选择其中的窗口属于你的进程。 (它们将是 GetWindowThreadProcessId 提供相同进程的进程CreateProcess 为您提供的 ID。)

关闭窗口可能还不够。 该程序可能会显示一个对话框(提示确认,或要求保存更改等)。 您需要提前知道如何关闭该对话框。

非 GUI 程序也可能有类似的问题。 模拟 Ctrl+C 击键可能就足够了。 不过,它可能会以不同的方式捕获和处理该击键。 它可能有一个菜单系统,要求您输入“Q”来退出程序。

简而言之,除非您事先知道该程序期望如何关闭,否则您无法正常关闭该程序。

The only generic way to stop a process is to use TerminateProcess. But that's as far from graceful as you can get. To gracefully close a process, you need to tell the process that you'd like it to stop, and then hope it obeys. There is no way to do that in general, though.

For a GUI program, the usual way to tell it that you want it to stop running is to close its main window. There's no formal idea of "main window," though. A program can have zero or more windows, and there's no way to know externally which one you're supposed to close in order to make the program stop working.

You could use EnumWindows to cycle through all the windows and select the ones that belong to your process. (They'd be the ones for which GetWindowThreadProcessId gives the same process ID that CreateProcess gave you.)

Closing a window might not be enough. The program might display a dialog box (prompting for confirmation, or asking to save changes, etc.). You would need to know in advance how to dismiss that dialog box.

Non-GUI programs can have similar problems. It might be enough to simulate a Ctrl+C keystroke. It might catch and handle that keystroke differently, though. It might have a menu system that expects you to type "Q" to quit the program.

In short, you cannot gracefully close a program unless you know in advance how that program expects to be closed.

天邊彩虹 2024-07-14 18:36:19

我会使用 JVCLTJvCreateProcess 组件,它包装了与进程相关的任何功能win32 以一种优雅的方式。 这个答案来自 Dont-touch-winapi-unless-really-required 部门:-)

I'd use TJvCreateProcess component of JVCL which wraps about any process related functionality of win32 in a graceful way. This answer comes from Dont-touch-winapi-unless-really-required department :-)

悟红尘 2024-07-14 18:36:19

您想要枚举与您启动的 ProcessId 匹配的打开窗口,并告诉这些窗口关闭。 下面是一些示例代码:

uses Windows;

interface   

function MyTerminateAppEnum(hHwnd:HWND; dwData:LPARAM):Boolean; stdcall;

implementation

function MyTerminateAppEnum(hHwnd:HWND; dwData:LPARAM):Boolean; 
var   
  vID:DWORD; 
begin   
  GetWindowThreadProcessID(hHwnd, @vID);   
  if vID = dwData then   
  begin
    PostMessage(hHwnd, WM_CLOSE, 0, 0); //tell window to close gracefully
    Result := False;  //can stop enumerating    
  end   
  else   
  begin
    Result := TRUE;  //keep enumerating until you find your id   
  end; 
end;

然后,当您想要关闭启动的应用程序时,您将需要在代码中使用它:

Procedure TerminateMe(YourSavedProcessInfo:TProcessInformation);
var
  vExitCode:UINT;
begin
  GetExitCodeProcess(YourSavedProcessInfo.hProcess, vExitCode);
  if (vExitCode = STILL_ACTIVE) then  //launched app still running..
  begin
    //tell it to close
    EnumWindows(@MyTerminateAppEnum, YourSavedProcessInfo.dwProcessId);

    if WaitForSingleObject(YourSavedProcessInfo.hProcess, TIMEOUT_VAL) <> WAIT_OBJECT_0 then
    begin
      if not TerminateProcess(YourSavedProcessInfo.hProcess, 0) then  //didn't close, try to terminate
      begin
         //uh-oh   Didn't close, didn't terminate..
      end;
    end;
  end;
  CloseHandle(YourSavedProcessInfo.hProcess);
  CloseHandle(YourSavedProcessInfo.hThread);
end;

You want to enumerate open windows that match your launched ProcessId and tell those windows to close. Here's some sample code for that:

uses Windows;

interface   

function MyTerminateAppEnum(hHwnd:HWND; dwData:LPARAM):Boolean; stdcall;

implementation

function MyTerminateAppEnum(hHwnd:HWND; dwData:LPARAM):Boolean; 
var   
  vID:DWORD; 
begin   
  GetWindowThreadProcessID(hHwnd, @vID);   
  if vID = dwData then   
  begin
    PostMessage(hHwnd, WM_CLOSE, 0, 0); //tell window to close gracefully
    Result := False;  //can stop enumerating    
  end   
  else   
  begin
    Result := TRUE;  //keep enumerating until you find your id   
  end; 
end;

Then you'll want to utilize this in your code when you want to shut down the launched applications:

Procedure TerminateMe(YourSavedProcessInfo:TProcessInformation);
var
  vExitCode:UINT;
begin
  GetExitCodeProcess(YourSavedProcessInfo.hProcess, vExitCode);
  if (vExitCode = STILL_ACTIVE) then  //launched app still running..
  begin
    //tell it to close
    EnumWindows(@MyTerminateAppEnum, YourSavedProcessInfo.dwProcessId);

    if WaitForSingleObject(YourSavedProcessInfo.hProcess, TIMEOUT_VAL) <> WAIT_OBJECT_0 then
    begin
      if not TerminateProcess(YourSavedProcessInfo.hProcess, 0) then  //didn't close, try to terminate
      begin
         //uh-oh   Didn't close, didn't terminate..
      end;
    end;
  end;
  CloseHandle(YourSavedProcessInfo.hProcess);
  CloseHandle(YourSavedProcessInfo.hThread);
end;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文