//
//  Copyright 2001 - Microsoft Corporation
//
//  Created By:
//      Geoff Pease (GPease)    23-JAN-2001
//
//  Maintained By:
//      Geoff Pease (GPease)    23-JAN-2001
//
//  Notes:
//      If ENTRY_PREFIX is defined, this indicates that your are trying to
//      included proxy/stub code into the DLL that is generated by the
//      MIDL compiler.
//
//////////////////////////////////////////////////////////////////////////////

#include "pch.h"
#include <InitGuid.h>
#include "Guids.h"

#include <shfusion.h>

#include "DocProp.h"
#include "DefProp.h"
#include "PropertyCacheItem.h"
#include "IEditVariantsInPlace.h"
#include "EditTypeItem.h"
#include "MLEditTypeItem.h"
#include "DropListTypeItem.h"
#include "CalendarTypeItem.h"

#pragma hdrstop

DEFINE_MODULE("DOCPROP3")

//
// Classes in this Component
//
// This table is used to create the objects supported in this DLL. It also is
// used to map a name with a particular CLSID. HrCoCreateInternalInstance() uses
// this table to shortcut COM.
//
//            CreateInstance                        CLSID                                 User Friendly Name                                   Apartment Model
BEGIN_CLASSTABLE 
DEFINE_CLASS( CDocPropShExt::CreateInstance       , CLSID_DocPropShellExtension         , "Microsoft DocProp Shell Ext"                      , "Apartment" )
DEFINE_CLASS( CEditTypeItem::CreateInstance       , CLSID_DocPropEditBoxControl         , "Microsoft DocProp Inplace Edit Box Control"       , "Apartment" )
DEFINE_CLASS( CMLEditTypeItem::CreateInstance     , CLSID_DocPropMLEditBoxControl       , "Microsoft DocProp Inplace ML Edit Box Control"    , "Apartment" )
DEFINE_CLASS( CDropListTypeItem::CreateInstance   , CLSID_DocPropDropListComboControl   , "Microsoft DocProp Inplace Droplist Combo Control" , "Apartment" )
DEFINE_CLASS( CCalendarTypeItem::CreateInstance   , CLSID_DocPropCalendarControl        , "Microsoft DocProp Inplace Calendar Control"       , "Apartment" )  
DEFINE_CLASS( CEditTypeItem::CreateInstance       , CLSID_DocPropTimeControl            , "Microsoft DocProp Inplace Time Control"           , "Apartment" )
END_CLASSTABLE


//
// DLL Globals
//

HINSTANCE g_hInstance = NULL;
LONG      g_cObjects  = 0;
LONG      g_cLock     = 0;
TCHAR     g_szDllFilename[ MAX_PATH ] = { 0 };

LPVOID    g_GlobalMemoryList = NULL;    // Global memory tracking list

#if defined( ENTRY_PREFIX )
extern "C"
{
    extern HINSTANCE hProxyDll;
}
#endif

//
//  Macros to generate RPC entry points
//
#define __rpc_macro_expand2(a, b) a##b
#define __rpc_macro_expand(a, b) __rpc_macro_expand2(a,b)

#if !defined(NO_DLL_MAIN) || defined(ENTRY_PREFIX) || defined(DEBUG)

