View Raw SPL
/*****************************************************************************
*                                                                            *
*   DSPDISP.CPP  Copyright (C) 2001-2004 DSP Development Corporation         *
*                               All Rights Reserved                          *
*                                                                            *
*   Author:      Randy Race                                                  *
*                                                                            *
*   Synopsis:    Example of calling DADiSP from C++ using IDispatch          *
*                (standard Automation)                                       *
*                                                                            *
*   Revisions:   18 Jan 2001  RRR  Creation                                  *
*                 4 Nov 2004  RRR  V_VT macros for variants                  *
*                                                                            *
*****************************************************************************/

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


/* forward declarations */
int       CstrToBstr(char *str, BSTR *pbstr);
char      *BstrToCstr(BSTR bstr);
int       strncpyfar(char *l, LPCOLESTR f, int n);
BOOL      ANSIToUnicode(PCSTR szStrA, PWSTR *pszStrW);
SAFEARRAY *DarrayToSafeArray(double *s, int nrow, int ncol);
void      ShowException(LPOLESTR szMember, HRESULT hr, EXCEPINFO *pexcep, unsigned int uiArgErr);
void      trace(char *msg);
void      DisplayVariant(VARIANT *pvar);
void      DisplayArray(SAFEARRAY *psa, int vtype);
void      CreateRandomArray(double *darray, int size);


/* defines */
#define STRSIZE    255
#define ARRAY_SIZE   4
#define MAX_PCOLS    8

double darray[ARRAY_SIZE];


/*
 *  DADiSP provides a COM dual interface to support Automation. This
 *  example demonstrates invoking DADiSP as an Automation Server 
 *  via the ActiveX standard IDispatch interface. 
 * 
 *  Using IDispatch/Invoke is standard for interpreted languages such
 *  as Visual Basic and even SPL. However IDispatch is very clumsy for
 *  the C++ programmer. See DSPATX1.CPP and DSPATX2.CPP for examples of 
 *  using the direct Vtbl IDadisp interface - much more straightforward 
 *  for C++ users.
 */


