Was this page helpful?
Your feedback about this content is important. Let us know what you think.
Additional feedback?
1500 characters remaining
Export (0) Print
Expand All

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


 

 

Show:
© 2015 Microsoft