//
// Description:
//      Dll entry point.
//
BOOL WINAPI
DllMain(
    HINSTANCE hInstIn,      //  DLL instance
    ULONG     ulReasonIn,   //  DLL reason code for entrance.
    LPVOID                  //  lpReservedIn
    )
{
    //
    // KB: NO_THREAD_OPTIMIZATIONS gpease 19-OCT-1999
    //
    // By not defining this you can prvent the linker
    // from calling you DllEntry for every new thread.
    // This makes creating new thread significantly
    // faster if every DLL in a process does it.
    // Unfortunately, not all DLLs do this.
    //
    // In CHKed/DEBUG, we keep this on for memory
    // tracking.
    //
#if defined( DEBUG )
    #define NO_THREAD_OPTIMIZATIONS
#endif // DEBUG

#if defined(NO_THREAD_OPTIMIZATIONS)
    switch( ulReasonIn )
    {
        case DLL_PROCESS_ATTACH:
        {
            SHFusionInitializeFromModule( hInstIn );
#if defined(USE_WMI_TRACING)
            TraceInitializeProcess( g_rgTraceControlGuidList,
                                    ARRAYSIZE( g_rgTraceControlGuidList )
                                    );
#else
            TraceInitializeProcess();
#endif
            TraceCreateMemoryList( g_GlobalMemoryList );
            TraceMemoryDelete( g_GlobalMemoryList, FALSE ); // can't track this list.

#if defined( DEBUG )
            TraceFunc( "" );
            TraceMessage( TEXT(__FILE__),
                          __LINE__,
                          __MODULE__,
                          mtfDLL,
                          TEXT("DLL: DLL_PROCESS_ATTACH - ThreadID = %#x"),
                          GetCurrentThreadId( )
                          );
            FRETURN( TRUE );
#endif // DEBUG
            g_hInstance = (HINSTANCE) hInstIn;

#if defined( ENTRY_PREFIX )
             hProxyDll = g_hInstance;
#endif

            GetModuleFileName( g_hInstance, g_szDllFilename, MAX_PATH );
            break;
        }

        case DLL_PROCESS_DETACH:
        {
#if defined( DEBUG )
            TraceFunc( "" );
            TraceMessage( TEXT(__FILE__),
                          __LINE__,
                          __MODULE__,
                          mtfDLL,
                          TEXT("DLL: DLL_PROCESS_DETACH - ThreadID = %#x [ g_cLock=%u, g_cObjects=%u ]"),
                          GetCurrentThreadId( ),
                          g_cLock,
                          g_cObjects
                          );
            FRETURN( TRUE );
#endif // DEBUG
            TraceMemoryAddAddress( g_GlobalMemoryList );
            TraceTerminateMemoryList( g_GlobalMemoryList );
#if defined(USE_WMI_TRACING)
            TraceTerminateProcess( g_rgTraceControlGuidList,
                                   ARRAYSIZE( g_rgTraceControlGuidList )
                                   );
#else
            TraceTerminateProcess();
#endif
            SHFusionUninitialize();
            break;
        }

        case DLL_THREAD_ATTACH:
        {
            TraceInitializeThread( NULL );
#if defined( DEBUG )
            TraceMessage( TEXT(__FILE__),
                          __LINE__,
                          __MODULE__,
                          mtfDLL,
                          TEXT("The thread %#x has started."),
                          GetCurrentThreadId( ) );
            TraceFunc( "" );
            TraceMessage( TEXT(__FILE__),
                          __LINE__,
                          __MODULE__,
                          mtfDLL,
                          TEXT("DLL: DLL_THREAD_ATTACH - ThreadID = %#x [ g_cLock=%u, g_cObjects=%u ]"),
                          GetCurrentThreadId( ),
                          g_cLock,
                          g_cObjects
                          );
            FRETURN( TRUE );
#endif // DEBUG
            break;
        }

        case DLL_THREAD_DETACH:
        {
#if defined( DEBUG )
            TraceFunc( "" );
            TraceMessage( TEXT(__FILE__),
                          __LINE__,
                          __MODULE__,
                          mtfDLL,
                          TEXT("DLL: DLL_THREAD_DETACH - ThreadID = %#x [ g_cLock=%u, g_cObjects=%u ]"),
                          GetCurrentThreadId( ),
                          g_cLock,
                          g_cObjects
                          );
            FRETURN( TRUE );
#endif // DEBUG
            TraceTerminateThread( );;
            break;
        }

        default:
        {
#if defined( DEBUG )
            TraceFunc( "" );
            TraceMessage( TEXT(__FILE__),
                          __LINE__,
                          __MODULE__,
                          mtfDLL,
                          TEXT("DLL: UNKNOWN ENTRANCE REASON - ThreadID = %#x [ g_cLock=%u, g_cObjects=%u ]"),
                          GetCurrentThreadId( ),
                          g_cLock,
                          g_cObjects
                          );
            FRETURN( TRUE );
#endif // DEBUG
            break;
        }
    }

    return TRUE;

#else // !NO_THREAD_OPTIMIZATIONS

    Assert( ulReasonIn == DLL_PROCESS_ATTACH || ulReasonIn == DLL_PROCESS_DETACH );

    if ( DLL_PROCESS_ATTACH == ulReasonIn )
    {
        SHFusionInitializeFromModule( hInstIn );
        g_hInstance = (HINSTANCE) hInstIn;
#ifdef ENTRY_PREFIX
         hProxyDll = g_hInstance;
#endif

#ifdef DEBUG
#ifdef USE_WMI_TRACING
        TraceInitializeProcess( g_rgTraceControlGuidList,
                                ARRAYSIZE( g_rgTraceControlGuidList )
                                );
#else
        TraceInitializeProcess();
#endif USE_WMI_TRACING
#endif DEBUG
        GetModuleFileName( g_hInstance, g_szDllFilename, MAX_PATH );
        BOOL fResult = DisableThreadLibraryCalls( g_hInstance );
        AssertMsg( fResult, "*ERROR* DisableThreadLibraryCalls( ) failed."  );
    }
    else
    {
#ifdef DEBUG
#ifdef USE_WMI_TRACING
        TraceTerminateProcess( g_rgTraceControlGuidList,
                               ARRAYSIZE( g_rgTraceControlGuidList )
                               );
#else
        TraceTerminateProcess();
#endif USE_WMI_TRACING
#endif DEBUG
        SHFusionUninitialize();
    }

    return TRUE;
#endif // NO_THREAD_OPTIMIZATIONS

}

