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?
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);
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With