/* this is a console application */
int main ()
{
        EXCEPINFO excepinfo;
        unsigned int argerr;
        SAFEARRAY *psa;
        DISPID dispid;        
        OLECHAR *name;
        VARIANT varResult;
        VARIANTARG varg, varga[2];
        BSTR bstrIn;
        IDispatch *pIDispatch = NULL;
        int status = 0;
        HRESULT hr;

        printf("dspdisp - DADiSP IDispatch Test, Initializing...\n");

        hr = OleInitialize(NULL);
        if (FAILED(hr)) 
        {
                trace("Failed to OleInitialize");
                return(1);
        }

        // Get the CLSID for the application.
        wchar_t progid[] = L"DADiSP.Application";
        CLSID clsid;
         hr = ::CLSIDFromProgID(progid, &clsid);
        if(FAILED(hr))
        {
                trace("Failed to CLSIDFromProgID");
                status = 1;
                goto exitpt;
        }

        // start DADiSP as an automation server
        hr = ::CoCreateInstance(clsid,
                                NULL,
                                CLSCTX_LOCAL_SERVER,
                                IID_IDispatch,
                                (void**)&pIDispatch);
        if (FAILED(hr))
        {
                trace("CoCreateInstance Failed");
                status = 1;
                goto exitpt;
        }
        trace("CoCreateInstance Succeeded.");


        // ---------------------------
        //  dadisp.getdata("version")
        // ---------------------------

        // First we need to get the ID for the function name
        ANSIToUnicode("GetData", &name);

        hr = pIDispatch->GetIDsOfNames(IID_NULL,
                                       &name,
                                       1,
                                       GetUserDefaultLCID(),
                                       &dispid);
        if (FAILED(hr))
        {
                trace("Query GetIDsOfNames Failed");
                status = 1;
                goto exitpt;
        }
        
        trace("\ndadisp.getdata(\"version\")");

        // Convert the wide-character string to a BSTR.
        CstrToBstr("version", &bstrIn);

        // Build up the parameters for the invoke call.

        // Allocate and initialize a VARIANT argument.
        ::VariantInit(&varg);      // Initialize the VARIANT.
        V_VT(&varg)   = VT_BSTR;   // Type of VARIANT data
        V_BSTR(&varg) = bstrIn;    // Data for the VARIANT

        // Fill in the DISPPARAMS structure.
        DISPPARAMS param;
        param.cArgs = 1;                 // Number of arguments
        param.rgvarg = &varg;            // Arguments
        param.cNamedArgs = 0;            // Number of named args
        param.rgdispidNamedArgs = NULL;  // Named arguments

        // Allocate a variant for the returned parameter.
        ::VariantInit(&varResult);

        // Invoke the function.
        hr = pIDispatch->Invoke(dispid,
                                IID_NULL,
                                GetUserDefaultLCID(),
                                DISPATCH_METHOD,
                                ¶m,
                                &varResult,
                                &excepinfo,
                                &argerr);
        if (FAILED(hr))
        {
                trace("Invoke Failed");
                ShowException(name, hr, &excepinfo, argerr);
                status = 1;
                goto exitpt;
        }

        // Display the returned value
        DisplayVariant(&varResult);


        // ------------------------------------
        //  dadisp.getdata("max(gnorm(100,1))")
        // ------------------------------------

        ::SysFreeString(bstrIn);
        CstrToBstr("max(gnorm(100,1))", &bstrIn);

        // Build up the parameters for the invoke call.

        // Allocate and initialize a VARIANT argument.
        ::VariantInit(&varg);      // Initialize the VARIANT.
        V_VT(&varg)   = VT_BSTR;   // Type of VARIANT data
        V_BSTR(&varg) = bstrIn;    // Data for the VARIANT

        // Fill in the DISPPARAMS structure.
        param.cArgs = 1;                 // Number of arguments
        param.rgvarg = &varg;            // Arguments
        param.cNamedArgs = 0;            // Number of named args
        param.rgdispidNamedArgs = NULL;  // Named arguments

        // Allocate a variant for the returned parameter.
        ::VariantInit(&varResult);

        trace("\ndadisp.getdata(\"max(gnorm(100,1))\")");

        // Invoke the function.
        hr = pIDispatch->Invoke(dispid,
                                IID_NULL,
                                GetUserDefaultLCID(),
                                DISPATCH_METHOD,
                                ¶m,
                                &varResult,
                                &excepinfo,
                                &argerr);
        if (FAILED(hr))
        {
                trace("Invoke Failed");
                ShowException(name, hr, &excepinfo, argerr);
                status = 1;
                goto exitpt;
        }

        // Display the returned value
        DisplayVariant(&varResult);



        // -------------------------------------
        //  dadisp.putdata("MyVar", RandomArray)
        // -------------------------------------

        free(name);
        ANSIToUnicode("Putdata", &name);

        hr = pIDispatch->GetIDsOfNames(IID_NULL,
                                       &name,
                                       1,
                                       GetUserDefaultLCID(),
                                       &dispid);
        if (FAILED(hr))
        {
                trace("Query GetIDsOfNames Failed");
                status = 1;
                goto exitpt;
        }

        // create a random array
        CreateRandomArray(darray, ARRAY_SIZE);

        // convert to automation SafeArray
        psa = DarrayToSafeArray(darray, ARRAY_SIZE, 1);

        ::SysFreeString(bstrIn);
        CstrToBstr("MyVar", &bstrIn);

        // Allocate and initialize a VARIANT argument.
        // This routine has two arguments, must add in *reverse* order

        ::VariantInit(&varga[1]);        // Initialize the VARIANT.
        V_VT(&(varga[1]))   = VT_BSTR;   // DADiSP variable name
        V_BSTR(&(varga[1])) = bstrIn;    // Data for the VARIANT

        ::VariantInit(&varga[0]);              // Initialize the VARIANT.
        V_VT(&(varga[0]))    = VT_ARRAY|VT_R8; // Array of doubles
        V_ARRAY(&(varga[0])) = psa;            // Data for the VARIANT


        // Fill in the DISPPARAMS structure.
        param.cArgs = 2;                 // Number of arguments
        param.rgvarg = varga;            // Arguments
        param.cNamedArgs = 0;            // Number of named args
        param.rgdispidNamedArgs = NULL;  // Named arguments

        // Allocate a variant for the returned parameter.
        ::VariantInit(&varResult);

        // Invoke the function.
        hr = pIDispatch->Invoke(dispid,
                                IID_NULL,
                                GetUserDefaultLCID(),
                                DISPATCH_METHOD,
                                ¶m,
                                &varResult,
                                &excepinfo,
                                &argerr);
        if (FAILED(hr))
        {
                trace("Invoke Failed");
                ShowException(name, hr, &excepinfo, argerr);
                status = 1;
                goto exitpt;
        }

        // Display the input array
        trace("\nRandom Input Array:");

        DisplayVariant(&varga[0]);


        // ---------------------------------
        //  dadisp.getdata("fft(MyVar)")
        // ---------------------------------

        free(name);
        ANSIToUnicode("Getdata", &name);

        hr = pIDispatch->GetIDsOfNames(IID_NULL,
                                       &name,
                                       1,
                                       GetUserDefaultLCID(),
                                       &dispid);
        if (FAILED(hr))
        {
                trace("Query GetIDsOfNames Failed");
                status = 1;
                goto exitpt;
        }

        ::SysFreeString(bstrIn);
        CstrToBstr("fft(MyVar)", &bstrIn);

        // Allocate and initialize a VARIANT argument.
        ::VariantInit(&varg);      // Initialize the VARIANT.
        V_VT(&varg)   = VT_BSTR;   // Type of VARIANT data
        V_BSTR(&varg) = bstrIn;    // Data for the VARIANT


        // Fill in the DISPPARAMS structure.
        param.cArgs = 1;                 // Number of arguments
        param.rgvarg = &varg;            // Arguments
        param.cNamedArgs = 0;            // Number of named args
        param.rgdispidNamedArgs = NULL;  // Named arguments

        // Allocate a variant for the returned parameter.
        ::VariantInit(&varResult);

        // Invoke the function.
        hr = pIDispatch->Invoke(dispid,
                                IID_NULL,
                                GetUserDefaultLCID(),
                                DISPATCH_METHOD,
                                ¶m,
                                &varResult,
                                &excepinfo,
                                &argerr);
        if (FAILED(hr))
        {
                trace("Invoke Failed");
                ShowException(name, hr, &excepinfo, argerr);
                status = 1;
                goto exitpt;
        }

        // Display the returned array
        trace("\nFFT Output Array:");
        DisplayVariant(&varResult);

exitpt:

        // Release the dispatch interface.
        if (pIDispatch) pIDispatch->Release();

        // Uninitialize the OLE library.
        OleUninitialize();
        return(0);
}






