//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 1997. // // File: cdispmgr.cxx // // Contents: The dispatch manager -- a class to manage // multiple IDispatch-callable interfaces. // // Classes: CDispatchMgr // // Functions: None external. // // History: ??-???-?? KrishnaG created // 07-Sep-97 t-blakej Commented, cleaned up, made // independent of ADSI. // // See cdispmgr.hxx for a more thorough description of the dispatch manager. // //---------------------------------------------------------------------------- // // Since this class is useful outside of ADSI, some work has been taken to // make it not depend on any ADSI-specific code. It needs two ADSI header // files (cdispmgr.hxx and iprops.hxx), but they only depend on definitions // from standard system header files. // // To accomodate the current building method in ADSI, the precompiled // header "procs.hxx" is included; this includes all the necessary ADSI // header files and definitions. But for use outside of ADSI, the few // necessary header files are explicitly included below; see the comment by // "#ifndef ADsAssert". // // So if not compiling for ADSI, comment the following two lines out. // #include "procs.hxx" #pragma hdrstop ////////////////////////////////////////////////////////////////////////////// // // General helper definitions, routines, and inclusions: // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- Begin Non-ADSI compile stuff // // This macro is defined by the precompiled header file, so the following // will only be included if not compiling for ADSI. // #ifndef ADsAssert // // Necessary system headers. // #define UNICODE #define _UNICODE #define _OLEAUT32_ #define INC_OLE2 #include #include // // Necessary class definitions used by the dispatch manager. // Edit these paths if necessary. // #include "cdispmgr.hxx" #include "iprops.hxx" #endif // ADsAssert // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- End Non-ADSI compile stuff // // Error recovery. // #define BAIL_ON_FAILURE(hr) if (FAILED(hr)) { goto cleanup; } // // A simple assert function. // #if DBG == 1 //+--------------------------------------------------------------------------- // // Function: AssertEx // // Synopsis: Display assertion information. // // Effects: Called when an assertion is hit. // // History: Simplified from Win4AssertEx, to make this dispatch manager // not depend on other files. // //---------------------------------------------------------------------------- static void AssertEx(char const *szFile, int iLine, char const *szMessage) { static char szAssertCaption[199]; DWORD dwCount = strlen("File: %s line %u, thread id %d"); if (szFile) { dwCount += strlen(szFile); } dwCount += 15; // for the line number and thread id if (dwCount > 199) { sprintf(szAssertCaption, "Error, could not get more infomration"); } else { sprintf(szAssertCaption, "File: %s line %u, thread id %d", szFile, iLine, GetCurrentThreadId()); } if (IDCANCEL == MessageBoxA( NULL, (char *) szMessage, (LPSTR) szAssertCaption, MB_SETFOREGROUND | MB_TASKMODAL | MB_ICONEXCLAMATION | MB_OKCANCEL )) { DebugBreak(); } } # define DispMgrAssert(x) (void)((x) || (AssertEx(__FILE__, __LINE__, #x),0)) #else # define AssertEx(f,l,m) do {} while(0) # define DispMgrAssert(x) do {} while(0) #endif ////////////////////////////////////////////////////////////////////////////// // // Inline routines: static inline LONG getDispMgrId(DISPID InputDispId) { return (InputDispId & 0x7f000000) >> 24; } static inline LONG getTypeInfoId(DISPID InputDispId) { return (InputDispId & 0x00ff0000) >> 16; } static inline LONG getDispId(DISPID InputDispId) { return (InputDispId & 0x0000ffff); } static inline void MakeDISPID(LONG TypeInfoId, LONG DispId, DISPID *pResult) { if (DispId == DISPID_UNKNOWN) *pResult = DispId; else if (TypeInfoId < 0 || TypeInfoId >= 0x100 || DispId < 0 || DispId >= 0x10000) { // // Might happen if some object has very large dispid's. // But we can't handle it if it does. // *pResult = DISPID_UNKNOWN; } else *pResult = ((TypeInfoId & 0xff) << 16) | (DispId & 0xffff); } static inline void IncDispMgrIds(DISPID *rgdispid, unsigned int cNames) { for (unsigned int i = 0; i < cNames; ++i) { if (rgdispid[i] == DISPID_UNKNOWN) continue; // // This is either a stack of DispMgrs >127 high, or // a programming error. More likely the latter. // DispMgrAssert(getDispMgrId(rgdispid[i]) < 0x7f); rgdispid[i] = (rgdispid[i] & 0x00ffffff) | (((getDispMgrId(rgdispid[i]) + 1) & 0x7f) << 24); } } static inline void DecDispMgrIds(DISPID *rgdispid, unsigned int cNames) { for (unsigned int i = 0; i < cNames; ++i) { // // It should never be less than zero, and the only place // this is called from guarantees it is not zero. // DispMgrAssert(getDispMgrId(rgdispid[i]) > 0); rgdispid[i] = (rgdispid[i] & 0x00ffffff) | (((getDispMgrId(rgdispid[i]) - 1) & 0x7f) << 24); } } static inline void MakeDISPIDs(LONG TypeInfoId, DISPID *rgdispid, unsigned int cNames) { for (unsigned int i = 0; i < cNames; i++) { MakeDISPID(TypeInfoId, rgdispid[i], &rgdispid[i]); } } // // Struct defs // typedef struct _typeinfotable { GUID iid; ITypeInfo * pTypeInfo; struct _typeinfotable *pNext; }TYPEINFO_TABLE, *PTYPEINFO_TABLE; PTYPEINFO_TABLE gpTypeInfoTable = NULL; CRITICAL_SECTION g_DispTypeInfoCritSect; #define ENTER_DISP_TYPEINFO_CRITSECT() EnterCriticalSection(&g_DispTypeInfoCritSect) #define LEAVE_DISP_TYPEINFO_CRITSECT() LeaveCriticalSection(&g_DispTypeInfoCritSect) LONG glnObjCount = 0; LONG glnTypesOfInfo = 0; LONG glnOledbObjCnt = 0; ////////////////////////////////////////////////////////////////////////////// // // Public methods: CDispatchMgr::CDispatchMgr() { _pTypeInfoEntry = NULL; _pDispidNewEnum = NULL; _dwTypeInfoId = 0; _pDispidValue = NULL; _pPropertyCache = NULL; _dwPropCacheID = 0; _pDispatch = NULL; } CDispatchMgr::~CDispatchMgr() { PTYPEINFOENTRY pTypeInfoEntry = NULL; PTYPEINFOENTRY pTemp = NULL; ITypeInfo *pTypeInfo = NULL; pTypeInfoEntry = _pTypeInfoEntry; while (pTypeInfoEntry) { pTemp = pTypeInfoEntry; pTypeInfo = (ITypeInfo *)pTypeInfoEntry->ptypeinfo; pTypeInfo->Release(); InterlockedDecrement(&glnObjCount); pTypeInfoEntry = pTemp->pNext; LocalFree(pTemp); } ENTER_DISP_TYPEINFO_CRITSECT(); if (glnObjCount == glnTypesOfInfo) { // // We need to clean up the list // FreeTypeInfoTable(); glnObjCount = glnTypesOfInfo = 0; } LEAVE_DISP_TYPEINFO_CRITSECT(); if (_pDispatch) _pDispatch->Release(); } void CDispatchMgr::RegisterPropertyCache(IPropertyCache *pPropertyCache) { _pPropertyCache = pPropertyCache; _dwPropCacheID = gentypeinfoid(); } void CDispatchMgr::RegisterBaseDispatchPtr(IDispatch *pDispatch) { _pDispatch = pDispatch; } STDMETHODIMP CDispatchMgr::GetTypeInfoCount(unsigned int *pctinfo) { return E_NOTIMPL; } STDMETHODIMP CDispatchMgr::GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo **pptinfo) { return E_NOTIMPL; } STDMETHODIMP CDispatchMgr::GetIDsOfNames(REFIID iid, LPWSTR *rgszNames, unsigned int cNames, LCID lcid, DISPID *rgdispid) { PTYPEINFOENTRY pTypeInfo = NULL; HRESULT hr = DISP_E_UNKNOWNNAME; // // Try our list of TypeInfos. // pTypeInfo = _pTypeInfoEntry; while (pTypeInfo) { hr = DispGetIDsOfNames(((ITypeInfo *)pTypeInfo->ptypeinfo), rgszNames, cNames, rgdispid ); if (SUCCEEDED(hr)) { MakeDISPIDs(pTypeInfo->TypeInfoId, rgdispid, cNames); return hr; } pTypeInfo = pTypeInfo->pNext; } // // Try our property cache. // if (FAILED(hr) && _pPropertyCache) { hr = S_OK; for (DWORD dw = 0; dw < cNames; dw++) { if (FAILED(_pPropertyCache->locateproperty(rgszNames[dw], (PDWORD)(rgdispid + dw)))) { hr = DISP_E_UNKNOWNNAME; rgdispid[dw] = DISPID_UNKNOWN; } } if (SUCCEEDED(hr)) { MakeDISPIDs(_dwPropCacheID, rgdispid, cNames); } } // // Try our "base class" dispatch pointer. // if (FAILED(hr) && _pDispatch) { hr = _pDispatch->GetIDsOfNames(iid, rgszNames, cNames, lcid, rgdispid); if (SUCCEEDED(hr)) { IncDispMgrIds(rgdispid, cNames); } } return hr; } STDMETHODIMP CDispatchMgr::Invoke(DISPID dispidMember, REFIID iid, LCID lcid, unsigned short wFlags, DISPPARAMS *pdispparams, VARIANT *pvarResult, EXCEPINFO *pexcepinfo, unsigned int *puArgErr) { // // Clear the error object before we call invoke. // SetErrorInfo(0, NULL); return TypeInfoInvoke(dispidMember, iid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr ); } ////////////////////////////////////////////////////////////////////////////// // // Private methods and helper functions: void * CDispatchMgr::getInterfacePtr(LONG TypeInfoId) { PTYPEINFOENTRY pTypeInfoEntry = FindTypeInfoEntry(TypeInfoId); return (pTypeInfoEntry ? pTypeInfoEntry->pInterfacePointer : NULL); } ITypeInfo * CDispatchMgr::getTypeInfo(LONG TypeInfoId) { PTYPEINFOENTRY pTypeInfoEntry = FindTypeInfoEntry(TypeInfoId); return (ITypeInfo *)(pTypeInfoEntry ? pTypeInfoEntry->ptypeinfo : NULL); } PTYPEINFOENTRY CDispatchMgr::FindTypeInfoEntry(LONG TypeInfoId) { PTYPEINFOENTRY pTypeInfoEntry; pTypeInfoEntry = _pTypeInfoEntry; while (pTypeInfoEntry) { if (pTypeInfoEntry->TypeInfoId == TypeInfoId) { return pTypeInfoEntry; } pTypeInfoEntry = pTypeInfoEntry->pNext; } return NULL; } PTYPEINFOENTRY CDispatchMgr::FindTypeInfo(void *pTypeInfo) { PTYPEINFOENTRY pTypeInfoEntry; pTypeInfoEntry = _pTypeInfoEntry; while (pTypeInfoEntry) { if (pTypeInfoEntry->ptypeinfo == pTypeInfo) { return pTypeInfoEntry; } pTypeInfoEntry = pTypeInfoEntry->pNext; } return NULL; } HRESULT CDispatchMgr::AddTypeInfo(void *ptypeinfo, void *pIntfptr) { PTYPEINFOENTRY pTypeInfoEntry = NULL; HRESULT hr; if (FindTypeInfo(ptypeinfo)) { return E_FAIL; } pTypeInfoEntry = (PTYPEINFOENTRY)LocalAlloc(LPTR,sizeof(TYPEINFOENTRY)); if (!pTypeInfoEntry) { BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); } pTypeInfoEntry->ptypeinfo = ptypeinfo; pTypeInfoEntry->TypeInfoId = gentypeinfoid(); pTypeInfoEntry->pInterfacePointer = pIntfptr; pTypeInfoEntry->pNext = _pTypeInfoEntry; _pTypeInfoEntry = pTypeInfoEntry; return S_OK; cleanup: return hr; } STDMETHODIMP CDispatchMgr::TypeInfoInvoke(DISPID dispidMember, REFIID iid, LCID lcid, unsigned short wFlags, DISPPARAMS *pdispparams, VARIANT *pvarResult, EXCEPINFO *pexcepinfo, unsigned int *puArgErr) { void *pInterfacePtr = NULL; DISPID dispid = 0; DISPID typeinfoid = 0; ITypeInfo *pTypeInfo = NULL; HRESULT hr = S_OK; if (dispidMember <= 0) { // // One of the special DISPIDs. // // If we have an interface pointer for it, use that. // If we don't, and we have a base IDispatch pointer, // pass it to the base pointer's Invoke() method. // If we don't, and we don't have a base IDispatch pointer, // return failure. // dispid = dispidMember; switch (dispid) { case DISPID_VALUE: if (_pDispidValue) { pTypeInfo = (ITypeInfo *)_pDispidValue->ptypeinfo; pInterfacePtr = _pDispidValue->pInterfacePointer; } break; case DISPID_NEWENUM: if (_pDispidNewEnum) { pTypeInfo = (ITypeInfo *)_pDispidNewEnum->ptypeinfo; pInterfacePtr = _pDispidNewEnum->pInterfacePointer; } break; default: break; } if (!pInterfacePtr) { if (_pDispatch) { return _pDispatch->Invoke(dispidMember, iid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr); } else { BAIL_ON_FAILURE(hr = DISP_E_MEMBERNOTFOUND); } }else { // // Fill in the special case scenarios here // hr = DispInvoke( pInterfacePtr, pTypeInfo, dispid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr ); return(hr); } } else if (getDispMgrId(dispidMember)) { // // A regular DISPID of a "base class" dispatch manager. // if (!_pDispatch){ char szMessage[80]; sprintf(szMessage, "_pDispatch is NULL, dispid = 0x%08x", dispidMember); AssertEx(__FILE__, __LINE__, szMessage); return DISP_E_MEMBERNOTFOUND; } DecDispMgrIds(&dispidMember, 1); hr = _pDispatch->Invoke( dispidMember, iid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr ); return(hr); } else { // // A regular DISPID of ours. // typeinfoid = getTypeInfoId(dispidMember); dispid = getDispId(dispidMember); if ((_pPropertyCache == NULL) || (typeinfoid != _dwPropCacheID)) { pInterfacePtr = getInterfacePtr(typeinfoid); pTypeInfo = getTypeInfo(typeinfoid); if (!pTypeInfo) // // Shouldn't happen. // BAIL_ON_FAILURE(hr = DISP_E_MEMBERNOTFOUND); } if ((_pPropertyCache == NULL) || (typeinfoid != _dwPropCacheID)) { // // A regular interface. // hr = DispInvoke( pInterfacePtr, pTypeInfo, dispid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr ); }else { // // A "dynamic DISPID", for the property cache. // hr = DynamicDispidInvoke( _pPropertyCache, dispid, wFlags, pdispparams, pvarResult ); } } cleanup: return hr; } HRESULT DynamicDispidInvoke( IPropertyCache * pPropertyCache, DISPID dispid, unsigned short wFlags, DISPPARAMS *pdispparams, VARIANT * pvarResult ) { HRESULT hr = S_OK; if (!pPropertyCache) { return(E_INVALIDARG); } if (wFlags & DISPATCH_PROPERTYGET) { if (!pvarResult) { BAIL_ON_FAILURE(hr = E_INVALIDARG); } hr = pPropertyCache->getproperty((DWORD)dispid,pvarResult); if (FAILED(hr)) { #if 1 // This lets us return S_OK and a VT_EMPTY variant if // there's no data. #if this out to disable it. V_VT(pvarResult) = VT_EMPTY; hr = S_OK; #else V_VT(pvarResult) = VT_ERROR; #endif } }else if (wFlags & DISPATCH_PROPERTYPUT) { if (pdispparams[0].cArgs != 1){ hr = DISP_E_BADPARAMCOUNT; } else { hr = pPropertyCache->putproperty( (DWORD)dispid, pdispparams[0].rgvarg[0] ); } }else { hr = E_INVALIDARG; } cleanup: return(hr); } HRESULT CDispatchMgr::MarkAsNewEnum(void *pTypeInfo) { PTYPEINFOENTRY pTypeInfoEntry; if (!pTypeInfo) { return E_FAIL; } if (!(pTypeInfoEntry = FindTypeInfo(pTypeInfo))) { return E_FAIL; } _pDispidNewEnum = pTypeInfoEntry; return S_OK; } HRESULT CDispatchMgr::MarkAsItem(void *pTypeInfo) { PTYPEINFOENTRY pTypeInfoEntry; if (!pTypeInfo) { return E_FAIL; } if (!(pTypeInfoEntry = FindTypeInfo(pTypeInfo))) { return E_FAIL; } _pDispidValue = pTypeInfoEntry; return S_OK; } LONG CDispatchMgr::gentypeinfoid() { // // This would mean we've registered 65536 IDispatch methods // in this object's dispatch manager. We lose. // DispMgrAssert(_dwTypeInfoId < 0xffff); return (_dwTypeInfoId++); } ITypeInfo * FindTypeInfo( PTYPEINFO_TABLE pTypeInfoTable, REFIID iid ) { PTYPEINFO_TABLE pTemp = NULL; pTemp = pTypeInfoTable; while (pTemp) { if (IsEqualIID(iid, pTemp->iid)) { return pTemp->pTypeInfo; } pTemp = pTemp->pNext; } return NULL; } PTYPEINFO_TABLE AddTypeInfo( PTYPEINFO_TABLE pTypeInfoTable, REFIID iid, ITypeInfo * pTypeInfo ) { PTYPEINFO_TABLE pTemp = NULL; pTemp = (PTYPEINFO_TABLE)LocalAlloc(LPTR, sizeof(TYPEINFO_TABLE)); if (!pTemp) { return NULL; } memcpy(&pTemp->iid, &iid, sizeof(GUID)); pTemp->pTypeInfo = pTypeInfo; pTemp->pNext = pTypeInfoTable; return pTemp; } //+------------------------------------------------------------------------ // // Function: LoadTypeInfo // // Synopsis: Loads a typeinfo from a registered typelib. // // Arguments: [clsidTL] -- TypeLib GUID // [clsidTI] -- TypeInfo GUID // [ppTI] -- Resulting typeInfo // // Returns: HRESULT // //------------------------------------------------------------------------- HRESULT LoadTypeInfo(CLSID clsidTL, CLSID clsidTI, LPTYPEINFO *ppTI) { HRESULT hr; ITypeLib * pTL; DispMgrAssert(ppTI); *ppTI = NULL; hr = LoadRegTypeLib(clsidTL, 1, 0, LOCALE_SYSTEM_DEFAULT, &pTL); if (!SUCCEEDED(hr)) return hr; hr = pTL->GetTypeInfoOfGuid(clsidTI, ppTI); pTL->Release(); return hr; } HRESULT CDispatchMgr::LoadTypeInfoEntry( REFIID libid, REFIID iid, void * pIntf, DISPID SpecialId ) { ITypeInfo * pTypeInfo = NULL; HRESULT hr; ENTER_DISP_TYPEINFO_CRITSECT(); pTypeInfo = ::FindTypeInfo(gpTypeInfoTable, iid); if (!pTypeInfo) { LEAVE_DISP_TYPEINFO_CRITSECT(); hr = LoadTypeInfo(libid, iid, &pTypeInfo); BAIL_ON_FAILURE(hr); ENTER_DISP_TYPEINFO_CRITSECT(); gpTypeInfoTable = ::AddTypeInfo( gpTypeInfoTable, iid, pTypeInfo ); if (!gpTypeInfoTable) { LEAVE_DISP_TYPEINFO_CRITSECT(); BAIL_ON_FAILURE(hr = HRESULT_FROM_WIN32(GetLastError())); } // // Increment the global list of the types of type info's. // InterlockedIncrement(&glnTypesOfInfo); InterlockedIncrement(&glnObjCount); } pTypeInfo->AddRef(); hr = AddTypeInfo(pTypeInfo, pIntf); if (FAILED(hr)) { LEAVE_DISP_TYPEINFO_CRITSECT(); BAIL_ON_FAILURE(hr); } // // We have a ref on the object as add was succesful // InterlockedIncrement(&glnObjCount); LEAVE_DISP_TYPEINFO_CRITSECT(); if (SpecialId == -4) { hr = MarkAsNewEnum(pTypeInfo); } else if (SpecialId == DISPID_VALUE) { hr = MarkAsItem(pTypeInfo); } return S_OK; cleanup: if (pTypeInfo) pTypeInfo->Release(); return hr; } // // Kept for backwards compatibility. // HRESULT LoadTypeInfoEntry( CDispatchMgr *pDispMgr, REFIID libid, REFIID iid, void * pIntf, DISPID SpecialId ) { return pDispMgr->LoadTypeInfoEntry(libid, iid, pIntf, SpecialId); } void FreeTypeInfoTable() { PTYPEINFO_TABLE pTypeInfoTable = NULL; PTYPEINFO_TABLE pTemp = NULL; ITypeInfo * pTypeInfo = NULL; pTypeInfoTable = gpTypeInfoTable; while (pTypeInfoTable) { pTypeInfo = pTypeInfoTable->pTypeInfo; pTypeInfo->Release(); pTemp = pTypeInfoTable; pTypeInfoTable = pTypeInfoTable->pNext; LocalFree(pTemp); } gpTypeInfoTable = NULL; } BOOL DllReadyToUnload() { BOOL retVal = FALSE; ENTER_DISP_TYPEINFO_CRITSECT(); retVal = ((glnTypesOfInfo == 0) && (glnOledbObjCnt == 0)); LEAVE_DISP_TYPEINFO_CRITSECT(); return retVal; }