556 lines
13 KiB
C++
556 lines
13 KiB
C++
#include <comdef.h>
|
|
|
|
#pragma hdrstop
|
|
|
|
#include <stdarg.h>
|
|
#include <malloc.h>
|
|
|
|
#pragma intrinsic(memset)
|
|
|
|
#pragma warning(disable:4290)
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
void __stdcall
|
|
_com_issue_error(HRESULT hr) throw(_com_error)
|
|
{
|
|
_com_raise_error(hr, NULL);
|
|
}
|
|
|
|
void __stdcall
|
|
_com_issue_errorex(HRESULT hr, IUnknown* punk, REFIID riid) throw(_com_error)
|
|
{
|
|
IErrorInfo* perrinfo = NULL;
|
|
if (punk == NULL) {
|
|
goto exeunt;
|
|
}
|
|
ISupportErrorInfo* psei;
|
|
if (FAILED(punk->QueryInterface(__uuidof(ISupportErrorInfo),
|
|
(void**)&psei))) {
|
|
goto exeunt;
|
|
}
|
|
HRESULT hrSupportsErrorInfo;
|
|
hrSupportsErrorInfo = psei->InterfaceSupportsErrorInfo(riid);
|
|
psei->Release();
|
|
if (hrSupportsErrorInfo != S_OK) {
|
|
goto exeunt;
|
|
}
|
|
if (GetErrorInfo(0, &perrinfo) != S_OK) {
|
|
perrinfo = NULL;
|
|
}
|
|
exeunt:
|
|
_com_raise_error(hr, perrinfo);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
#define VT_OPTIONAL 0x0800
|
|
|
|
struct FLOAT_ARG { BYTE floatBits[sizeof(float)]; };
|
|
struct DOUBLE_ARG { BYTE doubleBits[sizeof(double)]; };
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
static HRESULT
|
|
_com_invoke_helper(IDispatch* pDispatch,
|
|
DISPID dwDispID,
|
|
WORD wFlags,
|
|
VARTYPE vtRet,
|
|
void* pvRet,
|
|
const wchar_t* pwParamInfo,
|
|
va_list argList,
|
|
IErrorInfo** pperrinfo) throw()
|
|
{
|
|
*pperrinfo = NULL;
|
|
|
|
if (pDispatch == NULL) {
|
|
return E_POINTER;
|
|
}
|
|
|
|
DISPPARAMS dispparams;
|
|
VARIANT* rgvarg;
|
|
rgvarg = NULL;
|
|
memset(&dispparams, 0, sizeof dispparams);
|
|
|
|
// determine number of arguments
|
|
if (pwParamInfo != NULL) {
|
|
dispparams.cArgs = lstrlenW(pwParamInfo);
|
|
}
|
|
|
|
DISPID dispidNamed;
|
|
dispidNamed = DISPID_PROPERTYPUT;
|
|
if (wFlags & (DISPATCH_PROPERTYPUT|DISPATCH_PROPERTYPUTREF)) {
|
|
if (dispparams.cArgs <= 0) {
|
|
return E_INVALIDARG;
|
|
}
|
|
dispparams.cNamedArgs = 1;
|
|
dispparams.rgdispidNamedArgs = &dispidNamed;
|
|
}
|
|
|
|
if (dispparams.cArgs != 0) {
|
|
// allocate memory for all VARIANT parameters
|
|
rgvarg = (VARIANT*)_alloca(dispparams.cArgs * sizeof(VARIANT));
|
|
memset(rgvarg, 0, sizeof(VARIANT) * dispparams.cArgs);
|
|
dispparams.rgvarg = rgvarg;
|
|
|
|
// get ready to walk vararg list
|
|
const wchar_t* pw = pwParamInfo;
|
|
VARIANT* pArg;
|
|
pArg = rgvarg + dispparams.cArgs - 1; // params go in opposite order
|
|
|
|
while (*pw != 0) {
|
|
pArg->vt = *pw & ~VT_OPTIONAL; // set the variant type
|
|
switch (pArg->vt) {
|
|
case VT_I2:
|
|
#ifdef _MAC
|
|
pArg->iVal = (short)va_arg(argList, int);
|
|
#else
|
|
pArg->iVal = va_arg(argList, short);
|
|
#endif
|
|
break;
|
|
case VT_I4:
|
|
pArg->lVal = va_arg(argList, long);
|
|
break;
|
|
case VT_R4:
|
|
// Note: All float arguments to vararg functions are passed
|
|
// as doubles instead. That's why they are passed as VT_R8
|
|
// instead of VT_R4.
|
|
pArg->vt = VT_R8;
|
|
*(DOUBLE_ARG*)&pArg->dblVal = va_arg(argList, DOUBLE_ARG);
|
|
break;
|
|
case VT_R8:
|
|
*(DOUBLE_ARG*)&pArg->dblVal = va_arg(argList, DOUBLE_ARG);
|
|
break;
|
|
case VT_DATE:
|
|
*(DOUBLE_ARG*)&pArg->date = va_arg(argList, DOUBLE_ARG);
|
|
break;
|
|
case VT_CY:
|
|
pArg->cyVal = *va_arg(argList, CY*);
|
|
break;
|
|
case VT_BSTR:
|
|
pArg->bstrVal = va_arg(argList, BSTR);
|
|
break;
|
|
case VT_DISPATCH:
|
|
pArg->pdispVal = va_arg(argList, LPDISPATCH);
|
|
break;
|
|
case VT_ERROR:
|
|
pArg->scode = va_arg(argList, SCODE);
|
|
break;
|
|
case VT_BOOL:
|
|
#ifdef _MAC
|
|
V_BOOL(pArg) = (VARIANT_BOOL)va_arg(argList, int)
|
|
? VARIANT_TRUE : VARIANT_FALSE;
|
|
#else
|
|
V_BOOL(pArg) = va_arg(argList, VARIANT_BOOL)
|
|
? VARIANT_TRUE : VARIANT_FALSE;
|
|
#endif
|
|
break;
|
|
case VT_VARIANT:
|
|
*pArg = *va_arg(argList, VARIANT*);
|
|
break;
|
|
case VT_UNKNOWN:
|
|
pArg->punkVal = va_arg(argList, LPUNKNOWN);
|
|
break;
|
|
case VT_DECIMAL:
|
|
pArg->decVal = *va_arg(argList, DECIMAL*);
|
|
pArg->vt = VT_DECIMAL;
|
|
break;
|
|
case VT_UI1:
|
|
#ifdef _MAC
|
|
pArg->bVal = (BYTE)va_arg(argList, int);
|
|
#else
|
|
pArg->bVal = va_arg(argList, BYTE);
|
|
#endif
|
|
break;
|
|
|
|
case VT_I2|VT_BYREF:
|
|
pArg->piVal = va_arg(argList, short*);
|
|
break;
|
|
case VT_I4|VT_BYREF:
|
|
pArg->plVal = va_arg(argList, long*);
|
|
break;
|
|
case VT_R4|VT_BYREF:
|
|
pArg->pfltVal = va_arg(argList, float*);
|
|
break;
|
|
case VT_R8|VT_BYREF:
|
|
pArg->pdblVal = va_arg(argList, double*);
|
|
break;
|
|
case VT_DATE|VT_BYREF:
|
|
pArg->pdate = va_arg(argList, DATE*);
|
|
break;
|
|
case VT_CY|VT_BYREF:
|
|
pArg->pcyVal = va_arg(argList, CY*);
|
|
break;
|
|
case VT_BSTR|VT_BYREF:
|
|
pArg->pbstrVal = va_arg(argList, BSTR*);
|
|
break;
|
|
case VT_DISPATCH|VT_BYREF:
|
|
pArg->ppdispVal = va_arg(argList, LPDISPATCH*);
|
|
break;
|
|
case VT_ERROR|VT_BYREF:
|
|
pArg->pscode = va_arg(argList, SCODE*);
|
|
break;
|
|
case VT_BOOL|VT_BYREF:
|
|
pArg->pboolVal = va_arg(argList, VARIANT_BOOL*);
|
|
break;
|
|
case VT_VARIANT|VT_BYREF:
|
|
pArg->pvarVal = va_arg(argList, VARIANT*);
|
|
break;
|
|
case VT_UNKNOWN|VT_BYREF:
|
|
pArg->ppunkVal = va_arg(argList, LPUNKNOWN*);
|
|
break;
|
|
case VT_DECIMAL|VT_BYREF:
|
|
pArg->pdecVal = va_arg(argList, DECIMAL*);
|
|
break;
|
|
case VT_UI1|VT_BYREF:
|
|
pArg->pbVal = va_arg(argList, BYTE*);
|
|
break;
|
|
|
|
default:
|
|
// M00REVIEW - For safearrays, should be able to type-check
|
|
// against the base VT_* type.(?)
|
|
if (pArg->vt & VT_ARRAY) {
|
|
if (pArg->vt & VT_BYREF) {
|
|
pArg->pparray = va_arg(argList, LPSAFEARRAY*);
|
|
} else {
|
|
pArg->parray = va_arg(argList, LPSAFEARRAY);
|
|
}
|
|
break;
|
|
}
|
|
// unknown type!
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
--pArg; // get ready to fill next argument
|
|
++pw;
|
|
}
|
|
|
|
// Check for missing optional unnamed args at the end of the arglist,
|
|
// and remove them from the DISPPARAMS. This permits calling servers
|
|
// which modify their action depending on the actual number of args.
|
|
// E.g. Excel95 Application.Workbooks returns a Workbooks* if called
|
|
// with no args, a Workbook* if called with one arg - this shouldn't
|
|
// be necessary, but Excel95 doesn't appear to check for missing
|
|
// args indicated by VT_ERROR/DISP_E_PARAMNOTFOUND.
|
|
pArg = rgvarg + dispparams.cNamedArgs;
|
|
pw = pwParamInfo + dispparams.cArgs - dispparams.cNamedArgs - 1;
|
|
unsigned int cMissingArgs = 0;
|
|
|
|
// Count the number of missing arguments
|
|
while (pw >= pwParamInfo) {
|
|
// Optional args must be VARIANT or VARIANT*
|
|
if ((*pw & ~VT_BYREF) != (VT_VARIANT|VT_OPTIONAL)) {
|
|
break;
|
|
}
|
|
|
|
VARIANT* pVar;
|
|
pVar = (*pw & VT_BYREF) ? pArg->pvarVal : pArg;
|
|
if (V_VT(pVar) != VT_ERROR ||
|
|
V_ERROR(pVar) != DISP_E_PARAMNOTFOUND)
|
|
{
|
|
break;
|
|
}
|
|
|
|
++cMissingArgs;
|
|
++pArg;
|
|
--pw;
|
|
}
|
|
|
|
// Move the named args up next to the remaining unnamed args and
|
|
// adjust the DISPPARAMS struct.
|
|
if (cMissingArgs > 0) {
|
|
for (unsigned int c = 0; c < dispparams.cNamedArgs; ++c) {
|
|
rgvarg[c + cMissingArgs] = rgvarg[c];
|
|
}
|
|
dispparams.cArgs -= cMissingArgs;
|
|
dispparams.rgvarg += cMissingArgs;
|
|
}
|
|
}
|
|
|
|
// initialize return value
|
|
VARIANT* pvarResult;
|
|
VARIANT vaResult;
|
|
VariantInit(&vaResult);
|
|
pvarResult = (vtRet != VT_EMPTY) ? &vaResult : NULL;
|
|
|
|
// initialize EXCEPINFO struct
|
|
EXCEPINFO excepInfo;
|
|
memset(&excepInfo, 0, sizeof excepInfo);
|
|
|
|
UINT nArgErr;
|
|
nArgErr = (UINT)-1; // initialize to invalid arg
|
|
|
|
// make the call
|
|
HRESULT hr = pDispatch->Invoke(dwDispID, __uuidof(NULL), 0, wFlags,
|
|
&dispparams, pvarResult, &excepInfo,
|
|
&nArgErr);
|
|
|
|
// throw exception on failure
|
|
if (FAILED(hr)) {
|
|
VariantClear(&vaResult);
|
|
if (hr != DISP_E_EXCEPTION) {
|
|
// non-exception error code
|
|
// M00REVIEW - Is this all? What about looking for IErrorInfo?
|
|
// - Only if IID is passed in, I'd think
|
|
return hr;
|
|
}
|
|
|
|
// make sure excepInfo is filled in
|
|
if (excepInfo.pfnDeferredFillIn != NULL) {
|
|
excepInfo.pfnDeferredFillIn(&excepInfo);
|
|
}
|
|
|
|
// allocate new error info, and fill it
|
|
ICreateErrorInfo *pcerrinfo = NULL;
|
|
if (SUCCEEDED(CreateErrorInfo(&pcerrinfo))) {
|
|
// Set up ErrInfo object
|
|
// M00REVIEW - Use IID if decide to pass that in
|
|
pcerrinfo->SetGUID(__uuidof(IDispatch));
|
|
pcerrinfo->SetDescription(excepInfo.bstrDescription);
|
|
pcerrinfo->SetHelpContext(excepInfo.dwHelpContext);
|
|
pcerrinfo->SetHelpFile(excepInfo.bstrHelpFile);
|
|
pcerrinfo->SetSource(excepInfo.bstrSource);
|
|
|
|
if (FAILED(pcerrinfo->QueryInterface(__uuidof(IErrorInfo),
|
|
(void**)pperrinfo))) {
|
|
*pperrinfo = NULL;
|
|
}
|
|
}
|
|
|
|
if (excepInfo.wCode != 0) {
|
|
hr = _com_error::WCodeToHRESULT(excepInfo.wCode);
|
|
} else {
|
|
hr = excepInfo.scode;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
if (vtRet != VT_EMPTY) {
|
|
// convert return value unless already correct
|
|
if (vtRet != VT_VARIANT && vtRet != vaResult.vt) {
|
|
hr = VariantChangeType(&vaResult, &vaResult, 0, vtRet);
|
|
if (FAILED(hr)) {
|
|
VariantClear(&vaResult);
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
// copy return value into return spot!
|
|
switch (vtRet) {
|
|
case VT_I2:
|
|
*(short*)pvRet = vaResult.iVal;
|
|
break;
|
|
case VT_I4:
|
|
*(long*)pvRet = vaResult.lVal;
|
|
break;
|
|
case VT_R4:
|
|
*(FLOAT_ARG*)pvRet = *(FLOAT_ARG*)&vaResult.fltVal;
|
|
break;
|
|
case VT_R8:
|
|
*(DOUBLE_ARG*)pvRet = *(DOUBLE_ARG*)&vaResult.dblVal;
|
|
break;
|
|
case VT_DATE:
|
|
*(DOUBLE_ARG*)pvRet = *(DOUBLE_ARG*)&vaResult.date;
|
|
break;
|
|
case VT_CY:
|
|
*(CY*)pvRet = vaResult.cyVal;
|
|
break;
|
|
case VT_BSTR:
|
|
*(BSTR*)pvRet = vaResult.bstrVal;
|
|
break;
|
|
case VT_DISPATCH:
|
|
*(LPDISPATCH*)pvRet = vaResult.pdispVal;
|
|
break;
|
|
case VT_ERROR:
|
|
*(SCODE*)pvRet = vaResult.scode;
|
|
break;
|
|
case VT_BOOL:
|
|
*(VARIANT_BOOL*)pvRet = V_BOOL(&vaResult);
|
|
break;
|
|
case VT_VARIANT:
|
|
*(VARIANT*)pvRet = vaResult;
|
|
break;
|
|
case VT_UNKNOWN:
|
|
*(LPUNKNOWN*)pvRet = vaResult.punkVal;
|
|
break;
|
|
case VT_DECIMAL:
|
|
*(DECIMAL*)pvRet = vaResult.decVal;
|
|
break;
|
|
case VT_UI1:
|
|
*(BYTE*)pvRet = vaResult.bVal;
|
|
break;
|
|
|
|
default:
|
|
if ((vtRet & (VT_ARRAY|VT_BYREF)) == VT_ARRAY) {
|
|
// M00REVIEW - type-check against the base VT_* type?
|
|
*(LPSAFEARRAY*)pvRet = vaResult.parray;
|
|
break;
|
|
}
|
|
// invalid return type!
|
|
VariantClear(&vaResult);
|
|
return E_INVALIDARG;
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT __cdecl
|
|
_com_dispatch_raw_method(IDispatch* pDispatch,
|
|
DISPID dwDispID,
|
|
WORD wFlags,
|
|
VARTYPE vtRet,
|
|
void* pvRet,
|
|
const wchar_t* pwParamInfo,
|
|
...) throw()
|
|
{
|
|
va_list argList;
|
|
va_start(argList, pwParamInfo);
|
|
|
|
IErrorInfo* perrinfo;
|
|
HRESULT hr = _com_invoke_helper(pDispatch,
|
|
dwDispID,
|
|
wFlags,
|
|
vtRet,
|
|
pvRet,
|
|
pwParamInfo,
|
|
argList,
|
|
&perrinfo);
|
|
if (FAILED(hr)) {
|
|
SetErrorInfo(0, perrinfo);
|
|
}
|
|
|
|
va_end(argList);
|
|
return hr;
|
|
}
|
|
|
|
HRESULT __cdecl
|
|
_com_dispatch_method(IDispatch* pDispatch,
|
|
DISPID dwDispID,
|
|
WORD wFlags,
|
|
VARTYPE vtRet,
|
|
void* pvRet,
|
|
const wchar_t* pwParamInfo,
|
|
...) throw(_com_error)
|
|
{
|
|
va_list argList;
|
|
va_start(argList, pwParamInfo);
|
|
|
|
IErrorInfo* perrinfo;
|
|
HRESULT hr = _com_invoke_helper(pDispatch,
|
|
dwDispID,
|
|
wFlags,
|
|
vtRet,
|
|
pvRet,
|
|
pwParamInfo,
|
|
argList,
|
|
&perrinfo);
|
|
if (FAILED(hr)) {
|
|
_com_raise_error(hr, perrinfo);
|
|
}
|
|
|
|
va_end(argList);
|
|
return hr;
|
|
}
|
|
|
|
HRESULT __stdcall
|
|
_com_dispatch_raw_propget(IDispatch* pDispatch,
|
|
DISPID dwDispID,
|
|
VARTYPE vtProp,
|
|
void* pvProp) throw()
|
|
{
|
|
return _com_dispatch_raw_method(pDispatch,
|
|
dwDispID,
|
|
DISPATCH_PROPERTYGET,
|
|
vtProp,
|
|
pvProp,
|
|
NULL);
|
|
}
|
|
|
|
HRESULT __stdcall
|
|
_com_dispatch_propget(IDispatch* pDispatch,
|
|
DISPID dwDispID,
|
|
VARTYPE vtProp,
|
|
void* pvProp) throw(_com_error)
|
|
{
|
|
return _com_dispatch_method(pDispatch,
|
|
dwDispID,
|
|
DISPATCH_PROPERTYGET,
|
|
vtProp,
|
|
pvProp,
|
|
NULL);
|
|
}
|
|
|
|
HRESULT __cdecl
|
|
_com_dispatch_raw_propput(IDispatch* pDispatch,
|
|
DISPID dwDispID,
|
|
VARTYPE vtProp,
|
|
...) throw()
|
|
{
|
|
va_list argList;
|
|
va_start(argList, vtProp);
|
|
#ifdef _MAC
|
|
argList -= 2;
|
|
#endif
|
|
|
|
wchar_t rgwParams[2];
|
|
rgwParams[0] = vtProp;
|
|
rgwParams[1] = 0;
|
|
|
|
WORD wFlags = (vtProp == VT_DISPATCH || vtProp == VT_UNKNOWN)
|
|
? DISPATCH_PROPERTYPUTREF : DISPATCH_PROPERTYPUT;
|
|
|
|
IErrorInfo* perrinfo;
|
|
HRESULT hr = _com_invoke_helper(pDispatch,
|
|
dwDispID,
|
|
wFlags,
|
|
VT_EMPTY,
|
|
NULL,
|
|
rgwParams,
|
|
argList,
|
|
&perrinfo);
|
|
if (FAILED(hr)) {
|
|
SetErrorInfo(0, perrinfo);
|
|
}
|
|
|
|
va_end(argList);
|
|
return hr;
|
|
}
|
|
|
|
HRESULT __cdecl
|
|
_com_dispatch_propput(IDispatch* pDispatch,
|
|
DISPID dwDispID,
|
|
VARTYPE vtProp,
|
|
...) throw(_com_error)
|
|
{
|
|
va_list argList;
|
|
va_start(argList, vtProp);
|
|
#ifdef _MAC
|
|
argList -= 2;
|
|
#endif
|
|
|
|
wchar_t rgwParams[2];
|
|
rgwParams[0] = vtProp;
|
|
rgwParams[1] = 0;
|
|
|
|
WORD wFlags = (vtProp == VT_DISPATCH || vtProp == VT_UNKNOWN)
|
|
? DISPATCH_PROPERTYPUTREF : DISPATCH_PROPERTYPUT;
|
|
|
|
IErrorInfo* perrinfo;
|
|
HRESULT hr = _com_invoke_helper(pDispatch,
|
|
dwDispID,
|
|
wFlags,
|
|
VT_EMPTY,
|
|
NULL,
|
|
rgwParams,
|
|
argList,
|
|
&perrinfo);
|
|
if (FAILED(hr)) {
|
|
_com_raise_error(hr, perrinfo);
|
|
}
|
|
|
|
va_end(argList);
|
|
return hr;
|
|
}
|