// This is a part of the Microsoft Foundation Classes C++ library. // Copyright (C) 1992-1998 Microsoft Corporation // All rights reserved. // // This source code is only intended as a supplement to the // Microsoft Foundation Classes Reference and related // electronic documentation provided with the library. // See these sources for detailed information regarding the // Microsoft Foundation Classes product. #include "stdafx.h" #include #ifdef AFX_OLE5_SEG #pragma code_seg(AFX_OLE5_SEG) #endif #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #define new DEBUG_NEW ///////////////////////////////////////////////////////////////////////////// // COleDispatchDriver constructors/destructors HRESULT AFXAPI AfxGetClassIDFromString(LPCTSTR lpsz, LPCLSID lpClsID) { USES_CONVERSION; HRESULT hr; if (lpsz[0] == '{') hr = CLSIDFromString((LPOLESTR)T2COLE(lpsz), lpClsID); else hr = CLSIDFromProgID(T2COLE(lpsz), lpClsID); return hr; } COleDispatchDriver::COleDispatchDriver() { m_lpDispatch = NULL; m_bAutoRelease = TRUE; } COleDispatchDriver::COleDispatchDriver(LPDISPATCH lpDispatch, BOOL bAutoRelease) { m_lpDispatch = lpDispatch; m_bAutoRelease = bAutoRelease; } COleDispatchDriver::COleDispatchDriver(const COleDispatchDriver& dispatchSrc) { ASSERT(this != &dispatchSrc); // constructing from self? m_lpDispatch = dispatchSrc.m_lpDispatch; if (m_lpDispatch != NULL) m_lpDispatch->AddRef(); m_bAutoRelease = TRUE; } const COleDispatchDriver& COleDispatchDriver::operator=(const COleDispatchDriver& dispatchSrc) { if (this != &dispatchSrc) { LPDISPATCH lpTemp = m_lpDispatch; m_lpDispatch = dispatchSrc.m_lpDispatch; if (m_lpDispatch != NULL) m_lpDispatch->AddRef(); if (lpTemp != NULL && m_bAutoRelease) lpTemp->Release(); m_bAutoRelease = TRUE; } return *this; } BOOL COleDispatchDriver::CreateDispatch(REFCLSID clsid, COleException* pError) { ASSERT(m_lpDispatch == NULL); m_bAutoRelease = TRUE; // good default is to auto-release // create an instance of the object LPUNKNOWN lpUnknown = NULL; SCODE sc = CoCreateInstance(clsid, NULL, CLSCTX_ALL | CLSCTX_REMOTE_SERVER, IID_IUnknown, (LPLP)&lpUnknown); if (sc == E_INVALIDARG) { // may not support CLSCTX_REMOTE_SERVER, so try without sc = CoCreateInstance(clsid, NULL, CLSCTX_ALL & ~CLSCTX_REMOTE_SERVER, IID_IUnknown, (LPLP)&lpUnknown); } if (FAILED(sc)) goto Failed; // make sure it is running sc = OleRun(lpUnknown); if (FAILED(sc)) goto Failed; // query for IDispatch interface m_lpDispatch = QUERYINTERFACE(lpUnknown, IDispatch); if (m_lpDispatch == NULL) goto Failed; lpUnknown->Release(); ASSERT(m_lpDispatch != NULL); return TRUE; Failed: RELEASE(lpUnknown); if (pError != NULL) pError->m_sc = sc; TRACE1("Warning: CreateDispatch returning scode = %s.\n", AfxGetFullScodeString(sc)); return FALSE; } BOOL COleDispatchDriver::CreateDispatch(LPCTSTR lpszProgID, COleException* pError) { ASSERT(m_lpDispatch == NULL); // map prog id to CLSID CLSID clsid; SCODE sc = AfxGetClassIDFromString(lpszProgID, &clsid); if (FAILED(sc)) { if (pError != NULL) pError->m_sc = sc; return FALSE; } // create with CLSID return CreateDispatch(clsid, pError); } void COleDispatchDriver::AttachDispatch(LPDISPATCH lpDispatch, BOOL bAutoRelease) { ASSERT(lpDispatch != NULL); ReleaseDispatch(); // detach previous m_lpDispatch = lpDispatch; m_bAutoRelease = bAutoRelease; } void COleDispatchDriver::ReleaseDispatch() { if (m_lpDispatch != NULL) { if (m_bAutoRelease) m_lpDispatch->Release(); m_lpDispatch = NULL; } } LPDISPATCH COleDispatchDriver::DetachDispatch() { LPDISPATCH lpDispatch = m_lpDispatch; m_lpDispatch = NULL; // detach without Release return lpDispatch; } ///////////////////////////////////////////////////////////////////////////// // COleDispatchDriver implementation void COleDispatchDriver::InvokeHelperV(DISPID dwDispID, WORD wFlags, VARTYPE vtRet, void* pvRet, const BYTE* pbParamInfo, va_list argList) { USES_CONVERSION; if (m_lpDispatch == NULL) { TRACE0("Warning: attempt to call Invoke with NULL m_lpDispatch!\n"); return; } DISPPARAMS dispparams; memset(&dispparams, 0, sizeof dispparams); // determine number of arguments if (pbParamInfo != NULL) dispparams.cArgs = lstrlenA((LPCSTR)pbParamInfo); DISPID dispidNamed = DISPID_PROPERTYPUT; if (wFlags & (DISPATCH_PROPERTYPUT|DISPATCH_PROPERTYPUTREF)) { ASSERT(dispparams.cArgs > 0); dispparams.cNamedArgs = 1; dispparams.rgdispidNamedArgs = &dispidNamed; } if (dispparams.cArgs != 0) { // allocate memory for all VARIANT parameters VARIANT* pArg = new VARIANT[dispparams.cArgs]; ASSERT(pArg != NULL); // should have thrown exception dispparams.rgvarg = pArg; memset(pArg, 0, sizeof(VARIANT) * dispparams.cArgs); // get ready to walk vararg list const BYTE* pb = pbParamInfo; pArg += dispparams.cArgs - 1; // params go in opposite order while (*pb != 0) { ASSERT(pArg >= dispparams.rgvarg); pArg->vt = *pb; // set the variant type if (pArg->vt & VT_MFCBYREF) { pArg->vt &= ~VT_MFCBYREF; pArg->vt |= VT_BYREF; } switch (pArg->vt) { case VT_UI1: pArg->bVal = va_arg(argList, BYTE); break; case VT_I2: pArg->iVal = va_arg(argList, short); break; case VT_I4: pArg->lVal = va_arg(argList, long); break; case VT_R4: pArg->fltVal = (float)va_arg(argList, double); break; case VT_R8: pArg->dblVal = va_arg(argList, double); break; case VT_DATE: pArg->date = va_arg(argList, DATE); break; case VT_CY: pArg->cyVal = *va_arg(argList, CY*); break; case VT_BSTR: { LPCOLESTR lpsz = va_arg(argList, LPOLESTR); pArg->bstrVal = ::SysAllocString(lpsz); if (lpsz != NULL && pArg->bstrVal == NULL) AfxThrowMemoryException(); } break; #if !defined(_UNICODE) && !defined(OLE2ANSI) case VT_BSTRA: { LPCSTR lpsz = va_arg(argList, LPSTR); pArg->bstrVal = ::SysAllocString(T2COLE(lpsz)); if (lpsz != NULL && pArg->bstrVal == NULL) AfxThrowMemoryException(); pArg->vt = VT_BSTR; } break; #endif case VT_DISPATCH: pArg->pdispVal = va_arg(argList, LPDISPATCH); break; case VT_ERROR: pArg->scode = va_arg(argList, SCODE); break; case VT_BOOL: V_BOOL(pArg) = (VARIANT_BOOL)(va_arg(argList, BOOL) ? -1 : 0); break; case VT_VARIANT: *pArg = *va_arg(argList, VARIANT*); break; case VT_UNKNOWN: pArg->punkVal = va_arg(argList, LPUNKNOWN); break; case VT_I2|VT_BYREF: pArg->piVal = va_arg(argList, short*); break; case VT_UI1|VT_BYREF: pArg->pbVal = va_arg(argList, BYTE*); 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: { // coerce BOOL into VARIANT_BOOL BOOL* pboolVal = va_arg(argList, BOOL*); *pboolVal = *pboolVal ? MAKELONG(-1, 0) : 0; pArg->pboolVal = (VARIANT_BOOL*)pboolVal; } 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; default: ASSERT(FALSE); // unknown type! break; } --pArg; // get ready to fill next argument ++pb; } } // initialize return value VARIANT* pvarResult = NULL; VARIANT vaResult; AfxVariantInit(&vaResult); if (vtRet != VT_EMPTY) pvarResult = &vaResult; // initialize EXCEPINFO struct EXCEPINFO excepInfo; memset(&excepInfo, 0, sizeof excepInfo); UINT nArgErr = (UINT)-1; // initialize to invalid arg // make the call SCODE sc = m_lpDispatch->Invoke(dwDispID, IID_NULL, 0, wFlags, &dispparams, pvarResult, &excepInfo, &nArgErr); // cleanup any arguments that need cleanup if (dispparams.cArgs != 0) { VARIANT* pArg = dispparams.rgvarg + dispparams.cArgs - 1; const BYTE* pb = pbParamInfo; while (*pb != 0) { switch ((VARTYPE)*pb) { #if !defined(_UNICODE) && !defined(OLE2ANSI) case VT_BSTRA: #endif case VT_BSTR: VariantClear(pArg); break; } --pArg; ++pb; } } delete[] dispparams.rgvarg; // throw exception on failure if (FAILED(sc)) { VariantClear(&vaResult); if (sc != DISP_E_EXCEPTION) { // non-exception error code AfxThrowOleException(sc); } // make sure excepInfo is filled in if (excepInfo.pfnDeferredFillIn != NULL) excepInfo.pfnDeferredFillIn(&excepInfo); // allocate new exception, and fill it COleDispatchException* pException = new COleDispatchException(NULL, 0, excepInfo.wCode); ASSERT(pException->m_wCode == excepInfo.wCode); if (excepInfo.bstrSource != NULL) { pException->m_strSource = excepInfo.bstrSource; SysFreeString(excepInfo.bstrSource); } if (excepInfo.bstrDescription != NULL) { pException->m_strDescription = excepInfo.bstrDescription; SysFreeString(excepInfo.bstrDescription); } if (excepInfo.bstrHelpFile != NULL) { pException->m_strHelpFile = excepInfo.bstrHelpFile; SysFreeString(excepInfo.bstrHelpFile); } pException->m_dwHelpContext = excepInfo.dwHelpContext; pException->m_scError = excepInfo.scode; // then throw the exception THROW(pException); ASSERT(FALSE); // not reached } if (vtRet != VT_EMPTY) { // convert return value if (vtRet != VT_VARIANT) { SCODE sc = VariantChangeType(&vaResult, &vaResult, 0, vtRet); if (FAILED(sc)) { TRACE0("Warning: automation return value coercion failed.\n"); VariantClear(&vaResult); AfxThrowOleException(sc); } ASSERT(vtRet == vaResult.vt); } // copy return value into return spot! switch (vtRet) { case VT_UI1: *(BYTE*)pvRet = vaResult.bVal; break; case VT_I2: *(short*)pvRet = vaResult.iVal; break; case VT_I4: *(long*)pvRet = vaResult.lVal; break; case VT_R4: *(_AFX_FLOAT*)pvRet = *(_AFX_FLOAT*)&vaResult.fltVal; break; case VT_R8: *(_AFX_DOUBLE*)pvRet = *(_AFX_DOUBLE*)&vaResult.dblVal; break; case VT_DATE: *(_AFX_DOUBLE*)pvRet = *(_AFX_DOUBLE*)&vaResult.date; break; case VT_CY: *(CY*)pvRet = vaResult.cyVal; break; case VT_BSTR: AfxBSTR2CString((CString*)pvRet, vaResult.bstrVal); SysFreeString(vaResult.bstrVal); break; case VT_DISPATCH: *(LPDISPATCH*)pvRet = vaResult.pdispVal; break; case VT_ERROR: *(SCODE*)pvRet = vaResult.scode; break; case VT_BOOL: *(BOOL*)pvRet = (V_BOOL(&vaResult) != 0); break; case VT_VARIANT: *(VARIANT*)pvRet = vaResult; break; case VT_UNKNOWN: *(LPUNKNOWN*)pvRet = vaResult.punkVal; break; default: ASSERT(FALSE); // invalid return type specified } } } void AFX_CDECL COleDispatchDriver::InvokeHelper(DISPID dwDispID, WORD wFlags, VARTYPE vtRet, void* pvRet, const BYTE* pbParamInfo, ...) { va_list argList; va_start(argList, pbParamInfo); InvokeHelperV(dwDispID, wFlags, vtRet, pvRet, pbParamInfo, argList); va_end(argList); } void COleDispatchDriver::GetProperty(DISPID dwDispID, VARTYPE vtProp, void* pvProp) const { ((COleDispatchDriver*)this)->InvokeHelper(dwDispID, DISPATCH_PROPERTYGET, vtProp, pvProp, NULL); } void AFX_CDECL COleDispatchDriver::SetProperty(DISPID dwDispID, VARTYPE vtProp, ...) { va_list argList; // really only one arg, but... va_start(argList, vtProp); BYTE rgbParams[2]; if (vtProp & VT_BYREF) { vtProp &= ~VT_BYREF; vtProp |= VT_MFCBYREF; } #if !defined(_UNICODE) && !defined(OLE2ANSI) if (vtProp == VT_BSTR) vtProp = VT_BSTRA; #endif rgbParams[0] = (BYTE)vtProp; rgbParams[1] = 0; WORD wFlags = (WORD)(vtProp == VT_DISPATCH ? DISPATCH_PROPERTYPUTREF : DISPATCH_PROPERTYPUT); InvokeHelperV(dwDispID, wFlags, VT_EMPTY, NULL, rgbParams, argList); va_end(argList); } /////////////////////////////////////////////////////////////////////////////