#endif // !defined(NO_DLL_MAIN) && !defined(ENTRY_PREFIX) && !defined(DEBUG)

//
// Description:
//      OLE calls this to get the class factory from the DLL.
//
// Return Values:
//      S_OK
//          Success.
//
//      any other HRESULT to indicate failure.
//
STDAPI
DllGetClassObject(
    REFCLSID rclsidIn,  //  class ID of the object that the class factory should create.
    REFIID riidIn,      //  Interface of the class factory
    void** ppvOut       //  The interface pointer to the class factory.
    )
{
    TraceFunc( "rclsidIn, riidIn, ppvOut" );

    if ( ppvOut == NULL )
    {
        HRETURN(E_POINTER);
    }

    LPCFACTORY  lpClassFactory;
    HRESULT     hr;
    int         idx;

    hr = CLASS_E_CLASSNOTAVAILABLE;
    idx = 0;
    while( g_DllClasses[ idx ].rclsid )
    {
        if ( *g_DllClasses[ idx ].rclsid == rclsidIn )
        {
            TraceMessage( TEXT(__FILE__), __LINE__, __MODULE__, mtfFUNC, L"rclsidIn = %s", g_DllClasses[ idx ].pszName );
            hr = S_OK;
            break;

        }

        idx++;

    }

    if ( hr == CLASS_E_CLASSNOTAVAILABLE )
    {
        TraceMsgGUID( mtfFUNC, "rclsidIn = ", rclsidIn );
#if defined( ENTRY_PREFIX )
        //
        //  See if the MIDL generated code can create it.
        //
        hr = STHR( __rpc_macro_expand( ENTRY_PREFIX, DllGetClassObject )( rclsidIn, riidIn, ppvOut ) );
#endif // defined( ENTRY_PREFIX )
        goto Cleanup;
    }

    Assert( g_DllClasses[ idx ].pfnCreateInstance != NULL );

    lpClassFactory = new CFactory;
    if ( lpClassFactory == NULL )
    {
        hr = E_OUTOFMEMORY;
        goto Cleanup;

    }

    hr = THR( lpClassFactory->Init( g_DllClasses[ idx ].pfnCreateInstance ) );
    if ( FAILED( hr ) )
    {
        TraceDo( lpClassFactory->Release( ) );
        goto Cleanup;

    }

    // Can't safe type.
    hr = lpClassFactory->QueryInterface( riidIn, ppvOut );

    //
    // Release the created instance to counter the AddRef( ) in Init( ).
    //

    ((IUnknown *) lpClassFactory )->Release( );

Cleanup:
    HRETURN(hr);

}

//
// Description:
//      OLE's register entry point.
//
// Return Values:
//      S_OK
//          Success.
//
//      any other HRESULT.
//
STDAPI
DllRegisterServer( void )
{
    HRESULT hr;

    TraceFunc( "" );

    hr = THR( HrRegisterDll( TRUE ) );

#if defined( ENTRY_PREFIX )
    if ( SUCCEEDED( hr ) )
    {
        hr = THR( __rpc_macro_expand( ENTRY_PREFIX, DllRegisterServer )( ) );
    }
#endif // defined( ENTRY_PREFIX )

    if ( SUCCEEDED( hr ) )
    {
        hr = CDocPropShExt::RegisterShellExtensions( TRUE );
    }

    HRETURN(hr);

}

