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];
}