/* * A basic implementation for COM DLL implementor. * * Copyright (C) Hidenori TAKESHIMA * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include "windef.h" #include "winerror.h" #include "winbase.h" #include "wingdi.h" #include "winuser.h" #include "winnls.h" #include "ole2.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(comimpl); #include "comimpl.h" /* - All threading model including Apartment and Both are supported. - Aggregation is supported. - CoFreeUnusedLibraries() is supported. - DisableThreadLibraryCalls() is supported. */ static CRITICAL_SECTION csComImpl; static DWORD dwComImplRef; static HRESULT WINAPI IUnknown_fnQueryInterface(IUnknown* iface,REFIID riid,LPVOID *ppobj) { ICOM_THIS(COMIMPL_IUnkImpl,iface); size_t ofs; DWORD dwIndex; COMIMPL_IFDelegation* pDelegation; HRESULT hr; TRACE("(%p)->(%s,%p)\n",This,debugstr_guid(riid),ppobj); if ( ppobj == NULL ) return E_POINTER; *ppobj = NULL; ofs = 0; if ( IsEqualGUID( &IID_IUnknown, riid ) ) { TRACE("IID_IUnknown - returns inner object.\n"); } else { for ( dwIndex = 0; dwIndex < This->dwEntries; dwIndex++ ) { if ( IsEqualGUID( This->pEntries[dwIndex].piid, riid ) ) { ofs = This->pEntries[dwIndex].ofsVTPtr; break; } } if ( dwIndex == This->dwEntries ) { hr = E_NOINTERFACE; /* delegation */ pDelegation = This->pDelegationFirst; while ( pDelegation != NULL ) { hr = (*pDelegation->pOnQueryInterface)( iface, riid, ppobj ); if ( hr != E_NOINTERFACE ) break; pDelegation = pDelegation->pNext; } if ( hr == E_NOINTERFACE ) { FIXME("(%p) unknown interface: %s\n",This,debugstr_guid(riid)); } return hr; } } *ppobj = (LPVOID)(((char*)This) + ofs); IUnknown_AddRef((IUnknown*)(*ppobj)); return S_OK; } static ULONG WINAPI IUnknown_fnAddRef(IUnknown* iface) { ICOM_THIS(COMIMPL_IUnkImpl,iface); TRACE("(%p)->()\n",This); return InterlockedExchangeAdd(&(This->ref),1) + 1; } static ULONG WINAPI IUnknown_fnRelease(IUnknown* iface) { ICOM_THIS(COMIMPL_IUnkImpl,iface); LONG ref; TRACE("(%p)->()\n",This); ref = InterlockedExchangeAdd(&(This->ref),-1) - 1; if ( ref > 0 ) return (ULONG)ref; This->ref ++; if ( This->pOnFinalRelease != NULL ) (*(This->pOnFinalRelease))(iface); This->ref --; COMIMPL_FreeObj(This); return 0; } static ICOM_VTABLE(IUnknown) iunknown = { ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE /* IUnknown fields */ IUnknown_fnQueryInterface, IUnknown_fnAddRef, IUnknown_fnRelease, }; void COMIMPL_IUnkInit( COMIMPL_IUnkImpl* pImpl, IUnknown* punkOuter ) { TRACE("(%p)\n",pImpl); ICOM_VTBL(pImpl) = &iunknown; pImpl->pEntries = NULL; pImpl->dwEntries = 0; pImpl->pDelegationFirst = NULL; pImpl->pOnFinalRelease = NULL; pImpl->ref = 1; pImpl->punkControl = (IUnknown*)pImpl; /* for implementing aggregation. */ if ( punkOuter != NULL ) pImpl->punkControl = punkOuter; } void COMIMPL_IUnkAddDelegationHandler( COMIMPL_IUnkImpl* pImpl, COMIMPL_IFDelegation* pDelegation ) { pDelegation->pNext = pImpl->pDelegationFirst; pImpl->pDelegationFirst = pDelegation; } /************************************************************************/ static HRESULT WINAPI IClassFactory_fnQueryInterface(LPCLASSFACTORY iface,REFIID riid,LPVOID *ppobj); static ULONG WINAPI IClassFactory_fnAddRef(LPCLASSFACTORY iface); static ULONG WINAPI IClassFactory_fnRelease(LPCLASSFACTORY iface); static HRESULT WINAPI IClassFactory_fnCreateInstance(LPCLASSFACTORY iface,LPUNKNOWN pOuter,REFIID riid,LPVOID *ppobj); static HRESULT WINAPI IClassFactory_fnLockServer(LPCLASSFACTORY iface,BOOL dolock); static ICOM_VTABLE(IClassFactory) iclassfact = { ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE IClassFactory_fnQueryInterface, IClassFactory_fnAddRef, IClassFactory_fnRelease, IClassFactory_fnCreateInstance, IClassFactory_fnLockServer }; typedef struct { /* IUnknown fields */ ICOM_VFIELD(IClassFactory); LONG ref; /* IClassFactory fields */ const COMIMPL_CLASSENTRY* pEntry; } IClassFactoryImpl; static HRESULT WINAPI IClassFactory_fnQueryInterface(LPCLASSFACTORY iface,REFIID riid,LPVOID *ppobj) { ICOM_THIS(IClassFactoryImpl,iface); TRACE("(%p)->(%p,%p)\n",This,riid,ppobj); if ( ( IsEqualGUID( &IID_IUnknown, riid ) ) || ( IsEqualGUID( &IID_IClassFactory, riid ) ) ) { *ppobj = iface; IClassFactory_AddRef(iface); return S_OK; } return E_NOINTERFACE; } static ULONG WINAPI IClassFactory_fnAddRef(LPCLASSFACTORY iface) { ICOM_THIS(IClassFactoryImpl,iface); TRACE("(%p)->()\n",This); return InterlockedExchangeAdd(&(This->ref),1) + 1; } static ULONG WINAPI IClassFactory_fnRelease(LPCLASSFACTORY iface) { ICOM_THIS(IClassFactoryImpl,iface); LONG ref; TRACE("(%p)->()\n",This); ref = InterlockedExchangeAdd(&(This->ref),-1) - 1; if ( ref > 0 ) return (ULONG)ref; COMIMPL_FreeObj(This); return 0; } static HRESULT WINAPI IClassFactory_fnCreateInstance(LPCLASSFACTORY iface,LPUNKNOWN pOuter,REFIID riid,LPVOID *ppobj) { ICOM_THIS(IClassFactoryImpl,iface); HRESULT hr; IUnknown* punk; TRACE("(%p)->(%p,%s,%p)\n",This,pOuter,debugstr_guid(riid),ppobj); if ( ppobj == NULL ) return E_POINTER; if ( pOuter != NULL && !IsEqualGUID( riid, &IID_IUnknown ) ) return CLASS_E_NOAGGREGATION; *ppobj = NULL; hr = (*This->pEntry->pCreateIUnk)(pOuter,(void**)&punk); if ( hr != S_OK ) return hr; hr = IUnknown_QueryInterface(punk,riid,ppobj); IUnknown_Release(punk); return hr; } static HRESULT WINAPI IClassFactory_fnLockServer(LPCLASSFACTORY iface,BOOL dolock) { ICOM_THIS(IClassFactoryImpl,iface); HRESULT hr; TRACE("(%p)->(%d)\n",This,dolock); if (dolock) hr = IClassFactory_AddRef(iface); else hr = IClassFactory_Release(iface); return hr; } static HRESULT IClassFactory_Alloc( const CLSID* pclsid, void** ppobj ) { const COMIMPL_CLASSENTRY* pEntry; IClassFactoryImpl* pImpl; TRACE( "(%s,%p)\n", debugstr_guid(pclsid), ppobj ); pEntry = COMIMPL_ClassList; while ( pEntry->pclsid != NULL ) { if ( IsEqualGUID( pclsid, pEntry->pclsid ) ) goto found; pEntry ++; } return CLASS_E_CLASSNOTAVAILABLE; found: pImpl = (IClassFactoryImpl*)COMIMPL_AllocObj( sizeof(IClassFactoryImpl) ); if ( pImpl == NULL ) return E_OUTOFMEMORY; TRACE( "allocated successfully.\n" ); ICOM_VTBL(pImpl) = &iclassfact; pImpl->ref = 1; pImpl->pEntry = pEntry; *ppobj = (void*)pImpl; return S_OK; } /*********************************************************************** * COMIMPL_InitProcess (internal) */ static BOOL COMIMPL_InitProcess( HINSTANCE hInstDLL ) { TRACE("()\n"); dwComImplRef = 0; InitializeCriticalSection( &csComImpl ); #ifndef COMIMPL_PERTHREAD_INIT DisableThreadLibraryCalls((HMODULE)hInstDLL); #endif /* COMIMPL_PERTHREAD_INIT */ return TRUE; } /*********************************************************************** * COMIMPL_UninitProcess (internal) */ static void COMIMPL_UninitProcess( HINSTANCE hInstDLL ) { CHAR szThisDLL[ MAX_PATH ]; TRACE("()\n"); if ( dwComImplRef != 0 ) { szThisDLL[0] = '\0'; if ( !GetModuleFileNameA( (HMODULE)hInstDLL, szThisDLL, MAX_PATH ) ) szThisDLL[0] = '\0'; ERR( "you must release some objects allocated from %s.\n", szThisDLL ); } DeleteCriticalSection( &csComImpl ); } /*********************************************************************** * COMIMPL_DllMain */ BOOL WINAPI COMIMPL_DllMain( HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpvReserved ) { TRACE("(%08x,%08lx,%p)\n",hInstDLL,fdwReason,lpvReserved); switch ( fdwReason ) { case DLL_PROCESS_ATTACH: if ( !COMIMPL_InitProcess( hInstDLL ) ) return FALSE; break; case DLL_PROCESS_DETACH: COMIMPL_UninitProcess( hInstDLL ); break; case DLL_THREAD_ATTACH: break; case DLL_THREAD_DETACH: break; } return TRUE; } /*********************************************************************** * COMIMPL_DllGetClassObject */ HRESULT WINAPI COMIMPL_DllGetClassObject( const CLSID* pclsid,const IID* piid,void** ppv) { *ppv = NULL; if ( IsEqualGUID( &IID_IUnknown, piid ) || IsEqualGUID( &IID_IClassFactory, piid ) ) { return IClassFactory_Alloc( pclsid, ppv ); } return CLASS_E_CLASSNOTAVAILABLE; } /*********************************************************************** * COMIMPL_DllCanUnloadNow * * RETURNS * Success: S_OK * Failure: S_FALSE */ HRESULT WINAPI COMIMPL_DllCanUnloadNow(void) { HRESULT hr; EnterCriticalSection( &csComImpl ); hr = ( dwComImplRef == 0 ) ? S_OK : S_FALSE; LeaveCriticalSection( &csComImpl ); return hr; } /*********************************************************************** * COMIMPL_AllocObj */ void* COMIMPL_AllocObj( DWORD dwSize ) { void* pv; EnterCriticalSection( &csComImpl ); dwComImplRef ++; pv = HeapAlloc( COMIMPL_hHeap, 0, dwSize ); if ( pv == NULL ) dwComImplRef --; LeaveCriticalSection( &csComImpl ); return pv; } /*********************************************************************** * COMIMPL_FreeObj */ void COMIMPL_FreeObj( void* pobj ) { EnterCriticalSection( &csComImpl ); HeapFree( COMIMPL_hHeap, 0, pobj ); dwComImplRef --; LeaveCriticalSection( &csComImpl ); }