//
// Description:
//      OLE's unregister entry point.
//
// Return Values:
//      S_OK
//          Success.
//
//      any other HRESULT
//
STDAPI
DllUnregisterServer( void )
{
    TraceFunc( "" );

    HRESULT hr;

    hr = THR( HrRegisterDll( FALSE ) );

#if defined( ENTRY_PREFIX )
    if ( SUCCEEDED( hr ) )
    {
        hr = THR( __rpc_macro_expand( ENTRY_PREFIX, DllUnregisterServer )( ) );
    }
#endif // defined( ENTRY_PREFIX )

    if ( SUCCEEDED( hr ) )
    {
        hr = CDocPropShExt::RegisterShellExtensions( TRUE );
    }

    HRETURN( hr );

}

//
// Description:
//      OLE calls this entry point to see if it can unload the DLL.
//
// Return Values:
//      S_OK 
//          Can unload the DLL.
//
//      S_FALSE 
//          Can NOT unload the DLL.
//
STDAPI
DllCanUnloadNow( void )
{
    TraceFunc( "" );

    HRESULT hr = S_OK;

    if ( g_cLock || g_cObjects )
    {
        TraceMsg( mtfDLL, "DLL: Can't unload - g_cLock=%u, g_cObjects=%u", g_cLock, g_cObjects );
        hr = S_FALSE;

    } 
#if defined( ENTRY_PREFIX )
    else
    {
        //
        //  Check with the MIDL generated proxy/stubs.
        //
        hr = STHR( __rpc_macro_expand( ENTRY_PREFIX, DllCanUnloadNow )( ) );
    }
#endif

    HRETURN(hr);

}

//
// Description:
//      Mimic CoCreateInstance( ) except that it looks into the DLL table
//      to see if we can shortcut the CoCreate with a simple CreateInstance
//      call.
//
// Return Values:
//      S_OK
//          Success.
//
//      E_OUTOFMEMORY
//          Out of memory.
//
//      other HRESULT values
//
HRESULT
HrCoCreateInternalInstance(
    REFCLSID rclsidIn,      //  Class identifier (CLSID) of the object
    LPUNKNOWN pUnkOuterIn,  //  Pointer to controlling IUnknown
    DWORD dwClsContextIn,   //  Context for running executable code
    REFIID riidIn,          //  Reference to the identifier of the interface
    LPVOID * ppvOut         //  Address of output variable that receives
    )
{
    TraceFunc( "" );

    Assert( ppvOut != NULL );

    HRESULT hr = CLASS_E_CLASSNOTAVAILABLE;

    //
    // Limit simple CoCreate( ) only works to INPROC and non-aggregatable objects.
    //

    if ( ( dwClsContextIn & CLSCTX_INPROC_HANDLER )     // inproc only
      && ( NULL == pUnkOuterIn )                        // no aggregation
       )
    {
        int idx;

        //
        // Try to find the class in our DLL table.
        //
        for( idx = 0; g_DllClasses[ idx ].rclsid != NULL; idx++ )
        {
            if ( *g_DllClasses[ idx ].rclsid == rclsidIn )
            {
                LPUNKNOWN punk;
                Assert( g_DllClasses[ idx ].pfnCreateInstance != NULL );

                hr = THR( g_DllClasses[ idx ].pfnCreateInstance( &punk ) );
                if ( SUCCEEDED( hr ) )
                {
                    // Can't safe type.
                    hr = THR( punk->QueryInterface( riidIn, ppvOut ) );
                    punk->Release( );
                }

                break;  // bail loop
            }
        }
    }

    //
    // If not found or asking for something we do not support,
    // use the COM version.
    //

    if ( hr == CLASS_E_CLASSNOTAVAILABLE )
    {
        //
        // Try it the old fashion way...
        //
        hr = THR( CoCreateInstance( rclsidIn, pUnkOuterIn, dwClsContextIn, riidIn, ppvOut ) );

    }

    HRETURN( hr );
}


//
// TODO:    gpease 27-NOV-1999
//          Whilest parrusing the around the MIDL SDK, I foud that
//          RPC creates the same type of class table we do. Maybe
//          we can leverage the MIDL code to create our objects(??).
//
