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