/* --------------------- Support Routines -------------------------*/


// convert a string to a BSTR
int CstrToBstr (char *str, BSTR *pbstr)
{
        wchar_t *wsz = NULL;
        int status = TRUE;

        ANSIToUnicode(str, &wsz);
        *pbstr = ::SysAllocString(wsz);

        if (*pbstr == NULL) 
        {
                status = FALSE;
        }
        free(wsz);

        return(status);
}


// convert a BSTR to a string
char *BstrToCstr (BSTR bstr)
{
        int len;
        char *str = NULL;

        if ((len = ::SysStringByteLen(bstr)) > 0) 
        {
                if ((str = new char[len+1]) != NULL) 
                {
                        strncpyfar(str, bstr, len);
                }
        }
        return(str);
}


// strcpy at most n chars from FAR memory
int strncpyfar (char *l, LPCOLESTR f, int n)
{
        while (n > 0) 
        {
                if ((*l = (char) *f) == '\0') break;
                l++;
                f++;
                n--;
        }
        return(TRUE);
}


// convert C string to Unicode
BOOL ANSIToUnicode (PCSTR szStrA, PWSTR *pszStrW) 
{
        int nLenOfWideCharStr;
        BOOL fOK = FALSE;              // Assume failure

        *pszStrW = NULL;               // Assume failure
        if (szStrA == NULL) 
        {
                // It is OK to have a NULL string.
                return(TRUE);
        }

        // Get the number of bytes needed for the wide version of the string.
        nLenOfWideCharStr = ::MultiByteToWideChar(CP_ACP, 0, szStrA, -1, NULL, 0);

        // Allocate memory to accomodate the size of the wide character string.
        *pszStrW = new WCHAR[nLenOfWideCharStr];

        if (*pszStrW != NULL) 
        {
                // Convert the multi-byte string to a wide-character string.
                ::MultiByteToWideChar(CP_ACP, 0, szStrA, -1, *pszStrW, nLenOfWideCharStr);
                fOK = TRUE;
        }
        return(fOK);
}


