Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A Helper Class for Calling Invoke

0.00/5 (No votes)
6 May 2002 2  
SRComHelper makes it easier to call invoke specially in cases where there are several calls to Invoke

Introduction

One of the advantages of COM is the ability to dynamically link to other COM objects. I learnt to take advantage of this aspect during a project when customers would start saying "Can you find a way to call some program just before you run your program or just after." And of course each customer wanted a different process called. To cater to their needs we (I must include fellow developer and friend Estelle Mangeney who helped in creating this class) started placing calls to functions such as OnStart and OnEnd (in reality there were more than 100 such functions called from various objects in our code). For each function I defined and documented the prototype and told the customers that as long as they had a COM object (The ProgID of which was in the database) I would call the functions.

The problem was that I hated having to write all the code for calling Invoke for each of these functions. Each time I wrote the call to a new function using Invoke I made new errors and the whole process was rather painful. The result was SRComHelper a class that provides a slightly friendlier interface. I say slightly as I believe it can be made even better but I am often too busy and way too lazy.

Calling Invoke

The process of having to call Invoke for a function can be tedious. For example if there was a COM object SampleComDLL.DLL with a progID of "SampleComDLL.ComTest" with a function 

HRESULT Sum(VARIANT num1, VARIANT num2, [out, retval] VARIANT* sum) 

then the process of calling this function would be somewhat like:

void CallInvoke() {

//get the numbers to add

        int number1 = 10;
        int number2 = 20;
        int sum1And2 = 0;
//Create an instance of the object

    CLSID clsid;
        IDispatchPtr disp;
    HRESULT result = CLSIDFromProgID( progID, &clsid );
    if( SUCCEEDED( result ) ) {
        result = disp.CreateInstance(clsid);
        }
        else {
                Some error stuff;
        }
//Now Get ready to call the function


    EXCEPINFO *pExcepInfo = NULL;
    unsigned int *puArgErr = NULL;
        DISPID functionID[1];
        VARIANT dispRes;
        DISPPARAMS dispparams;
    dispparams.rgvarg[0].vt = VT_I2;
    dispparams.rgvarg[0].iVal = number2;  

        //and try to remember the reverse order


    dispparams.rgvarg[1].vt = VT_I2;
    dispparams.rgvarg[1].iVal = number1;
    dispparams.cArgs = 2;
    dispparams.cNamedArgs = 0;

    LPOLESTR GetFName[1] = {"Sum"}; //name of the function to call

    hr = disp->GetIDsOfNames(IID_NULL, GetFName, 1, LOCALE_SYSTEM_DEFAULT,
                             functionID);

    try {
        if( SUCCEEDED(hr) ) {
            hr = disp->Invoke( functionID[0], IID_NULL,
                               LOCALE_SYSTEM_DEFAULT, 
                               DISPATCH_METHOD, 
                               &dispparams, &dispRes, 
                               pExcepInfo, puArgErr );
        }
    }
    catch(_com_error* e) {
//                 Some Error Stuff        

    }
        sum1And2 = dispRes.iVal  //Get rthe resulting sum of the two numbers

}

SRComHelper

SRComHelper is class that encapsulates the above process and provides the user with a much simpler interface. The class provides a series of SetParam functions that a user can use to set the arguments of the function to call. A call to the Sum() function above using SRComHelper looks like:

int num1 = 3;
int num2 = 7;
int sum = 0;
int sumProduct = 0;

//Create the COM Helpper class with the progid of the com object

CSRComHelper comHelper( "SampleComDLL.ComTest" );
comHelper.CreateArray( 2 );  //Set up for a function with 2 arguments


//Now set the inputs

comHelper.SetParam( num1 );  //Set the values for the args

comHelper.SetParam( num2 );

CComVariant retVal ("");
CString sFunctionName( "Sum" );  //Set the name of the function to call


HRESULT hr = S_OK;
hr = comHelper.CallInvoke( sFunctionName.AllocSysString(), 
                           retVal ); //Invoke it

sum = retVal.iVal;
SRComHelper gets a little trickier to use when you have a return value and an argument passed as a pointer. Lets say we had a function 
HRESULT SumAndMultiply(VARIANT num1, VARIANT num2,
                       [out] VARIANT* pProduct,
                       [out, retval] VARIANT* pSum).

SumAndMultiply returns the sum of two numbers and the product. The call to Sum followed by a call to SumAndMultiply using SRComHelper would be:

//Create the COM Helpper class with the progid of the com object

CSRComHelper comHelper( "SampleComDLL.ComTest" );
int num1 = 3;
int num2 = 7;
int sum = 0;
int sumProduct = 0;

comHelper.CreateArray( 2 );
//Now set the inputs

comHelper.SetParam( num1 );
comHelper.SetParam( num2 );

CComVariant retVal ("");
CString sFunctionName( "Sum" );

HRESULT hr = S_OK;
hr = comHelper.CallInvoke( sFunctionName.AllocSysString(), retVal );
sum = retVal.iVal;

comHelper.Reset();      //This resets SRComHelper and sets it up for a new call

comHelper.CreateArray( 3 );
//Now set the inputs

comHelper.SetParam( num1 );
comHelper.SetParam( num2 );
comHelper.SetParam( &sumProduct );

CComVariant retVal2;
sFunctionName = CString( "SumAndMultiply" );
hr = comHelper.CallInvoke( sFunctionName.AllocSysString(), retVal2 );
VARIANT res2;
res2 = comHelper.GetOutput( 2 );  //This is the tricky part

SRComHelper is not very elegant when getting the value of an argument passed as a pointer. One has to call the GetOutput( int index ) function. It is, however, still better than having to write all the code behind the call to Invoke each time you have a new function.

I have not gone into much detail regarding the code of SRComHelper as it is rather self explanatory (See code available for download). I use this class in a DLL with some other tools that I have built. The class can be directly part of your project or in a DLL. Hope it comes to some good use and saves you some time. Also, not all data types are included in the SetParams functions as I have been adding them as I need them. But the remaining should be easy to add. Also, please note that I treat each call to Invoke as a call to a Method.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here