Can I get notification that a process I did not spawn is shutting down in Windows XP/7?
Asked Answered
E

3

5

I have a Delphi 6 application that works with the Skype API. I want to know when the Skype client has shut down even though my software did not launch it (so I don't have a process handle for it). This way I can know if the user shut down the Skype client I can get the process ID for the Skype client fairly easily, so is there a Windows API call or other technique that accepts a process ID where I can get a notification when the process (Skype client) has terminated?

If not, is there a WinApi call that I can use to poll Windows to see if the process ID is still valid, or does Windows reuse process IDs so there's a chance that I could end up with a process ID belonging to a recently launched process that is not the Skype client, which would invalidate my polling efforts?

Elapse answered 2/2, 2012 at 22:31 Comment(0)
C
12

Call OpenProcess to get a process handle. The SYNCHRONIZE access right will probably be enough. Then wait on the handle. Something like:

HANDLE hProcess = OpenProcess(SYNCHRONIZE, FALSE, pid);
WaitForSingleObject(hProcess, INFINITE);
CloseHandle(hProcess);
Concoff answered 2/2, 2012 at 22:49 Comment(0)
C
7

You can use the __InstanceDeletionEvent WMI intrinsic event to monitor the Win32_Process class and the filter by the ProcessId property, this event run in async mode in your code.

Check this sample code (Written in delphi XE2, but must work in delphi 6 without problems)

Note : You must import the Microsoft WMI Scripting V1.2 Library before to use it.

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, WbemScripting_TLB;

type
  TWmiAsyncEvent = class
  private
    FWQL      : string;
    FSink     : TSWbemSink;
    FLocator  : ISWbemLocator;
    FServices : ISWbemServices;
    procedure EventReceived(ASender: TObject; const objWbemObject: ISWbemObject; const objWbemAsyncContext: ISWbemNamedValueSet);
  public
    procedure  Start;
    constructor Create(Pid : DWORD);
    Destructor Destroy;override;
  end;

  TFrmDemo = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    AsyncEvent : TWmiAsyncEvent;
  public
    { Public declarations }
  end;

var
  FrmDemo: TFrmDemo;

implementation

{$R *.dfm}

uses
 ActiveX;

{ TWmiAsyncEvent }

constructor TWmiAsyncEvent.Create(Pid: DWORD);
begin
  inherited Create;
  CoInitializeEx(nil, COINIT_MULTITHREADED);
  FLocator  := CoSWbemLocator.Create;
  FServices := FLocator.ConnectServer('.', 'root\CIMV2', '', '', '', '', wbemConnectFlagUseMaxWait, nil);
  FSink     := TSWbemSink.Create(nil);
  FSink.OnObjectReady := EventReceived;
  //construct the WQL sentence with the pid to monitor
  FWQL:=Format('Select * From __InstanceDeletionEvent Within 1 Where TargetInstance ISA "Win32_Process"  And TargetInstance.ProcessId=%d',[Pid]);

end;

destructor TWmiAsyncEvent.Destroy;
begin
  if FSink<>nil then
    FSink.Cancel;
  FLocator  :=nil;
  FServices :=nil;
  FSink     :=nil;
  CoUninitialize;
  inherited;
end;

procedure TWmiAsyncEvent.EventReceived(ASender: TObject;
  const objWbemObject: ISWbemObject;
  const objWbemAsyncContext: ISWbemNamedValueSet);
var
  PropVal: OLEVariant;
begin      
  PropVal := objWbemObject;
  //do something when the event is received.
  ShowMessage(Format('The Application %s Pid %d was finished',[String(PropVal.TargetInstance.Name), Integer(PropVal.TargetInstance.ProcessId)]));
end;


procedure TWmiAsyncEvent.Start;
begin
 FServices.ExecNotificationQueryAsync(FSink.DefaultInterface,FWQL,'WQL', 0, nil, nil);
end;

procedure TFrmDemo.FormCreate(Sender: TObject);
begin
    //here you must pass the pid of the process
    AsyncEvent:=TWmiAsyncEvent.Create(1852);
    AsyncEvent.Start;
end;

procedure TFrmDemo.FormDestroy(Sender: TObject);
begin
  AsyncEvent.Free;
end;

end.

For more info you can check this article Delphi and WMI Events

Calisaya answered 3/2, 2012 at 1:2 Comment(0)
V
1

Windows does reuse process IDs, so do not rely on that by itself.

You can use EnumProcesses() to know which processes are currently running, then grab their filenames and process IDs, etc. See this example on MSDN.

Volscian answered 3/2, 2012 at 2:24 Comment(5)
Question is about notification on shutdown. I hope you aren't proposing polling?Trodden
Yes, I was, and the OP did ask about that possibility. I was unaware of __InstanceDeletionEvent.Volscian
Well, waiting on the process handle looks like the best solution to me.Trodden
Sure - if you start with the correct process ID to begin with. If you are not spawning the process yourself, then there is always the risk of timing if the process terminates after you get the ID but before you open the handle. At the very least, once you do have the handle open, double check the filename associated with it before doing anything else with it.Volscian
Exact same comments apply to your answer.Trodden

© 2022 - 2024 — McMap. All rights reserved.