/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public License * Version 1.0 (the "NPL"); you may not use this file except in * compliance with the NPL. You may obtain a copy of the NPL at * http://www.mozilla.org/NPL/ * * Software distributed under the NPL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL * for the specific language governing rights and limitations under the * NPL. * * The Initial Developer of this code under the NPL is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ #include "stdafx.h" #include "apiapi.h" // get all global interfaces class CAPIList { protected: CAPIList * m_pNext; CAPIList * m_pPrevious; void * m_pData; public: CAPIList(void * pData = NULL); ~CAPIList(); long elements(void); CAPIList * Add(void * element); CAPIList * Remove(void * element); void * GetAt (int element); inline CAPIList * GetNext(void) { return m_pNext; } inline CAPIList * GetPrevious(void) { return m_pPrevious; } inline CAPIList * SetNext(CAPIList * pElement) { CAPIList * pNext = m_pNext; m_pNext = pElement; return pNext; } inline CAPIList * SetPrevious(CAPIList * pElement) { CAPIList * pPrevious = m_pPrevious; m_pPrevious = pElement; return pPrevious; } inline void * GetData(void) { return m_pData; } }; CAPIList::CAPIList(void * pData) { m_pNext = NULL; m_pPrevious = NULL; SetNext(this); SetPrevious(this); m_pData = pData; } CAPIList::~CAPIList() { assert(m_pNext); assert(m_pPrevious); GetPrevious()->SetNext(GetNext()); GetNext()->SetPrevious(GetPrevious()); } CAPIList * CAPIList::Add(void * element) { CAPIList * pNode = new CAPIList(element); CAPIList * pOld = SetNext(pNode); pNode->SetNext(pOld); pOld->SetPrevious(pNode); pNode->SetPrevious(this); return pNode; } long CAPIList::elements(void) { CAPIList * pNode = this; long lcount = 0; do { lcount++; pNode = pNode->GetNext(); } while (pNode != this); return lcount; } CAPIList * CAPIList::Remove(void * element) { BOOL found = FALSE; CAPIList * pNode = this; do { if (pNode->GetData() == element) { found = TRUE; break; } pNode = pNode->GetNext(); } while (pNode != this); if (found) { if (GetNext() == this) { delete this; return NULL; } else { CAPIList * ptr = pNode->GetPrevious(); delete pNode; return ptr; } } return this; } void * CAPIList::GetAt ( int element ) { CAPIList * pNode = this; do { if (!element) return pNode->GetData(); element--; pNode = pNode->GetNext(); } while (pNode != this); return NULL; } // the API repository implements the APIAPI interface and maintains // the list of APIs, their IDs, and reference counts. class ApiEntry { protected: LPUNKNOWN m_pUnk; APISIGNATURE m_apiSig; public: ApiEntry(LPUNKNOWN pUnk = NULL, APISIGNATURE apiSig = NULL ) { m_pUnk =pUnk; m_apiSig = apiSig; } inline LPUNKNOWN GetUnk() { return m_pUnk; } inline APISIGNATURE GetSig() { return m_apiSig; } inline void SetSig(APISIGNATURE apiSig) { m_apiSig = apiSig; } inline void SetUnk(LPUNKNOWN pUnk) { m_pUnk = pUnk; } }; class ClassFactory { // class factory entries public: LPCLASSFACTORY m_pClassFactory; // class instance creation factory char * m_pszClassName; // name of class BOOL m_bIsLoaded; // is class factory loaded? char * m_pszModuleName; // module containing class factory HMODULE m_hModule; // factory dll instance CAPIList * m_ApiList; // list of api instances ClassFactory (char * pszClassName, LPCLASSFACTORY pFactory, char * pszModule = 0 ); ~ClassFactory(); LPUNKNOWN CreateClassInstance(LPUNKNOWN pUnkOuter, APISIGNATURE apiSig = NULL); LPUNKNOWN GetFirst(void); LPUNKNOWN GetNext(LPUNKNOWN); void Insert(LPUNKNOWN pUnk,APISIGNATURE apiSig = NULL); void Remove(LPUNKNOWN); LPUNKNOWN LocateBySignature(APISIGNATURE); BOOL AttemptToLoad(void); }; void ClassFactory::Insert( LPUNKNOWN pUnk, APISIGNATURE apiSig ) { if (!m_ApiList) { m_ApiList = new CAPIList((void*)(new ApiEntry(pUnk,apiSig))); } else { m_ApiList = m_ApiList->Add((void*)(new ApiEntry(pUnk,apiSig))); } } void ClassFactory::Remove(LPUNKNOWN pUnk) { if (m_ApiList) { ApiEntry * pEntry; for (int i = 0; i < m_ApiList->elements(); i++) { pEntry = (ApiEntry *)m_ApiList->GetAt(i); assert(pEntry); if (pEntry->GetUnk() == pUnk) { m_ApiList = m_ApiList->Remove((void*)pEntry); delete pEntry; return; } } } } LPUNKNOWN ClassFactory::GetFirst(void) { if (!m_ApiList) return 0; if (!m_ApiList->elements()) return 0; return ((ApiEntry *)(m_ApiList->GetAt(0)))->GetUnk(); } LPUNKNOWN ClassFactory::GetNext(LPUNKNOWN pUnk) { if (m_ApiList) { for (int i = 0; i < m_ApiList->elements() - 1; i++) if (((ApiEntry*)m_ApiList->GetAt(i))->GetUnk() == (void*)pUnk) return ((ApiEntry*)m_ApiList->GetAt(i+1))->GetUnk(); } return 0; } LPUNKNOWN ClassFactory::LocateBySignature(APISIGNATURE apiSig) { if (!apiSig || !m_ApiList) return NULL; ApiEntry * pEntry; for (int i = 0; i < m_ApiList->elements(); i++) { pEntry = (ApiEntry*)m_ApiList->GetAt(i); if (APISIGCMP(pEntry->GetSig(),apiSig)) return pEntry->GetUnk(); } return NULL; } ClassFactory::ClassFactory(char * pszClassName, LPCLASSFACTORY pFactory, char * pszModule) { assert(pszClassName); m_pClassFactory = pFactory; m_pszClassName = strdup(pszClassName); m_hModule = NULL; m_ApiList = NULL; if (pszModule != 0) { m_pszModuleName = strdup(pszModule); m_bIsLoaded = FALSE; } else { m_bIsLoaded = TRUE; m_pszModuleName = NULL; } } ClassFactory::~ClassFactory() { if (m_pszClassName != NULL) free (m_pszClassName); if (m_pszModuleName != NULL) free (m_pszModuleName); if (m_hModule != NULL) FreeLibrary(m_hModule); int elements; elements = m_ApiList ? (int)m_ApiList->elements( ): 0; while ( elements ) { ApiEntry * pEntry = (ApiEntry *)m_ApiList->GetAt(elements - 1); assert(pEntry); LPUNKNOWN entry = (LPUNKNOWN)pEntry->GetUnk(); // FIXME release objects here! if (entry) while (entry->Release()) ; delete pEntry; elements--; } } LPUNKNOWN ClassFactory::CreateClassInstance( LPUNKNOWN pUnkOuter, APISIGNATURE apiSig) { assert(m_pClassFactory); LPUNKNOWN pUnknown; if (m_pClassFactory->CreateInstance( pUnkOuter, IID_IUnknown, (LPVOID*)&pUnknown) == NOERROR) Insert(pUnknown,apiSig); return pUnknown; } class ClassRepository : public CGenericFactory, public IApiapi { protected: ULONG m_ulRefCount; CAPIList * m_ClassList; // lookup attempts to find an API entry based on id ClassFactory * Lookup (char * pszClassName); ClassFactory * GetFirst(void); ClassFactory * GetNext(ClassFactory *); void Remove(ClassFactory*); public: ClassRepository ( ); ~ClassRepository ( ); // IClassFactory Interface STDMETHODIMP CreateInstance(LPUNKNOWN,REFIID,LPVOID*); // IApiapi interface LPUNKNOWN GetFirstInstance(APICLASS lpszClassName); LPUNKNOWN GetNextInstance(APICLASS lpszClassName, LPUNKNOWN pUnk ); void * GetFirstApiInstance( REFIID refiid, LPUNKNOWN * ppUnk); int RemoveInstance( LPUNKNOWN pUnk ); LPUNKNOWN CreateClassInstance ( APICLASS lpszClassName,LPUNKNOWN pUnkOuter,APISIGNATURE apiSig); int RegisterClassFactory ( APICLASS lpszClassName, LPCLASSFACTORY pClassFactory, char * szModuleName = 0); }; ClassRepository::ClassRepository ( ) { m_ClassList = NULL; RegisterClassFactory(APICLASS_APIAPI,(LPCLASSFACTORY)this); } ClassRepository::~ClassRepository ( ) { if (m_ClassList) { int elements = (int)m_ClassList->elements( ); while ( elements ) { elements--; } } } STDMETHODIMP ClassRepository::CreateInstance( LPUNKNOWN pUnkOuter,REFIID refiid, LPVOID * ppvObj) { *ppvObj = NULL; ClassRepository * pRepository = new ClassRepository; return pRepository->QueryInterface(refiid,ppvObj); } int ClassRepository::RemoveInstance( LPUNKNOWN pUnk ) { if (m_ClassList) { int elements = (int) m_ClassList->elements(); int i; for ( i = 0; i < elements; i++ ) { ClassFactory * entry = (ClassFactory *)m_ClassList->GetAt(i); entry->Remove(pUnk); } } return 0; } ClassFactory * ClassRepository::GetFirst(void) { if (!m_ClassList) return 0; if (!m_ClassList->elements()) return 0; return (ClassFactory *)m_ClassList->GetAt(0); } ClassFactory * ClassRepository::GetNext(ClassFactory * pFactory) { if (m_ClassList) { for (int i = 0; i < m_ClassList->elements() - 1; i++) if (m_ClassList->GetAt(i) == (void*)pFactory) return (ClassFactory *)m_ClassList->GetAt(i+1); } return 0; } void ClassRepository::Remove(ClassFactory * pFactory) { if (m_ClassList) m_ClassList = m_ClassList->Remove((void*)pFactory); } LPUNKNOWN ClassRepository::CreateClassInstance( APICLASS lpszClassName, LPUNKNOWN pUnkOuter, APISIGNATURE apiSig ) { ClassFactory * pFactory = Lookup(lpszClassName); if (pFactory) { if (apiSig) { LPUNKNOWN pUnk; if ((pUnk = pFactory->LocateBySignature(apiSig)) != NULL) return pUnk; } return pFactory->CreateClassInstance(pUnkOuter,apiSig); } return NULL; } BOOL ClassFactory::AttemptToLoad(void) { if ((!m_pszModuleName) || m_bIsLoaded) return FALSE; // attempt to load the interface module (DLL) m_hModule = LoadLibrary (m_pszModuleName); if (m_hModule > (HMODULE)32) { // The class factory initializer is the first routine in the DLL. APIBOOTER fpApiInitialize = (APIBOOTER)GetProcAddress(m_hModule,MAKEINTRESOURCE(1)); // if we got a valid initializer, then call it with the // class name. This was the module can initialize one at // a time if it likes. if (fpApiInitialize != NULL ) { m_bIsLoaded = TRUE; if ((*fpApiInitialize)(m_pszClassName)) return TRUE; } else m_bIsLoaded = FALSE; } else { // couldn't load the module char szMessage[1024]; sprintf ( szMessage, "Cannot load module: %.800s\nClass factory %800s", m_pszModuleName, m_pszClassName); MessageBox ( 0, szMessage, "Interface Error", MB_OK | MB_ICONEXCLAMATION ); } return FALSE; } ClassFactory * ClassRepository::Lookup ( char * pszClassName ) { // get the number of stored elements if (m_ClassList) { int elements = (int) m_ClassList->elements(); int i; // look up the api for ( i = 0; i < elements; i++ ) { ClassFactory * entry = (ClassFactory *)m_ClassList->GetAt(i); if (!stricmp(entry->m_pszClassName, pszClassName)) { if (!entry->m_bIsLoaded) if (entry->AttemptToLoad()) return entry; else return 0; return entry; } } } return 0; } int ClassRepository::RegisterClassFactory ( char * pszClassName, LPCLASSFACTORY pClassFactory, char * szModuleName ) { ClassFactory * pClassEntry; if ((pClassEntry = Lookup (pszClassName)) != 0) { pClassEntry->m_pClassFactory = pClassFactory; return 1; } else { pClassEntry = new ClassFactory ( pszClassName, pClassFactory, szModuleName); if (!m_ClassList) m_ClassList = new CAPIList((void*)pClassEntry); else m_ClassList = m_ClassList->Add ((void*)pClassEntry ); } return 0; } void * ClassRepository::GetFirstApiInstance( REFIID refiid, LPUNKNOWN * ppUnk) { void * pSomeInterface; *ppUnk = 0; ClassFactory * pClass = GetFirst(); while (pClass) { LPUNKNOWN pUnk = pClass->GetFirst(); while (pUnk) { if (pUnk->QueryInterface(refiid,(LPVOID*)&pSomeInterface) == NOERROR) { *ppUnk = pUnk; return pSomeInterface; } pUnk = pClass->GetNext(pUnk); } pClass = GetNext(pClass); } return NULL; } // function looks up an API by id and returns a void pointer which in // turn can be cast to the requested interface type. This is done // for you by the Api template. LPUNKNOWN ClassRepository::GetFirstInstance ( char * pszClassName ) { if (m_ClassList) { for (int i = 0; i < m_ClassList->elements(); i++) { ClassFactory * pClassFactory = (ClassFactory *)m_ClassList->GetAt(i); if (!stricmp(pClassFactory->m_pszClassName, pszClassName)) { if (pClassFactory->m_ApiList) return ((ApiEntry*)pClassFactory->m_ApiList->GetAt(0))->GetUnk(); return NULL; } } } return NULL; } LPUNKNOWN ClassRepository::GetNextInstance ( char * pszClassName, LPUNKNOWN pUnk ) { if (m_ClassList) { for (int i = 0; i < m_ClassList->elements(); i++) { ClassFactory * pClassFactory = (ClassFactory *)m_ClassList->GetAt(i); if (!stricmp(pClassFactory->m_pszClassName, pszClassName)) { CAPIList * pList = pClassFactory->m_ApiList; if (pList) for (int j = 0; jelements()-1; j++) if (((ApiEntry*)pList->GetAt(j))->GetUnk() == pUnk) return ((ApiEntry*) pClassFactory->m_ApiList->GetAt(j+1))->GetUnk(); return NULL; } } } return NULL; } // The apiapi object MUST be constructed first, this pragma ensures that // the static constructor will occur before any other static construction // happens. #pragma warning( disable : 4074 ) #pragma init_seg(compiler) static ClassRepository classRepository; // global function gets the ApiApi from the static repository // From here, all registered APIs can be retrieved and set API_PUBLIC(LPAPIAPI) GetAPIAPI() { return (LPAPIAPI) &classRepository; } // Generic Objects // Generic Factory STDMETHODIMP CGenericFactory::QueryInterface(REFIID refiid, LPVOID * ppv) { *ppv = NULL; if (IsEqualIID(refiid,IID_IUnknown)) *ppv = (LPUNKNOWN) this; else if (IsEqualIID(refiid,IID_IClassFactory)) *ppv = (LPCLASSFACTORY) this; if (*ppv != NULL) { AddRef(); return NOERROR; } return ResultFromScode(E_NOINTERFACE); } STDMETHODIMP_(ULONG) CGenericFactory::AddRef(void) { return ++m_ulRefCount; } STDMETHODIMP_(ULONG) CGenericFactory::Release(void) { ULONG ulRef; ulRef = --m_ulRefCount; if (m_ulRefCount == 0) ; // delete this; return ulRef; } STDMETHODIMP CGenericFactory::CreateInstance( LPUNKNOWN pUnkOuter,REFIID refiid, LPVOID * ppvObj) { return NULL; // User Implemented } STDMETHODIMP CGenericFactory::LockServer(BOOL fLock) { return NOERROR; } // Generic Object STDMETHODIMP CGenericObject::QueryInterface(REFIID refiid, LPVOID * ppv) { return ResultFromScode(E_NOINTERFACE); } STDMETHODIMP_(ULONG) CGenericObject::AddRef(void) { return ++m_ulRefCount; } STDMETHODIMP_(ULONG) CGenericObject::Release(void) { ULONG ulRef; ulRef = --m_ulRefCount; if (m_ulRefCount == 0) { classRepository.RemoveInstance(this); delete this; } return ulRef; }