windows-nt/Source/XPSP1/NT/enduser/vccomsupport/comsupp.cpp
2020-09-26 16:20:57 +08:00

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