Anders is correct, WMI works nicely for this. Since I needed this for a project I can share the code for detecting (arbitrary) process termination (given its ID):
ProcessTerminationNotification.h:
#ifndef __ProcessTerminationNotification_h__
#define __ProcessTerminationNotification_h__
#include <boost/function.hpp>
namespace ProcessTerminationNotification
{
typedef boost::function< void(void) > TNotificationFunction;
void registerTerminationCallback(TNotificationFunction callback, unsigned processId);
}
#endif // __ProcessTerminationNotification_h__
ProcessTerminationNotification.cpp:
#define _WIN32_DCOM
#include <iostream>
using namespace std;
#include <comdef.h>
#include <Wbemidl.h>
#include <atlcomcli.h>
#pragma comment(lib, "wbemuuid.lib")
#include "ProcessTerminationNotification.h"
class EventSink : public IWbemObjectSink
{
friend void ProcessTerminationNotification::registerTerminationCallback(TNotificationFunction callback, unsigned processId);
CComPtr<IWbemServices> pSvc;
CComPtr<IWbemObjectSink> pStubSink;
LONG m_lRef;
ProcessTerminationNotification::TNotificationFunction m_callback;
public:
EventSink(ProcessTerminationNotification::TNotificationFunction callback)
: m_lRef(0)
, m_callback(callback)
{}
~EventSink()
{}
virtual ULONG STDMETHODCALLTYPE AddRef()
{
return InterlockedIncrement(&m_lRef);
}
virtual ULONG STDMETHODCALLTYPE Release()
{
LONG lRef = InterlockedDecrement(&m_lRef);
if (lRef == 0)
delete this;
return lRef;
}
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppv)
{
if (riid == IID_IUnknown || riid == IID_IWbemObjectSink)
{
*ppv = (IWbemObjectSink *) this;
AddRef();
return WBEM_S_NO_ERROR;
}
else return E_NOINTERFACE;
}
virtual HRESULT STDMETHODCALLTYPE Indicate(
LONG lObjectCount,
IWbemClassObject __RPC_FAR *__RPC_FAR *apObjArray
)
{
m_callback();
/* Unregister event sink since process is terminated */
pSvc->CancelAsyncCall(pStubSink);
return WBEM_S_NO_ERROR;
}
virtual HRESULT STDMETHODCALLTYPE SetStatus(
/* [in] */ LONG lFlags,
/* [in] */ HRESULT hResult,
/* [in] */ BSTR strParam,
/* [in] */ IWbemClassObject __RPC_FAR *pObjParam
)
{
return WBEM_S_NO_ERROR;
}
};
void ProcessTerminationNotification::registerTerminationCallback( TNotificationFunction callback, unsigned processId )
{
CComPtr<IWbemLocator> pLoc;
HRESULT hres = CoCreateInstance(
CLSID_WbemLocator,
0,
CLSCTX_INPROC_SERVER,
IID_IWbemLocator,
(LPVOID*)&pLoc);
if (FAILED(hres))
{
cout << "Failed to create IWbemLocator object. "
<< "Err code = 0x"
<< hex << hres << endl;
throw std::exception("ProcessTerminationNotificaiton initialization failed");
}
// Step 4: ---------------------------------------------------
// Connect to WMI through the IWbemLocator::ConnectServer method
CComPtr<EventSink> pSink(new EventSink(callback));
// Connect to the local root\cimv2 namespace
// and obtain pointer pSvc to make IWbemServices calls.
hres = pLoc->ConnectServer(
_bstr_t(L"ROOT\\CIMV2"),
NULL,
NULL,
0,
NULL,
0,
0,
&pSink->pSvc
);
if (FAILED(hres))
{
cout << "Could not connect. Error code = 0x"
<< hex << hres << endl;
throw std::exception("ProcessTerminationNotificaiton initialization failed");
}
// Step 5: --------------------------------------------------
// Set security levels on the proxy -------------------------
hres = CoSetProxyBlanket(
pSink->pSvc, // Indicates the proxy to set
RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx
RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx
NULL, // Server principal name
RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx
RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
NULL, // client identity
EOAC_NONE // proxy capabilities
);
if (FAILED(hres))
{
cout << "Could not set proxy blanket. Error code = 0x"
<< hex << hres << endl;
throw std::exception("ProcessTerminationNotificaiton initialization failed");
}
// Step 6: -------------------------------------------------
// Receive event notifications -----------------------------
// Use an unsecured apartment for security
CComPtr<IUnsecuredApartment> pUnsecApp;
hres = CoCreateInstance(CLSID_UnsecuredApartment, NULL,
CLSCTX_LOCAL_SERVER, IID_IUnsecuredApartment,
(void**)&pUnsecApp);
CComPtr<IUnknown> pStubUnk;
pUnsecApp->CreateObjectStub(pSink, &pStubUnk);
pStubUnk->QueryInterface(IID_IWbemObjectSink,
(void **) &pSink->pStubSink);
// The ExecNotificationQueryAsync method will call
// The EventQuery::Indicate method when an event occurs
char buffer[512];
sprintf_s(buffer, "SELECT * "
"FROM __InstanceDeletionEvent WITHIN 1 "
"WHERE TargetInstance ISA 'Win32_Process' AND TargetInstance.ProcessId=%u", processId);
hres = pSink->pSvc->ExecNotificationQueryAsync(
_bstr_t("WQL"),
_bstr_t(buffer),
WBEM_FLAG_SEND_STATUS,
NULL,
pSink->pStubSink);
// Check for errors.
if (FAILED(hres))
{
cout << "ExecNotificationQueryAsync failed "
"with = 0x" << hex << hres << endl;
throw std::exception("ProcessTerminationNotificaiton initialization failed");
}
}
Note that the code to initialize COM and COM process security (CoInitializeEx and CoInitializeSecurity) is omitted here since it should be done in the application initialization.
Use it with global functions or use boost::bind to connect to an arbitrary method, example of the latter:
class MyClass
{
public:
void myProcessTerminationCallback() { cout << "Wohoo!!" << endl; }
};
ProcessTerminationNotification::registerTerminationCallback(
boost::bind(&MyClass::myProcessTerminationCallback, <pointer to MyClass instance>),
1234); // Process ID = 1234