Implementing the Event Handlers for Job Events in C++
When you subscribe to job events, you must implement a class that derives from ISchedulerJobEvents. The following C++ example shows a class that implements the ISchedulerJobEvents interface. For an example that shows how to subscribe to the events, see Creating and Submitting a Job.
extern HANDLE g_JobDoneEvent; // Defined in the "Creating and Submitting a Job" topic's C++ example
// Dispath IDs that identify the methods for the ISchedulerJobEvents interface.
#define DISPID_0NJOBSTATE 0x60020000
#define DISPID_ONTASKSTATE 0x60020001
class CJobEventHandler : public ISchedulerJobEvents
{
LONG m_lRefCount;
ITypeInfo* m_pTypeInfo;
public:
// Constructor, Destructor
CJobEventHandler()
{
m_lRefCount = 1;
m_pTypeInfo = NULL;
};
~CJobEventHandler()
{
if (m_pTypeInfo)
{
m_pTypeInfo->Release();
m_pTypeInfo = NULL;
}
};
// IUnknown methods
HRESULT __stdcall QueryInterface(REFIID riid, LPVOID *ppvObj);
ULONG __stdcall AddRef();
ULONG __stdcall Release();
// IDispatch methods
HRESULT __stdcall GetTypeInfoCount(UINT* pCount);
HRESULT __stdcall GetTypeInfo(UINT uiTInfoRequest, LCID lcid, ITypeInfo** ppTInfo);
HRESULT __stdcall GetIDsOfNames(REFIID riid, OLECHAR** ppNames, UINT cNames, LCID lcid, DISPID* pDispId);
HRESULT __stdcall Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags,
DISPPARAMS* pDispParams, VARIANT* pvarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr);
// ISchedulerJobEvents methods
void __stdcall OnJobState(VARIANT sender, IJobStateEventArg* args);
void __stdcall OnTaskState(VARIANT sender, ITaskStateEventArg* args);
private:
HRESULT LoadTypeInfo(void);
LPWSTR GetJobStateString(JobState state);
LPWSTR GetTaskStateString(TaskState state);
};
// Strings associated with the job state values.
LPWSTR JobStateStrings[] = {
L"Configuring", L"Submitted", L"Validating",
L"External validation", L"Queued", L"Running",
L"Finishing", L"Finished", L"Failed",
L"Canceled", L"Canceling"
};
// Strings associated with the task state values.
LPWSTR TaskStateStrings[] = {
L"Configuring", L"Submitted", L"Validating",
L"Queued", L"Dispatching", L"Running",
L"Finishing", L"Finished", L"Failed",
L"Canceled", L"Canceling"
};
// IUnknown methods implementation.
HRESULT CJobEventHandler::QueryInterface(REFIID riid, LPVOID* ppvObj)
{
if (riid == __uuidof(IUnknown) ||
riid == __uuidof(IDispatch) ||
riid == __uuidof(ISchedulerJobEvents))
{
*ppvObj = this;
}
else
{
*ppvObj = NULL;
return E_NOINTERFACE;
}
AddRef();
return NOERROR;
}
ULONG CJobEventHandler::AddRef()
{
return InterlockedIncrement(&m_lRefCount);
}
ULONG CJobEventHandler::Release()
{
ULONG ulCount = InterlockedDecrement(&m_lRefCount);
if(0 == ulCount)
{
delete this;
}
return ulCount;
}
// IDispatch methods implementation.
HRESULT __stdcall CJobEventHandler::GetTypeInfoCount(UINT* pctInfo)
{
HRESULT hr = S_OK;
if (pctInfo == NULL)
return E_INVALIDARG;
hr = LoadTypeInfo();
if (SUCCEEDED(hr))
*pctInfo = 1;
return hr;
}
HRESULT __stdcall CJobEventHandler::GetTypeInfo(UINT itInfo, LCID lcid, ITypeInfo** ppTInfo)
{
HRESULT hr = S_OK;
if (ppTInfo == NULL)
return E_POINTER;
if(itInfo != 0)
return DISP_E_BADINDEX;
*ppTInfo = NULL;
hr = LoadTypeInfo();
if (SUCCEEDED(hr))
{
m_pTypeInfo->AddRef();
*ppTInfo = m_pTypeInfo;
}
return hr;
}
HRESULT __stdcall CJobEventHandler::GetIDsOfNames(REFIID riid, OLECHAR** rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId)
{
HRESULT hr = S_OK;
hr = LoadTypeInfo();
if (FAILED(hr))
return hr;
return (m_pTypeInfo->GetIDsOfNames(rgszNames, cNames, rgDispId));
}
// Invoke the event handler methods directly.
HRESULT __stdcall CJobEventHandler::Invoke(DISPID dispId, REFIID riid, LCID lcid, WORD wFlags,
DISPPARAMS* pDispParams, VARIANT* pvarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr)
{
HRESULT hr = S_OK;
ITaskStateEventArg* pTaskStateArgs = NULL;
IJobStateEventArg* pJobStateArgs = NULL;
_variant_t vSender;
_variant_t vArgs;
if (2 != pDispParams->cArgs)
return E_INVALIDARG;
vSender = pDispParams->rgvarg[1]; // IScheduler
vArgs = pDispParams->rgvarg[0]; // Arguments
switch (dispId)
{
case DISPID_ONTASKSTATE :
hr = vArgs.pdispVal->QueryInterface(IID_ITaskStateEventArg, (void**)&pTaskStateArgs);
if (SUCCEEDED(hr))
{
OnTaskState(vSender, pTaskStateArgs);
pTaskStateArgs->Release();
pTaskStateArgs = NULL;
}
break;
case DISPID_0NJOBSTATE :
hr = vArgs.pdispVal->QueryInterface(IID_IJobStateEventArg, (void**)&pJobStateArgs);
if (SUCCEEDED(hr))
{
OnJobState(vSender, pJobStateArgs);
pJobStateArgs->Release();
pJobStateArgs = NULL;
}
break;
default:
hr = DISP_E_BADINDEX; // Bad dispId
}
return hr;
}
HRESULT CJobEventHandler::LoadTypeInfo(void)
{
HRESULT hr;
ITypeLib* pTypeLib = NULL;
if (m_pTypeInfo)
return S_OK;
// Load type library.
hr = LoadRegTypeLib(LIBID_Microsoft_Hpc_Scheduler, 2, 0, LOCALE_USER_DEFAULT, &pTypeLib);
if (SUCCEEDED(hr))
{
// Get type information for interface of the object.
hr = pTypeLib->GetTypeInfoOfGuid(DIID_ISchedulerJobEvents, &m_pTypeInfo);
pTypeLib->Release();
if (FAILED(hr))
m_pTypeInfo = NULL;
}
return hr;
}
// ISchedulerJobEvents implementation
// The SchedulerJob object will call both handler methods. If you do not want
// to handle the event, simply return.
void CJobEventHandler::OnJobState(VARIANT sender, IJobStateEventArg* pargs)
{
HRESULT hr = S_OK;
IScheduler* pScheduler = NULL;
ISchedulerJob* pJob = NULL;
JobState State;
long JobId = 0;
hr = pargs->get_NewState(&State);
if (FAILED(hr))
{
// Handle error;
goto cleanup;
}
hr = pargs->get_JobId(&JobId);
if (FAILED(hr))
{
// Handle error;
goto cleanup;
}
wprintf(L"\nOnJobState: State for job %d is %s\n", JobId, GetJobStateString(State));
hr = sender.pdispVal->QueryInterface(__uuidof(IScheduler), reinterpret_cast<void **> (&pScheduler));
hr = pScheduler->OpenJob(JobId, &pJob);
if (FAILED(hr))
{
// Handle error;
goto cleanup;
}
// TODO: Do something with the job.
if (JobState_Finished == State ||
JobState_Failed == State ||
JobState_Canceled == State)
{
SetEvent(g_JobDoneEvent);
}
cleanup:
if (pScheduler)
pScheduler->Release();
if (pJob)
pJob->Release();
}
void CJobEventHandler::OnTaskState(VARIANT sender, ITaskStateEventArg* pargs)
{
HRESULT hr = S_OK;
IScheduler* pScheduler = NULL;
ISchedulerJob* pJob = NULL;
ISchedulerTask* pTask = NULL;
ITaskId* pTaskId = NULL;
TaskState State;
BSTR bstr = NULL;
long JobId = 0;
long JobTaskId = 0;
long InstanceId = 0;
hr = pargs->get_NewState(&State);
if (FAILED(hr))
{
// Handle error;
goto cleanup;
}
hr = pargs->get_JobId(&JobId);
if (FAILED(hr))
{
// Handle error;
goto cleanup;
}
hr = pargs->get_TaskId(&pTaskId);
if (FAILED(hr))
{
// Handle error;
goto cleanup;
}
hr = pTaskId->get_JobTaskId(&JobTaskId);
if (FAILED(hr))
{
// Handle error;
goto cleanup;
}
hr = pTaskId->get_InstanceId(&InstanceId);
if (FAILED(hr))
{
// Handle error;
goto cleanup;
}
if (InstanceId)
wprintf(L"\nOnTaskState: State for instance %d of task %d is %s\n",
InstanceId, JobTaskId, GetTaskStateString(State));
else
wprintf(L"\nOnTaskState: State for task %d is %s\n", JobTaskId, GetTaskStateString(State));
hr = sender.pdispVal->QueryInterface(__uuidof(IScheduler), reinterpret_cast<void **> (&pScheduler));
// If the task is finished or failed, get the output.
if (TaskState_Finished == State || TaskState_Failed == State)
{
hr = pScheduler->OpenJob(JobId, &pJob);
if (FAILED(hr))
{
// Handle error;
goto cleanup;
}
hr = pJob->OpenTask(pTaskId, &pTask);
if (FAILED(hr))
{
// Handle error;
goto cleanup;
}
hr = pTask->get_Output(&bstr);
if (FAILED(hr))
{
// Handle error;
goto cleanup;
}
wprintf(L"Output from task:\n%s\n\n", bstr);
}
cleanup:
if (pScheduler)
pScheduler->Release();
if (pJob)
pJob->Release();
if (pTaskId)
pTaskId->Release();
if (bstr)
SysFreeString(bstr);
}
// Returns the string associated with the state value.
LPWSTR CJobEventHandler::GetJobStateString(JobState state)
{
DWORD flag = state;
DWORD bitPosition = 0;
for (bitPosition = 0; flag = flag >> 1; bitPosition++)
;
return JobStateStrings[bitPosition];
}
// Returns the string associated with the state value.
LPWSTR CJobEventHandler::GetTaskStateString(TaskState state)
{
DWORD flag = state;
DWORD bitPosition = 0;
for (bitPosition = 0; flag = flag >> 1; bitPosition++)
;
return TaskStateStrings[bitPosition];
}