Implementing the Event Handlers for Command Events in C++
When you subscribe to command events, you must implement a class that derives from IRemoteCommandEvents. The following C++ example shows a class that implements the IRemoteCommandEvents interface. For an example that shows how to subscribe to the events, see Executing Commands.
extern HANDLE g_CommandDoneEvent; // Defined in the "Executing Commands" topic's C++ example
// Dispath IDs that identify the methods for the IRemoteCommandEvents interface.
#define DISPID_0NCOMMANDOUTPUT 0x60020000
#define DISPID_ONCOMMANDRAWOUTPUT 0x60020001
#define DISPID_ONCOMMANDTASKSTATE 0x60020002
#define DISPID_ONCOMMANDJOBSTATE 0x60020003
class CCommandEventHandler : public IRemoteCommandEvents
{
LONG m_lRefCount;
ITypeInfo* m_pTypeInfo;
public:
// Constructor, Destructor
CCommandEventHandler()
{
m_lRefCount = 1;
m_pTypeInfo = NULL;
};
~CCommandEventHandler()
{
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);
// IRemoteCommandEvents methods
void __stdcall OnCommandOutput(VARIANT sender, ICommandOutputEventArg* args);
void __stdcall OnCommandRawOutput(VARIANT sender, ICommandRawOutputEventArg* args);
void __stdcall OnCommandTaskState(VARIANT sender, ICommandTaskStateEventArg* args);
void __stdcall OnCommandJobState(VARIANT sender, IJobStateEventArg* args);
private:
HRESULT LoadTypeInfo(void);
ISchedulerJob* GetCommandJob(VARIANT sender, long JobId);
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 implementation.
HRESULT CCommandEventHandler::QueryInterface(REFIID riid, LPVOID* ppvObj)
{
if (riid == __uuidof(IUnknown) ||
riid == __uuidof(IDispatch) ||
riid == __uuidof(IRemoteCommandEvents))
{
*ppvObj = this;
}
else
{
*ppvObj = NULL;
return E_NOINTERFACE;
}
AddRef();
return NOERROR;
}
ULONG CCommandEventHandler::AddRef()
{
return InterlockedIncrement(&m_lRefCount);
}
ULONG CCommandEventHandler::Release()
{
ULONG ulCount = InterlockedDecrement(&m_lRefCount);
if(0 == ulCount)
{
delete this;
}
return ulCount;
}
// IDispatch implementation.
HRESULT __stdcall CCommandEventHandler::GetTypeInfoCount(UINT* pctInfo)
{
HRESULT hr = S_OK;
if (pctInfo == NULL)
return E_INVALIDARG;
hr = LoadTypeInfo();
if (SUCCEEDED(hr))
*pctInfo = 1;
return hr;
}
HRESULT __stdcall CCommandEventHandler::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 CCommandEventHandler::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 CCommandEventHandler::Invoke(DISPID dispId, REFIID riid, LCID lcid, WORD wFlags,
DISPPARAMS* pDispParams, VARIANT* pvarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr)
{
HRESULT hr = S_OK;
ICommandOutputEventArg* pOutputArgs = NULL;
ICommandRawOutputEventArg* pRawOutputArgs = NULL;
ICommandTaskStateEventArg* pCommandStateArgs = 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_0NCOMMANDOUTPUT :
hr = vArgs.pdispVal->QueryInterface(IID_ICommandOutputEventArg, (void**)&pOutputArgs);
if (SUCCEEDED(hr))
{
OnCommandOutput(vSender, pOutputArgs);
pOutputArgs->Release();
pOutputArgs = NULL;
}
break;
case DISPID_ONCOMMANDRAWOUTPUT :
hr = vArgs.pdispVal->QueryInterface(IID_ICommandRawOutputEventArg, (void**)&pRawOutputArgs);
if (SUCCEEDED(hr))
{
OnCommandRawOutput(vSender, pRawOutputArgs);
pRawOutputArgs->Release();
pRawOutputArgs = NULL;
}
break;
case DISPID_ONCOMMANDTASKSTATE :
hr = vArgs.pdispVal->QueryInterface(IID_ICommandTaskStateEventArg, (void**)&pCommandStateArgs);
if (SUCCEEDED(hr))
{
OnCommandTaskState(vSender, pCommandStateArgs);
pCommandStateArgs->Release();
pCommandStateArgs = NULL;
}
break;
case DISPID_ONCOMMANDJOBSTATE :
hr = vArgs.pdispVal->QueryInterface(IID_IJobStateEventArg, (void**)&pJobStateArgs);
if (SUCCEEDED(hr))
{
OnCommandJobState(vSender, pJobStateArgs);
pJobStateArgs->Release();
pJobStateArgs = NULL;
}
break;
default:
hr = DISP_E_BADINDEX; // Bad dispId
}
return hr;
}
HRESULT CCommandEventHandler::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_IRemoteCommandEvents, &m_pTypeInfo);
pTypeLib->Release();
if (FAILED(hr))
m_pTypeInfo = NULL;
}
return hr;
}
// IRemoteCommandEvents implementation.
// The RemoteCommand object will call all event handler methods - there is no way to
// subscribe to a single event. If you do not want to handle the event, simply return;
void CCommandEventHandler::OnCommandOutput(VARIANT sender, ICommandOutputEventArg* pargs)
{
HRESULT hr = S_OK;
BSTR bstrNodeName = NULL;
BSTR bstrOutput = NULL;
hr = pargs->get_NodeName(&bstrNodeName);
if (FAILED(hr))
{
// Handle error;
goto cleanup;
}
wprintf(L"\nFormatted line output from %s.\n", bstrNodeName);
hr = pargs->get_Message(&bstrOutput);
if (FAILED(hr))
{
// Handle error;
goto cleanup;
}
wprintf(L"<%s>\n", bstrOutput);
cleanup:
if (bstrNodeName)
SysFreeString(bstrNodeName);
if (bstrOutput)
SysFreeString(bstrOutput);
}
void CCommandEventHandler::OnCommandRawOutput(VARIANT sender, ICommandRawOutputEventArg* pargs)
{
HRESULT hr = S_OK;
BSTR bstrNodeName = NULL;
SAFEARRAY* psaBuffer = NULL;
PBYTE pData = NULL;
PBYTE pOutput = NULL;
long cb = 0;
hr = pargs->get_NodeName(&bstrNodeName);
if (FAILED(hr))
{
// Handle error;
goto cleanup;
}
wprintf(L"\nRaw output from %s.\n", bstrNodeName);
hr = pargs->get_Output(&psaBuffer);
if (FAILED(hr))
{
// Handle error;
goto cleanup;
}
if (psaBuffer)
{
hr = SafeArrayAccessData(psaBuffer, (void**)&pData);
cb = psaBuffer->rgsabound->cElements * sizeof(byte);
// The output string is in ASCII, not Unicode.
pOutput = (PBYTE)malloc(cb + 1);
if (pOutput)
{
memcpy_s(pOutput, cb + 1, pData, cb);
*(LPSTR)(pOutput+cb) = '\0';
wprintf(L"<%S>\n\n", (LPSTR)pOutput);
free(pOutput);
pOutput = NULL;
}
else
{
// Handle error;
goto cleanup;
}
}
cleanup:
if (bstrNodeName)
SysFreeString(bstrNodeName);
if (pData)
SafeArrayUnaccessData(psaBuffer);
if (psaBuffer)
SafeArrayDestroy(psaBuffer);
}
void CCommandEventHandler::OnCommandTaskState(VARIANT sender, ICommandTaskStateEventArg* pargs)
{
HRESULT hr = S_OK;
ITaskStateEventArg* pStateArgs = NULL;
TaskState State;
long lExitCode = 0;
BSTR bstrNodeName = NULL;
BSTR bstrMessage = NULL;
VARIANT_BOOL fIsProxyTask = VARIANT_FALSE;
hr = pargs->get_IsProxy(&fIsProxyTask);
if (FAILED(hr))
{
// Handle error;
goto cleanup;
}
// Report state changes for the command, not the proxy task.
if (VARIANT_FALSE == fIsProxyTask)
{
// The state information is contained in the ITaskStateEventArg so you need to query
// pargs for the interface.
hr = pargs->QueryInterface(IID_ITaskStateEventArg, reinterpret_cast<void **> (&pStateArgs));
hr = pargs->get_NodeName(&bstrNodeName);
if (FAILED(hr))
{
// Handle error;
goto cleanup;
}
hr = pStateArgs->get_NewState(&State);
if (FAILED(hr))
{
// Handle error;
goto cleanup;
}
wprintf(L"\nOnCommandTaskState Node: %s State: %s\n", bstrNodeName, GetTaskStateString(State));
if (TaskState_Failed == State)
{
hr = pargs->get_ExitCode(&lExitCode);
if (FAILED(hr))
{
// Handle error;
goto cleanup;
}
hr = pargs->get_ErrorMessage(&bstrMessage);
if (FAILED(hr))
{
// Handle error;
goto cleanup;
}
wprintf(L"Exit code: %ld\nMessage: %s\n", lExitCode, bstrMessage);
}
}
cleanup:
if (pStateArgs)
pStateArgs->Release();
if (bstrNodeName)
SysFreeString(bstrNodeName);
if (bstrMessage)
SysFreeString(bstrMessage);
}
void CCommandEventHandler::OnCommandJobState(VARIANT sender, IJobStateEventArg* pargs)
{
HRESULT hr = S_OK;
ISchedulerJob* pJob = NULL;
long JobId = 0;
JobState State;
hr = pargs->get_NewState(&State);
if (FAILED(hr))
{
// Handle error;
goto cleanup;
}
wprintf(L"\nOnCommandJobState State: %s\n", GetJobStateString(State));
// If you need to do something with the job, remove the comments.
//hr = pargs->get_JobId(&JobId);
//if (FAILED(hr))
//{
// // Handle error;
// goto cleanup;
//}
//pJob = GetCommandJob(sender, JobId);
//if (NULL == pJob)
//{
// // Handle error;
// goto cleanup;
//}
// TODO: Do something with the job.
if (JobState_Finished == State ||
JobState_Failed == State ||
JobState_Canceled == State)
{
SetEvent(g_CommandDoneEvent);
}
cleanup:
if (pJob)
pJob->Release();
}
// Called from CCommandEventHandler::OnCommandJobState.
ISchedulerJob* CCommandEventHandler::GetCommandJob(VARIANT sender, long JobId)
{
HRESULT hr = S_OK;
IScheduler* pScheduler = NULL;
ISchedulerJob* pJob = NULL;
hr = sender.pdispVal->QueryInterface(__uuidof(IScheduler), reinterpret_cast<void **> (&pScheduler));
hr = pScheduler->OpenJob(JobId, &pJob);
if (FAILED(hr))
{
// Handle error;
goto cleanup;
}
cleanup:
if (pScheduler)
pScheduler->Release();
return pJob;
}
// Returns the string associated with the state value.
LPWSTR CCommandEventHandler::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 CCommandEventHandler::GetTaskStateString(TaskState state)
{
DWORD flag = state;
DWORD bitPosition = 0;
for (bitPosition = 0; flag = flag >> 1; bitPosition++)
;
return TaskStateStrings[bitPosition];
}