| //------------------------------------------------------------------------------ | |
| // File: DlleEntry.cpp | |
| // | |
| // Desc: DirectShow base classes - implements classes used to support dll | |
| // entry points for COM objects. | |
| // | |
| // Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. | |
| //------------------------------------------------------------------------------ | |
| #include <streams.h> | |
| #include <initguid.h> | |
| #ifdef DEBUG | |
| #ifdef UNICODE | |
| #ifndef _UNICODE | |
| #define _UNICODE | |
| #endif // _UNICODE | |
| #endif // UNICODE | |
| #include <tchar.h> | |
| #endif // DEBUG | |
| #include <strsafe.h> | |
| extern CFactoryTemplate g_Templates[]; | |
| extern int g_cTemplates; | |
| HINSTANCE g_hInst; | |
| DWORD g_amPlatform; // VER_PLATFORM_WIN32_WINDOWS etc... (from GetVersionEx) | |
| OSVERSIONINFO g_osInfo; | |
| // | |
| // an instance of this is created by the DLLGetClassObject entrypoint | |
| // it uses the CFactoryTemplate object it is given to support the | |
| // IClassFactory interface | |
| class CClassFactory : public IClassFactory, public CBaseObject | |
| { | |
| private: | |
| const CFactoryTemplate *const m_pTemplate; | |
| ULONG m_cRef; | |
| static int m_cLocked; | |
| public: | |
| CClassFactory(const CFactoryTemplate *); | |
| // IUnknown | |
| STDMETHODIMP QueryInterface(REFIID riid, __deref_out void ** ppv); | |
| STDMETHODIMP_(ULONG)AddRef(); | |
| STDMETHODIMP_(ULONG)Release(); | |
| // IClassFactory | |
| STDMETHODIMP CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, __deref_out void **pv); | |
| STDMETHODIMP LockServer(BOOL fLock); | |
| // allow DLLGetClassObject to know about global server lock status | |
| static BOOL IsLocked() { | |
| return (m_cLocked > 0); | |
| }; | |
| }; | |
| // process-wide dll locked state | |
| int CClassFactory::m_cLocked = 0; | |
| CClassFactory::CClassFactory(const CFactoryTemplate *pTemplate) | |
| : CBaseObject(NAME("Class Factory")) | |
| , m_cRef(0) | |
| , m_pTemplate(pTemplate) | |
| { | |
| } | |
| STDMETHODIMP | |
| CClassFactory::QueryInterface(REFIID riid,__deref_out void **ppv) | |
| { | |
| CheckPointer(ppv,E_POINTER) | |
| ValidateReadWritePtr(ppv,sizeof(PVOID)); | |
| *ppv = NULL; | |
| // any interface on this object is the object pointer. | |
| if ((riid == IID_IUnknown) || (riid == IID_IClassFactory)) { | |
| *ppv = (LPVOID) this; | |
| // AddRef returned interface pointer | |
| ((LPUNKNOWN) *ppv)->AddRef(); | |
| return NOERROR; | |
| } | |
| return ResultFromScode(E_NOINTERFACE); | |
| } | |
| STDMETHODIMP_(ULONG) | |
| CClassFactory::AddRef() | |
| { | |
| return ++m_cRef; | |
| } | |
| STDMETHODIMP_(ULONG) | |
| CClassFactory::Release() | |
| { | |
| LONG lRef = InterlockedDecrement((volatile LONG *)&m_cRef); | |
| if (lRef == 0) { | |
| delete this; | |
| return 0; | |
| } else { | |
| return lRef; | |
| } | |
| } | |
| STDMETHODIMP | |
| CClassFactory::CreateInstance( | |
| LPUNKNOWN pUnkOuter, | |
| REFIID riid, | |
| __deref_out void **pv) | |
| { | |
| CheckPointer(pv,E_POINTER) | |
| ValidateReadWritePtr(pv,sizeof(void *)); | |
| *pv = NULL; | |
| /* Enforce the normal OLE rules regarding interfaces and delegation */ | |
| if (pUnkOuter != NULL) { | |
| if (IsEqualIID(riid,IID_IUnknown) == FALSE) { | |
| *pv = NULL; | |
| return ResultFromScode(E_NOINTERFACE); | |
| } | |
| } | |
| /* Create the new object through the derived class's create function */ | |
| HRESULT hr = NOERROR; | |
| CUnknown *pObj = m_pTemplate->CreateInstance(pUnkOuter, &hr); | |
| if (pObj == NULL) { | |
| *pv = NULL; | |
| if (SUCCEEDED(hr)) { | |
| hr = E_OUTOFMEMORY; | |
| } | |
| return hr; | |
| } | |
| /* Delete the object if we got a construction error */ | |
| if (FAILED(hr)) { | |
| delete pObj; | |
| *pv = NULL; | |
| return hr; | |
| } | |
| /* Get a reference counted interface on the object */ | |
| /* We wrap the non-delegating QI with NDAddRef & NDRelease. */ | |
| /* This protects any outer object from being prematurely */ | |
| /* released by an inner object that may have to be created */ | |
| /* in order to supply the requested interface. */ | |
| pObj->NonDelegatingAddRef(); | |
| hr = pObj->NonDelegatingQueryInterface(riid, pv); | |
| pObj->NonDelegatingRelease(); | |
| /* Note that if NonDelegatingQueryInterface fails, it will */ | |
| /* not increment the ref count, so the NonDelegatingRelease */ | |
| /* will drop the ref back to zero and the object will "self-*/ | |
| /* destruct". Hence we don't need additional tidy-up code */ | |
| /* to cope with NonDelegatingQueryInterface failing. */ | |
| if (SUCCEEDED(hr)) { | |
| ASSERT(*pv); | |
| } | |
| return hr; | |
| } | |
| STDMETHODIMP | |
| CClassFactory::LockServer(BOOL fLock) | |
| { | |
| if (fLock) { | |
| m_cLocked++; | |
| } else { | |
| m_cLocked--; | |
| } | |
| return NOERROR; | |
| } | |
| // --- COM entrypoints ----------------------------------------- | |
| //called by COM to get the class factory object for a given class | |
| __control_entrypoint(DllExport) STDAPI | |
| DllGetClassObject( | |
| __in REFCLSID rClsID, | |
| __in REFIID riid, | |
| __deref_out void **pv) | |
| { | |
| *pv = NULL; | |
| if (!(riid == IID_IUnknown) && !(riid == IID_IClassFactory)) { | |
| return E_NOINTERFACE; | |
| } | |
| // traverse the array of templates looking for one with this | |
| // class id | |
| for (int i = 0; i < g_cTemplates; i++) { | |
| const CFactoryTemplate * pT = &g_Templates[i]; | |
| if (pT->IsClassID(rClsID)) { | |
| // found a template - make a class factory based on this | |
| // template | |
| *pv = (LPVOID) (LPUNKNOWN) new CClassFactory(pT); | |
| if (*pv == NULL) { | |
| return E_OUTOFMEMORY; | |
| } | |
| ((LPUNKNOWN)*pv)->AddRef(); | |
| return NOERROR; | |
| } | |
| } | |
| return CLASS_E_CLASSNOTAVAILABLE; | |
| } | |
| // | |
| // Call any initialization routines | |
| // | |
| void | |
| DllInitClasses(BOOL bLoading) | |
| { | |
| int i; | |
| // traverse the array of templates calling the init routine | |
| // if they have one | |
| for (i = 0; i < g_cTemplates; i++) { | |
| const CFactoryTemplate * pT = &g_Templates[i]; | |
| if (pT->m_lpfnInit != NULL) { | |
| (*pT->m_lpfnInit)(bLoading, pT->m_ClsID); | |
| } | |
| } | |
| } | |
| // called by COM to determine if this dll can be unloaded | |
| // return ok unless there are outstanding objects or a lock requested | |
| // by IClassFactory::LockServer | |
| // | |
| // CClassFactory has a static function that can tell us about the locks, | |
| // and CCOMObject has a static function that can tell us about the active | |
| // object count | |
| STDAPI | |
| DllCanUnloadNow() | |
| { | |
| DbgLog((LOG_MEMORY,2,TEXT("DLLCanUnloadNow called - IsLocked = %d, Active objects = %d"), | |
| CClassFactory::IsLocked(), | |
| CBaseObject::ObjectsActive())); | |
| if (CClassFactory::IsLocked() || CBaseObject::ObjectsActive()) { | |
| return S_FALSE; | |
| } else { | |
| return S_OK; | |
| } | |
| } | |
| // --- standard WIN32 entrypoints -------------------------------------- | |
| extern "C" void __cdecl __security_init_cookie(void); | |
| extern "C" BOOL WINAPI _DllEntryPoint(HINSTANCE, ULONG, __inout_opt LPVOID); | |
| #pragma comment(linker, "/merge:.CRT=.rdata") | |
| extern "C" | |
| DECLSPEC_NOINLINE | |
| BOOL | |
| WINAPI | |
| DllEntryPoint( | |
| HINSTANCE hInstance, | |
| ULONG ulReason, | |
| __inout_opt LPVOID pv | |
| ) | |
| { | |
| if ( ulReason == DLL_PROCESS_ATTACH ) { | |
| // Must happen before any other code is executed. Thankfully - it's re-entrant | |
| __security_init_cookie(); | |
| } | |
| return _DllEntryPoint(hInstance, ulReason, pv); | |
| } | |
| DECLSPEC_NOINLINE | |
| BOOL | |
| WINAPI | |
| _DllEntryPoint( | |
| HINSTANCE hInstance, | |
| ULONG ulReason, | |
| __inout_opt LPVOID pv | |
| ) | |
| { | |
| #ifdef DEBUG | |
| extern bool g_fDbgInDllEntryPoint; | |
| g_fDbgInDllEntryPoint = true; | |
| #endif | |
| switch (ulReason) | |
| { | |
| case DLL_PROCESS_ATTACH: | |
| DisableThreadLibraryCalls(hInstance); | |
| DbgInitialise(hInstance); | |
| { | |
| // The platform identifier is used to work out whether | |
| // full unicode support is available or not. Hence the | |
| // default will be the lowest common denominator - i.e. N/A | |
| g_amPlatform = VER_PLATFORM_WIN32_WINDOWS; // win95 assumed in case GetVersionEx fails | |
| g_osInfo.dwOSVersionInfoSize = sizeof(g_osInfo); | |
| if (GetVersionEx(&g_osInfo)) { | |
| g_amPlatform = g_osInfo.dwPlatformId; | |
| } else { | |
| DbgLog((LOG_ERROR, 1, TEXT("Failed to get the OS platform, assuming Win95"))); | |
| } | |
| } | |
| g_hInst = hInstance; | |
| DllInitClasses(TRUE); | |
| break; | |
| case DLL_PROCESS_DETACH: | |
| DllInitClasses(FALSE); | |
| #ifdef DEBUG | |
| if (CBaseObject::ObjectsActive()) { | |
| DbgSetModuleLevel(LOG_MEMORY, 2); | |
| TCHAR szInfo[512]; | |
| extern TCHAR m_ModuleName[]; // Cut down module name | |
| TCHAR FullName[_MAX_PATH]; // Load the full path and module name | |
| TCHAR *pName; // Searches from the end for a backslash | |
| GetModuleFileName(NULL,FullName,_MAX_PATH); | |
| pName = _tcsrchr(FullName,'\\'); | |
| if (pName == NULL) { | |
| pName = FullName; | |
| } else { | |
| pName++; | |
| } | |
| (void)StringCchPrintf(szInfo, NUMELMS(szInfo), TEXT("Executable: %s Pid %x Tid %x. "), | |
| pName, GetCurrentProcessId(), GetCurrentThreadId()); | |
| (void)StringCchPrintf(szInfo+lstrlen(szInfo), NUMELMS(szInfo) - lstrlen(szInfo), TEXT("Module %s, %d objects left active!"), | |
| m_ModuleName, CBaseObject::ObjectsActive()); | |
| DbgAssert(szInfo, TEXT(__FILE__),__LINE__); | |
| // If running remotely wait for the Assert to be acknowledged | |
| // before dumping out the object register | |
| DbgDumpObjectRegister(); | |
| } | |
| DbgTerminate(); | |
| #endif | |
| break; | |
| } | |
| #ifdef DEBUG | |
| g_fDbgInDllEntryPoint = false; | |
| #endif | |
| return TRUE; | |
| } | |