#include #pragma hdrstop #include #include #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; }