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

时间:2008-11-06 10:19:53

标签: delphi windows-services createprocess

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

在停止服务时,我还要关闭这些程序。当服务最初编写时,这工作正常,但我认为我已经更新了tProcess组件,现在 - 下级程序没有被关闭。

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程序启动的程序。

你会怎么做?

3 个答案:

答案 0 :(得分:4)

我使用JVCL TJvCreateProcess 组件,它以优雅的方式包装了win32的任何与进程相关的功能。这个答案来自Dont-touch-winapi - 除非真正需要的部门: - )

答案 1 :(得分:1)

停止进程的唯一通用方法是使用TerminateProcess。但这远不是你能得到的优雅。要优雅地关闭流程,您需要告诉流程您希望它停止,然后希望它遵守。但是,一般情况下无法做到这一点。

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

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

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

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

简而言之,除非您事先知道该程序预计会如何关闭,否则您无法优雅地关闭程序。

答案 2 :(得分:1)

您希望枚举与已启动的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;