2007. szeptember 17., hétfő

Get notified when another application terminates


Problem/Question/Abstract:

My application is starting processes invoking ShellExecute API calls. These processes are manipulating files stored in a database BLOB field. I'd like to be notified when the external application terminates in order to save shanges made on the file. My idea was to use windows hooks, but I didn't guess exactly how. ShellExecute returns the Instance Handle of the application that was run (or DDE handle).

Answer:

Solve 1:

How about using the WndProc procedure and listening for the WM_QUERYENDSESSION in the main application, and broadcasting another message to the other one. Then your applications do what they need to do when they receive the message. For example:

{MainApp:}

const
  EndAppMsg = $FFF8B;

  public

  procedure WndProc(var Msg: TMessage); override;

procdure TMainForm.WndProc(var Msg: TMessage);
begin
  if Msg.Msg = WM_QUERYENDSESSION then
  begin
    BroadcastSystemMessage(BSF_POSTMESSAGE, BSM_ALLCOMPONENTS, EndAppMsg, 0, 0)
  end
  else
    inherited;
end;

And then use the WndProc again in the other applications that the main application calls, and listen for the EndAppMsg message. When it is recived execute the code that you need to execute. For example:

{Client/Worker App}

procedure WndProc(var Msg: Tmessage);
const
  EnaAppMsg = $FFF8B;
begin
  if Msg.Msg = EndAppMsg then
  begin
    {Execute your code here or call another procedure to execute the code}
  end
  else
    inherited;
end;


Solve 2:

You might try using ShellExecuteEx instead, then using the returned hProcess in a WaitForSingleObject call. Here's an example I used in a small demo application:

uses
  Windows, ShellApi;

var
  Info: TShellExecuteInfo;
begin
  FillChar(Info, SizeOf(Info), 0);
  Info.cbSize := SizeOf(Info);
  Info.fMask := SEE_MASK_NOCLOSEPROCESS;
  Info.Wnd := Handle;
  Info.lpVerb := 'open';
  Info.lpFile := 'C:\SomeTextFile.Text'; {Change me!}
  Info.lpParameters := nil;
  Info.lpDirectory := nil;
  Info.nShow := SW_SHOW;
  if (ShellExecuteEx(@Info)) then
  begin
    WaitForSingleObject(Info.hProcess, INFINITE);
    MessageBox(Handle, 'You closed the app I launched!', 'Finished!', MB_OK);
    CloseHandle(Info.hProcess);
  end;
end;

I'm not sure if you want your entire application becoming unresponsive while the launched application is running (the WaitForSingleObject call doesn't return until the application is closed), but if you don't, you might consider launching individual threads for each ShellExecuteEx, then using WaitForSingleObject in those threads (and writing an OnTerminate handler for the thread object to determine when the application finally finished).


Nincsenek megjegyzések:

Megjegyzés küldése