// convert double array to SAFEARRAY
SAFEARRAY *DarrayToSafeArray (double *darray, int nrow, int ncol)
{
        long iL, jL;
        int status = FALSE;
        double *pdval;
        SAFEARRAY *psa;
        SAFEARRAYBOUND sa[2];
        HRESULT hr;
        unsigned int dim;
        void *pvdata;

        if (darray == NULL) return(NULL);

        dim = 2;

        sa[0].lLbound = 0;
        sa[0].cElements = nrow;

        sa[1].lLbound = 0;
        sa[1].cElements = ncol;

        if ((psa = ::SafeArrayCreate(VT_R8, dim, sa)) != NULL) 
        {
                if ((hr = ::SafeArrayAccessData(psa, (LPVOID *) &pvdata)) != NOERROR) 
                {
                        return(NULL);
                }

                pdval = (double *) pvdata;

                if (ncol == 1L)  /* series */
                {
                        for (iL = 0L; iL < nrow; iL++) 
                        {
                                *pdval++ = *darray++;
                        }
                        status = TRUE;
                }

                else /* general case - array */
                {
                        /* for each col ... */
                        for (iL = 0L; iL < ncol; iL++) 
                        {
                                /* for each row ... */
                                for (jL = 0; jL < nrow; jL++) 
                                {
                                        *pdval++ = *darray++;
                                }
                        }
                        status = TRUE;
                }
                ::SafeArrayUnaccessData(psa);
        }

        return(psa);
}


// create an empty SafeArray
SAFEARRAY *CreateSafeArray ()
{
        SAFEARRAY *psa;
        SAFEARRAYBOUND sa[2];
        unsigned int dim;

        dim = 2;

        // no rows
        sa[0].lLbound = 0;
        sa[0].cElements = 0;

        // one column
        sa[1].lLbound = 0;
        sa[1].cElements = 1;

        psa = ::SafeArrayCreate(VT_R8, dim, sa);
        return(psa);
}



// output a message
void trace (char *msg)
{
        printf("%s\n", msg);
}


// display variant as a string
void DisplayVariant (VARIANT *pvar)
{
        VARIANT vtemp, *ptemp;
        int vtype;
        char *str;

        if (V_ISARRAY(pvar)) 
        {
                vtype = V_VT(pvar) & ~VT_ARRAY;
                DisplayArray(V_ARRAY(pvar), vtype);
        }
        else 
        {
                // Display result as a string
                if (V_VT(pvar) != VT_BSTR)
                {
                        ::VariantInit(&vtemp);
                        ::VariantChangeType(&vtemp, pvar, 0, VT_BSTR);
                        ptemp = &vtemp;
                }
                else
                {
                        ptemp = pvar;
                }

                if (V_VT(ptemp) == VT_BSTR) 
                {
                        str = BstrToCstr(V_BSTR(ptemp));
                        printf("DADiSP returned: %s\n", str);
                        free(str);

                        // Free the string.
                        if (ptemp == &vtemp)
                        {
                                ::SysFreeString(V_BSTR(ptemp));
                        }
                }
                else 
                {
                        printf("Could Not Display Type %d", V_VT(pvar));
                }
        }
}


