//------------------------------------------------------------------------------

// File: DllEntry.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



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, void ** ppv);

    STDMETHODIMP_(ULONG)AddRef();

    STDMETHODIMP_(ULONG)Release();



    // IClassFactory

    STDMETHODIMP CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, 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,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() {

    if(--m_cRef == 0) {

        delete this;

        return 0;

    }

    else {

        return m_cRef;

    }

}



STDMETHODIMP

CClassFactory::CreateInstance(

    LPUNKNOWN pUnkOuter,

    REFIID riid,

    void **pv) {

    CheckPointer(pv,E_POINTER)

    ValidateReadWritePtr(pv,sizeof(void *));



    /* Enforce the normal OLE rules regarding interfaces and delegation */



    if(pUnkOuter != NULL) {

        if(IsEqualIID(riid,IID_IUnknown) == FALSE) {

            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) {

        if(SUCCEEDED(hr)) {

            hr = E_OUTOFMEMORY;

        }

        return hr;

    }



    /* Delete the object if we got a construction error */



    if(FAILED(hr)) {

        delete pObj;

        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

STDAPI

DllGetClassObject(

    REFCLSID rClsID,

    REFIID riid,

    void **pv) {

    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" BOOL WINAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID);



BOOL WINAPI

DllEntryPoint(HINSTANCE hInstance, ULONG ulReason, LPVOID pv) {

#ifdef DEBUG

    extern bool g_fDbgInDllEntryPoint;

    g_fDbgInDllEntryPoint = true;

#endif



    switch(ulReason) {



        case DLL_PROCESS_ATTACH:

            DisableThreadLibraryCalls(hInstance);

#ifdef DEBUG

            DbgInitialise(hInstance);

#endif



            {

                // 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++;

                }



                DWORD cch = wsprintf(szInfo, TEXT("Executable: %s  Pid %x  Tid %x. "),

                    pName, GetCurrentProcessId(), GetCurrentThreadId());



                wsprintf(szInfo+cch, 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;

}







