2010年12月9日木曜日

プロセスの再起動

仕事で、プロセスを外部から強制的に再起動を
する必要があったのでとりあえずつくってみた。

停止させるプロセスは、仕様上一意性が保障されている
ので、コマンドライン引数に起動するプロセスの絶対パスを
与えて、そこからプロセスIDを求めています。

プロセスに停止メッセージ(メインウインドウのクローズ)を
ポストし、5秒まっても終了していなかったら
強制終了しています。

プロセスの起動には、JvCreateProcessコンポーネントを
使っています。
このコンポーネントは、非常に便利ですね。




program RestartProcess;

{$APPTYPE CONSOLE}

uses
  SysUtils,Windows,TLHELP32,Messages,JvCreateProcess;


function GetProcessFromName(ProcessName :String) : Cardinal;
var
   ProcEntry : TProcessEntry32;
   SanpshotHandle : THandle;
   ListProcName : String;

begin
   //Toolhelp32を使用する例
  Result := 0;
   SanpshotHandle := TlHelp32.CreateToolhelp32Snapshot(TlHelp32.TH32CS_SNAPPROCESS,0);
   if (SanpshotHandle <> -1) then
      begin
         ProcEntry.dwSize := Sizeof(TProcessEntry32W);
         if (TlHelp32.Process32First(SanpshotHandle,ProcEntry)) Then
         begin
            repeat
              ListProcName := ProcEntry.szExeFile;
              if CompareText(ListProcName,ProcessName) = 0 then
              begin
                 Result := ProcEntry.th32ProcessID;
              end;
              //WriteLn(ListProcName);
          until (TlHelp32.Process32Next(SanpshotHandle,ProcEntry) = false);
       end;
    end;
    CloseHandle(SanpshotHandle);

end;


function EnumWindowsProc(hwindow :HWnd; lparam :LPARAM):BOOL; stdcall;
var
  ProcessID : Cardinal;
  ThreadID : Cardinal;
begin

   Result := True;

   ThreadID := GetWindowThreadProcessId(hwindow, ProcessID);

   If (ProcessID = lParam) Then
   begin
      PostMessage(hwindow, WM_CLOSE, 0, 0);
     Result := true;
   End;
End;


function SendClose(ProcID : Cardinal) : Boolean;
begin
   Result := EnumWindows(@EnumWindowsProc, ProcID)
End;

function StopProcess(ProcessName : String; Force : Boolean = false) : Integer;
var
   ProcessID : Cardinal;
  hProcess : THandle;
begin
   ProcessID := GetProcessFromName(ProcessName);
  if ProcessID = 0 then
  begin
     Result := -1;
  end
  else
  begin
     if (ProcessID > 0) Then
     begin
        if Force then
        begin
           hProcess := OpenProcess(PROCESS_TERMINATE, False, ProcessID);
           TerminateProcess(hProcess , 0 );
           CloseHandle(hProcess);
           Result := 0;
        end
        else
        begin
           Result := 1;
           if SendClose(ProcessID) then
           begin
              Result := 0;
           end;
        end;
     end;
  end;
end;

var
   StopResult : Integer;
   JvCreateProcess: TJvCreateProcess;
  ExeName : String;
  ProcessID : Cardinal;

begin
  try
  { TODO -oUser -cConsole Main : ここにコードを記述してください }

     if ParamCount > 0 then
     begin
        ExeName := ExtractFileName(ParamStr(1));
         StopResult := StopProcess(ExeName);

        //五秒まって停止イしたかどうかを確認する
        Sleep(5000);

        ProcessID := GetProcessFromName(ExeName);

        //プロセスが正常に停止できなかったので' +
        //強制終了する
        if ProcessID > 0 Then
        begin
           StopResult := StopProcess(ExeName,true);
           Sleep(10000);
        end;


        if StopResult <> 1 then
        begin
           JvCreateProcess := TJvCreateProcess.Create(nil);
           try
              JvCreateProcess.CommandLine := ParamStr(1);
              JvCreateProcess.WaitForTerminate := false;
              JvCreateProcess.Run;
           finally
              JvCreateProcess.Free;
            end;
        end;
     end;
     //ReadLn;
  except
    on E:Exception do
      Writeln(E.Classname, ': ', E.Message);
  end;
end.