// simple screen display of an array        
void DisplayArray (SAFEARRAY *psa, int vtype)
{
        int i, j, nrow, ncol, num;
        HRESULT hr;
        double *pdval;
        void *pvdata;

        if (psa == NULL) return;

        if (psa->fFeatures & FADF_VARIANT) 
        {
                vtype = VT_VARIANT;
        }

        nrow = ncol = num = 0;

        // get num rows & columns
        if (psa->cDims > 1) 
        {
                /* note : these are flipped from creation */
                ncol = psa->rgsabound[0].cElements;
                nrow = psa->rgsabound[1].cElements;
        }
        else 
        {
                nrow = psa->rgsabound[0].cElements;
                ncol = 1;
        }

        if ((hr = ::SafeArrayAccessData(psa, (LPVOID *) &pvdata)) != NOERROR) 
        {
                return;
        }

        // we only handle an array of doubles for now
        if (vtype == VT_R8) 
        {
                pdval = (double *) pvdata;

                for (i = 0; i < nrow; i++) 
                {
                        for (j = 0; j < ncol; j++)
                        {
                                printf("%9g ", *(pdval + j * nrow));
                        }
                        printf("\n");
                        pdval++;
                }
        }

        ::SafeArrayUnaccessData(psa);
}


// set array to random data
void CreateRandomArray (double *darray, int size)
{
        int i;

        // randomly seed the generator 
        srand((int) clock());

        // create random array
        for (i = 0; i < size; i++) 
        {
                *darray++ = (double) rand();
        }
}

/* create 1..n arrays */
void CreateLinearArray (double *darray, int size)
{
        int i;

        // randomly seed the generator 
        srand((int) clock());

        // create random array
        for (i = 0; i < size; i++) 
        {
                *darray++ = (double) (i+1);
        }
}

// handy function to display Automation errors
void ShowException (LPOLESTR szMember, HRESULT hr, EXCEPINFO *pexcep, unsigned int uiArgErr)
{
        char *str;
        char memstr[STRSIZE+1], mes[STRSIZE+1];

        memstr[STRSIZE] = '\0';
        mes[STRSIZE]    = '\0';

        strncpyfar(memstr, szMember, STRSIZE);
        
        switch (GetScode(hr)) 
        {
                case DISP_E_UNKNOWNNAME:
                        sprintf(mes, "%s: Unknown name or named argument.", memstr);
                        break;
        
                case DISP_E_BADPARAMCOUNT:
                        sprintf(mes, "%s: Incorrect number of arguments.", memstr);
                        break;
                        
                case DISP_E_EXCEPTION:
                        sprintf(mes, "%s: Error %d: ", memstr, pexcep->wCode);
                        if (pexcep->bstrDescription != NULL) 
                        {
                                str = BstrToCstr(pexcep->bstrDescription);
                                strcat(mes, str);
                                free(str);
                        }
                        else 
                        {
                                strcat(mes, "<>");
                        }
                        break;
                        
                case DISP_E_MEMBERNOTFOUND:
                        sprintf(mes, "%s: method or property not found.", memstr);
                        break;
                
                case DISP_E_OVERFLOW:
                        sprintf(mes, "%s: Overflow while coercing argument values.", memstr);
                        break;
                
                case DISP_E_NONAMEDARGS:
                        sprintf(mes, "%s: Object implementation does not support named arguments.",
                                                memstr);
                    break;
                    
                case DISP_E_UNKNOWNLCID:
                        sprintf(mes, "%s: The locale ID is unknown.", memstr);
                        break;
                
                case DISP_E_PARAMNOTOPTIONAL:
                        sprintf(mes, "%s: Missing a required parameter.", memstr);
                        break;
                
                case DISP_E_PARAMNOTFOUND:
                        sprintf(mes, "%s: Argument not found, argument %d.", memstr, uiArgErr);
                        break;
                        
                case DISP_E_TYPEMISMATCH:
                        sprintf(mes, "%s: Type mismatch, argument %d.", memstr, uiArgErr);
                        break;

                default:
                        sprintf(mes, "%s: Unknown error occured.", memstr);
                        break;
        }
        
        printf(mes);
}