/*----------------------------------------------------------- (c) 1998, Daan Leijen, leijen@@fwi.uva.nl 1999-, Sigbjorn Finne sof@galconn.com Changes: * made cygwin32 (&gcc) friendly. -- sof 10/98 -----------------------------------------------------------*/ #if !defined(__CYGWIN32__) && !defined(__MINGW32__) #define CINTERFACE #define COBJMACROS #endif #include #include #if defined(__CYGWIN32__) || defined(__MINGW32__) #include #endif #ifdef __GNUC__ #include "PointerSrc.h" #include "autoPrim.h" #include "comPrim.h" #include #include /* No support for nameless unions..yet. */ #if __W32API_MAJOR_VERSION == 1 #define cyVal n1.n2.n3.cyVal #define bVal n1.n2.n3.bVal #define punkVal n1.n2.n3.punkVal #define boolVal n1.n2.n3.boolVal #define pdispVal n1.n2.n3.pdispVal #define bstrVal n1.n2.n3.bstrVal #define date n1.n2.n3.date #define dblVal n1.n2.n3.dblVal #define fltVal n1.n2.n3.fltVal #define lVal n1.n2.n3.lVal #define ulVal n1.n2.n3.ulVal #define iVal n1.n2.n3.iVal #define parray n1.n2.n3.parray #define pparray n1.n2.n3.pparray #define decVal n1.decVal #define vt n1.n2.vt #endif #endif /*----------------------------------------------------------- -- Invoke -----------------------------------------------------------*/ HRESULT dispatchInvoke(IDispatch *obj, DISPID dispid, LCID lcid, BOOL isfunction, unsigned flags, int cargs, int cargsout, VARIANT *args, VARIANT *argsout, EXCEPINFO **info) { HRESULT hr; DISPPARAMS dp; EXCEPINFO excepInfo; UINT argErr; VARIANT varRes; VARIANT *res; DISPID dispput; int i; if (info) *info = NULL; /* create VT_BYREF variants for out parameters */ for (i = 0; i < cargsout; i++) { VariantInit(args + i); args[i].vt = argsout[i].vt | VT_BYREF; #if !defined(__GNUC__) || __W32API_MAJOR_VERSION > 1 args[i].byref = &(argsout[i].byref); #else args[i].n1.n2.n3.byref = &(argsout[i].n1.n2.n3.byref); #endif } if (!obj) return E_POINTER; dp.cArgs = cargs; dp.rgvarg = args; /* set special flags for property puts */ if (flags & DISPATCH_PROPERTYPUT) { dispput = DISPID_PROPERTYPUT; dp.rgdispidNamedArgs = &dispput; dp.cNamedArgs = 1; } else { dp.rgdispidNamedArgs = NULL; dp.cNamedArgs = 0; } /* if this is surely a function, return the result in the first element of argsout, and decrement the number of arguments */ if (isfunction) { dp.cArgs--; dp.rgvarg++; res = argsout; } else { res = &varRes; VariantInit(res); } hr = IDispatch_Invoke(obj, dispid, &IID_NULL, lcid, flags, &dp, res, &excepInfo, &argErr); /* if guessed that this was not a function, but it returned a result (in varRes) or it complained about the number of arguments, than we again try to call it but now as a function. */ /* Turned this smart off, leaving it to HDirect (and the user) to make the distinction between memberX and functionX. -- sof No, let's not -- sometimes the type information isn't precise enough about this (e.g., the type info for msi.dll's Installer.OpenDatabase() suggests its a _method_, but it turns out to be a _function_. */ if (!isfunction && flags == DISPATCH_METHOD && (hr == DISP_E_BADPARAMCOUNT || hr == DISP_E_TYPEMISMATCH || res->vt != VT_EMPTY)) { isfunction = TRUE; dp.cArgs--; dp.rgvarg++; VariantClear(res); res = argsout; hr = IDispatch_Invoke(obj, dispid, &IID_NULL, lcid, flags, &dp, res, &excepInfo, &argErr); } if (!isfunction) VariantClear(res); /* free the input arguments */ for (i = cargsout; i < cargs; i++) VariantClear(args + i); /* check for exceptions */ if (hr == DISP_E_EXCEPTION && info) { *info = (EXCEPINFO *)primAllocMemory(sizeof(EXCEPINFO)); if (*info) { **info = excepInfo; if ((*info)->pfnDeferredFillIn) (*info)->pfnDeferredFillIn(*info); } } return hr; } void freeExcepInfo(EXCEPINFO *info) { if (!info) return; if (info->bstrSource) SysFreeString(info->bstrSource); if (info->bstrDescription) SysFreeString(info->bstrDescription); if (info->bstrHelpFile) SysFreeString(info->bstrHelpFile); } char *getExcepInfoMessage(EXCEPINFO *info) { #define MAXMSG 255 #define MAXSTR 80 char member[MAXSTR + 1] = ""; char source[MAXSTR + 1] = ""; char wcode[MAXSTR + 1] = ""; char description[MAXMSG + 1] = "Exception occurred"; char *msg; msg = primAllocMemory(MAXMSG + 1); if (!msg) return NULL; if (info) { if (info->bstrSource) { char sourceReg[MAXSTR]; wcstombs(sourceReg, info->bstrSource, MAXSTR); if (ERROR_SUCCESS != RegQueryValueA(HKEY_CLASSES_ROOT, sourceReg, source, NULL)) wcstombs(source, info->bstrSource, MAXSTR); } if (info->wCode) { sprintf(wcode, "(%d)", info->wCode); } if (info->bstrDescription) { wcstombs(description, info->bstrDescription, MAXMSG); } else if (info->scode) { strncpy(description, hresultString(info->scode), MAXMSG); } } if (info && info->bstrSource) #ifdef __GNUC__ sprintf(msg, "%s.%s:%s %s", source, member, wcode, description); #else _snprintf(msg, MAXMSG, "%s.%s:%s %s", source, member, wcode, description); #endif else #ifdef __GNUC__ sprintf(msg, "%s %s", wcode, description); #else _snprintf(msg, MAXMSG, "%s %s", wcode, description); #endif return msg; } HRESULT dispatchGetMemberID(IDispatch *obj, BSTR name, LCID lcid, DISPID *dispid) { if (obj) return IDispatch_GetIDsOfNames(obj, &IID_NULL, &name, 1, lcid, dispid); else return E_POINTER; } /*----------------------------------------------------------- -- Variant marshalling (we just lack VT_ARRAY support) -----------------------------------------------------------*/ void freeVariants(int count, VARIANT *p) { int i; if (!p) return; for (i = 0; i < count; i++) VariantClear(p + i); } int readVariantTag(VARIANT *pw) { return (pw->vt); } #define READWRITEVAR(htp, ctp, tp, fld, def) \ void writeVar##htp(ctp x, VARIANT *v) \ { \ WRITEVAR(tp, fld); \ } \ HRESULT readVar##htp(VARIANT *v, ctp *p) \ { \ READVAR(tp, fld, def); \ } #define WRITEVAR(tp, fld) \ { \ if (!v) \ return; \ VariantInit(v); \ v->vt = tp; \ v->fld = x; \ } #define READVAR(tp, fld, def) \ { \ if (!p) \ return E_POINTER; \ if (!v) \ { \ *p = def; \ return E_POINTER; \ } \ if (v->vt == tp) \ { \ *p = v->fld; \ return S_OK; \ } \ else \ { \ VARIANT w; \ HRESULT hr; \ VariantInit(&w); \ hr = VariantChangeType(&w, v, 0, tp); \ if (SUCCEEDED(hr)) \ *p = w.fld; \ else \ *p = def; \ return hr; \ } \ } #define READWRITETEMPVAR(htp, ctp, tp, fld, def) \ void writeVar##htp(ctp x, VARIANT *v) \ { \ WRITEVAR(tp, fld); \ } \ HRESULT readVar##htp(VARIANT *v, ctp *p, VARIANT **w) \ { \ READTEMPVAR(tp, fld, def); \ } #define READTEMPVAR(tp, fld, def) \ { \ if (w) \ *w = NULL; \ if (!p) \ return E_POINTER; \ if (!v) \ { \ *p = def; \ return E_POINTER; \ } \ if (v->vt == tp) \ { \ *p = v->fld; \ return S_OK; \ } \ else \ { \ HRESULT hr; \ *p = def; \ if (!w) \ return E_POINTER; \ *w = primAllocMemory(sizeof(VARIANT)); \ if (*w == NULL) \ return E_OUTOFMEMORY; \ VariantInit(*w); \ hr = VariantChangeType(*w, v, 0, tp); \ if (SUCCEEDED(hr)) \ *p = (*w)->fld; \ else \ { \ primFreeMemory(*w); \ *w = NULL; \ } \ return hr; \ } \ } READWRITEVAR(Short, int, VT_I2, iVal, 0) READWRITEVAR(Int, int, VT_I4, lVal, 0) READWRITEVAR(Word, unsigned int, VT_UI4, ulVal, 0) READWRITEVAR(Float, float, VT_R4, fltVal, 0.0) READWRITEVAR(Double, double, VT_R8, dblVal, 0.0) READWRITEVAR(Date, double, VT_DATE, date, 0.0) READWRITETEMPVAR(String, BSTR, VT_BSTR, bstrVal, NULL) READWRITETEMPVAR(Dispatch, IDispatch *, VT_DISPATCH, pdispVal, NULL) READWRITEVAR(Bool, BOOL, VT_BOOL, boolVal, 0) READWRITETEMPVAR(Unknown, IUnknown *, VT_UNKNOWN, punkVal, NULL) READWRITEVAR(Byte, unsigned char, VT_UI1, bVal, 0) #if __W32API_MAJOR_VERSION == 1 READWRITEVAR(Error, int, VT_ERROR, n1.n2.n3.scode, 0) #else READWRITEVAR(Error, int, VT_ERROR, scode, 0) #endif void writeVarOptional(VARIANT *v) { if (!v) return; VariantInit(v); v->vt = VT_ERROR; #if __W32API_MAJOR_VERSION == 1 v->n1.n2.n3.scode = DISP_E_PARAMNOTFOUND; #else v->scode = DISP_E_PARAMNOTFOUND; #endif } /* ToDo: readVarOptional */ void writeVarSAFEARRAY(VARIANT *v, SAFEARRAY *sa, VARTYPE ovt) { if (!v) return; VariantInit(v); v->vt = VT_ARRAY | ovt; v->parray = sa; } HRESULT readVarSAFEARRAY(VARIANT *v, SAFEARRAY **sa, VARTYPE ovt) { if (!v) return E_POINTER; if (v->vt & VT_ARRAY || v->vt == VT_SAFEARRAY) { /* Will return same pointer, but let's follow the rules. */ if (v->vt & VT_BYREF) { *sa = *(v->pparray); } else { *sa = v->parray; } return S_OK; } else { VARIANT w; HRESULT hr; VariantInit(&w); hr = VariantChangeType(&w, v, 0, VT_ARRAY | ovt); if (SUCCEEDED(hr)) { *sa = w.parray; } else { *sa = NULL; } return hr; } } /* VT_EMPTY, VT_NULL, VT_CY, VT_VARIANT */ void writeVarEmpty(VARIANT *v) { if (!v) return; VariantInit(v); v->vt = VT_EMPTY; } /* readEmpty; succeeds always */ void writeVarNull(VARIANT *v) { if (!v) return; VariantInit(v); v->vt = VT_NULL; } HRESULT readVarNull(VARIANT *v) { if (!v) return E_POINTER; if (v->vt == VT_NULL) return S_OK; else { VARIANT w; VariantInit(&w); return VariantChangeType(&w, v, 0, VT_NULL); } } void writeVarCurrency(int hi, unsigned int lo, VARIANT *v) { if (!v) return; VariantInit(v); v->vt = VT_CY; v->cyVal.Hi = hi; v->cyVal.Lo = lo; } HRESULT readVarCurrency(VARIANT *v, int *hi, unsigned int *lo) { if (!v) return E_POINTER; if (!hi) return E_POINTER; if (!lo) return E_POINTER; if (v->vt == VT_CY) { *hi = v->cyVal.Hi; *lo = v->cyVal.Lo; return S_OK; } else { VARIANT w; HRESULT hr; VariantInit(&w); fprintf(stderr, "0x%04x\n", v->vt); fflush(stderr); hr = VariantChangeType(&w, v, 0, VT_CY); if (SUCCEEDED(hr)) { *hi = w.cyVal.Hi; *lo = w.cyVal.Lo; } else { *hi = *lo = 0; } return hr; } } void writeVarWord64(unsigned int hi, unsigned int lo, VARIANT *v) { ULONGLONG r; r = (ULONGLONG)hi; r >>= 32; r += (ULONGLONG)lo; if (!v) return; VariantInit(v); v->vt = VT_DECIMAL; v->decVal.Lo64 = r; v->decVal.Hi32 = 0; v->decVal.sign = 0; v->decVal.scale = 0; } HRESULT readVarWord64(VARIANT *v, unsigned int *hi, unsigned int *lo) { ULONGLONG r; if (!v) return E_POINTER; if (!hi) return E_POINTER; if (!lo) return E_POINTER; if (v->vt == VT_DECIMAL) { r = v->decVal.Lo64; *lo = (unsigned long)r; r <<= 32; *hi = (unsigned long)r; return S_OK; } else { VARIANT w; HRESULT hr; VariantInit(&w); hr = VariantChangeType(&w, v, 0, VT_DECIMAL); if (SUCCEEDED(hr)) { r = v->decVal.Lo64; *lo = (unsigned long)r; r <<= 32; *hi = (unsigned long)r; } else { *hi = *lo = 0; } return hr; } } void writeVarVariant(VARIANT *x, VARIANT *v) { if (!v) return; if (!x) return; VariantInit(v); VariantCopy(v, x); } HRESULT readVarVariant(VARIANT *v, VARIANT *x) { if (!v) return E_POINTER; if (!x) return E_POINTER; VariantInit(x); return VariantCopy(x, v); } /* First VARIANT* is uninitialised chunk of memory. Initialise and copy the second one into it. */ HRESULT primCopyVARIANT(VARIANT *p1, VARIANT *p2) { VariantInit(p1); return (VariantCopy(p1, p2)); } HRESULT primVARIANTClear(VARIANT *p1) { return VariantClear(p1); } /* * Converting a time_t to date/time representation used by * Automation. */ HRESULT primClockToDate(int t, double *pt) { FILETIME ft; SYSTEMTIME st; LONGLONG l = Int32x32To64(t, 10000000) + 116444736000000000ULL; ft.dwLowDateTime = (DWORD)l; ft.dwHighDateTime = l >> 32; if (!FileTimeToSystemTime(&ft, &st)) { return E_FAIL; } #if ((!defined(__MINGW32__) && !defined(__CYGWIN__)) || \ (__W32API_MAJOR_VERSION >= 1 && (__W32API_MAJOR_VERSION > 1 || __W32API_MINOR_VERSION >= 1))) /* If on a non-gnuwin platform or on one with a w32api version that's 1.1 or greater. */ if (!SystemTimeToVariantTime(&st, pt)) { return E_FAIL; } return S_OK; #else /* SystemTimeToVariantTime() isn't provided in the oleaut32 import library until w32api 1.1 (or thereabouts, haven't exactly pinpointed when this bug fixed). */ return E_FAIL; #endif }