////////////////////////////////////////////////////////////////////////////// // // Copyright (c) 1999-2001 Microsoft Corporation // // Module Name: // DllSrc.cpp // // Description: // DLL services/entry points. // // Maintained By: // David Potter (DavidP) 19-MAR-2001 // Geoffrey Pease (GPease) 18-OCT-1999 // // Notes: // Switches: // - ENTRY_PREFIX // If defined, include proxy/stub code into the DLL that is // generated by the MIDL compiler. // - USE_FUSION // If defined, initialize and uninitialize Fusion on process // attach and detach respectively. The constant IDR_MANIFEST // must be defined with a value that represents the resource ID // for the manifest resource. // - NO_DLL_MAIN // If defined, don't implement DllMain. // - DEBUG_SW_TRACING_ENABLED // If defined, initialize and uninitialize software tracing on // process attach and detach respectively. // -- NO_THREAD_OPTIMIZATIONS // If defined, disable the thread notification calls when a thread // starts up or goes away. // ////////////////////////////////////////////////////////////////////////////// // // 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 ) ////////////////////////////////////////////////////////////////////////////// //++ // // DllMain // // Description: // Dll entry point. // // Arguments: // hInstIn - DLL instance handle. // ulReasonIn - DLL reason code for entrance. // lpReservedIn - Not used. // // Return Values: // TRUE // //-- ////////////////////////////////////////////////////////////////////////////// BOOL WINAPI DllMain( HINSTANCE hInstIn, ULONG ulReasonIn, LPVOID // lpReservedIn ) { // // KB: THREAD_OPTIMIZATIONS gpease 19-OCT-1999 // // By defining this you can prevent the linker // from calling your DllEntry for every new thread. // This makes creating new threads 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 THREAD_OPTIMIZATIONS #endif // DEBUG switch( ulReasonIn ) { ////////////////////////////////////////////////////////////////////// // DLL_PROCESS_ATTACH ////////////////////////////////////////////////////////////////////// case DLL_PROCESS_ATTACH: { #if defined( DEBUG_SW_TRACING_ENABLED ) TraceInitializeProcess( g_rgTraceControlGuidList, ARRAYSIZE( g_rgTraceControlGuidList ), TRUE ); #else // ! DEBUG_SW_TRACING_ENABLED TraceInitializeProcess( TRUE ); #endif // DEBUG_SW_TRACING_ENABLED TraceFunc( "" ); TraceMessage( TEXT(__FILE__), __LINE__, __MODULE__, mtfDLL, TEXT("DLL: DLL_PROCESS_ATTACH - ThreadID = %#x"), GetCurrentThreadId() ); g_hInstance = hInstIn; #if defined( ENTRY_PREFIX ) hProxyDll = g_hInstance; #endif // ENTRY_PREFIX GetModuleFileName( g_hInstance, g_szDllFilename, ARRAYSIZE( g_szDllFilename ) ); // // Create a global memory list so that memory allocated by one // thread and handed to another can be tracked without causing // unnecessary trace messages. // TraceCreateMemoryList( g_GlobalMemoryList ); #if defined( THREAD_OPTIMIZATIONS ) { // // Disable thread library calls so that we don't get called // on thread attach and detach. // BOOL fResult = DisableThreadLibraryCalls( g_hInstance ); if ( ! fResult ) { TW32MSG( GetLastError(), "DisableThreadLibraryCalls()" ); } } #endif // THREAD_OPTIMIZATIONS #if defined( USE_FUSION ) // // Initialize Fusion. // // The value of IDR_MANIFEST in the call to // SHFusionInitializeFromModuleID() must match the value specified in the // sources file for SXS_MANIFEST_RESOURCE_ID. // BOOL fResult = SHFusionInitializeFromModuleID( hInstIn, IDR_MANIFEST ); if ( ! fResult ) { TW32MSG( GetLastError(), "SHFusionInitializeFromModuleID()" ); } #endif // USE_FUSION // // This is necessary here because TraceFunc() defines a variable // on the stack which isn't available outside the scope of this // block. // This function doesn't do anything but clean up after // TraceFunc(). // FRETURN( TRUE ); break; } // case: DLL_PROCESS_ATTACH ////////////////////////////////////////////////////////////////////// // DLL_PROCESS_DETACH ////////////////////////////////////////////////////////////////////// case DLL_PROCESS_DETACH: { 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 ); // // Cleanup the global memory list used to track memory allocated // in one thread and then handed to another. // TraceTerminateMemoryList( g_GlobalMemoryList ); // // This is necessary here because TraceFunc() defines a variable // on the stack which isn't available outside the scope of this // block. // This function doesn't do anything but clean up after // TraceFunc(). // FRETURN( TRUE ); #if defined( DEBUG_SW_TRACING_ENABLED ) TraceTerminateProcess( g_rgTraceControlGuidList, ARRAYSIZE( g_rgTraceControlGuidList ) ); #else // ! DEBUG_SW_TRACING_ENABLED TraceTerminateProcess(); #endif // DEBUG_SW_TRACING_ENABLED #if defined( USE_FUSION ) SHFusionUninitialize(); #endif // USE_FUSION break; } // case: DLL_PROCESS_DETACH #if ! defined( THREAD_OPTIMIZATIONS ) ////////////////////////////////////////////////////////////////////// // DLL_THREAD_ATTACH ////////////////////////////////////////////////////////////////////// case DLL_THREAD_ATTACH: { TraceInitializeThread( NULL ); TraceMessage( TEXT(__FILE__), __LINE__, __MODULE__, mtfDLL, TEXT("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 ); // // This is necessary here because TraceFunc() defines a variable // on the stack which isn't available outside the scope of this // block. // This function doesn't do anything but clean up after // TraceFunc(). // FRETURN( TRUE ); break; } // case: DLL_THREAD_ATTACH ////////////////////////////////////////////////////////////////////// // DLL_THREAD_DETACH ////////////////////////////////////////////////////////////////////// case DLL_THREAD_DETACH: { 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 ); // // This is necessary here because TraceFunc() defines a variable // on the stack which isn't available outside the scope of this // block. // This function doesn't do anything but clean up after // TraceFunc(). // FRETURN( TRUE ); TraceThreadRundown(); break; } // case: DLL_THREAD_DETACH #endif // ! THREAD_OPTIMIZATIONS default: { 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 ); #if defined( THREAD_OPTIMIZATIONS ) Assert( ( ulReasonIn != DLL_THREAD_ATTACH ) && ( ulReasonIn != DLL_THREAD_DETACH ) ); #endif // THREAD_OPTIMIZATIONS // // This is necessary here because TraceFunc defines a variable // on the stack which isn't available outside the scope of this // block. // This function doesn't do anything but clean up after TraceFunc. // FRETURN( TRUE ); break; } // default case } // switch on reason code return TRUE; } //*** DllMain() #endif // ! defined( NO_DLL_MAIN ) && ! defined( ENTRY_PREFIX ) && ! defined( DEBUG ) ////////////////////////////////////////////////////////////////////////////// //++ // // DllGetClassObject // // Description: // OLE calls this to get the class factory from the DLL. // // Arguments: // rclsidIn // - Class ID of the object that the class factory should create. // riidIn // - Interface of the class factory // ppvOut // - The interface pointer to the class factory. // // Return Values: // S_OK - Operation completed successfully. // E_POINTER - Required output parameter was specified as NULL. // CLASS_E_CLASSNOTAVAILABLE // - Class ID not supported by this DLL. // E_OUTOFMEMORY - Error allocating memory. // Other HRESULTs to indicate failure. // //-- ////////////////////////////////////////////////////////////////////////////// STDAPI DllGetClassObject( REFCLSID rclsidIn, REFIID riidIn, void ** ppvOut ) { TraceFunc( "rclsidIn, riidIn, ppvOut" ); LPCFACTORY lpClassFactory; HRESULT hr; int idx; if ( ppvOut == NULL ) { hr = E_POINTER; goto Cleanup; } // if: bad argument 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; } // if: class found idx++; } // while: finding class // Didn't find the class ID. 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; } // if: class not found Assert( g_DllClasses[ idx ].pfnCreateInstance != NULL ); lpClassFactory = new CFactory; if ( lpClassFactory == NULL ) { hr = E_OUTOFMEMORY; goto Cleanup; } // if: memory failure hr = THR( lpClassFactory->HrInit( g_DllClasses[ idx ].pfnCreateInstance ) ); if ( FAILED( hr ) ) { TraceDo( lpClassFactory->Release() ); goto Cleanup; } // if: initialization failed // 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 ); } //*** DllGetClassObject() ////////////////////////////////////////////////////////////////////////////// //++ // // DllRegisterServer // // Description: // OLE's register entry point. // // Argument: // None. // // Return Values: // S_OK - Operation completed successfully. // Other HRESULTs to indicate failure. // //-- ////////////////////////////////////////////////////////////////////////////// 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 )() ); } // if: register proxy/stub #endif // defined( ENTRY_PREFIX ) HRETURN( hr ); } //*** DllRegisterServer() ////////////////////////////////////////////////////////////////////////////// //++ // // DllUnregisterServer // // Description: // OLE's unregister entry point. // // Arguments: // None. // // Return Values: // S_OK - Operation completed successful. // Other HRESULTs to indicate failure. // //-- ////////////////////////////////////////////////////////////////////////////// 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 )() ); } // if: unregister proxy/stub #endif // defined( ENTRY_PREFIX ) HRETURN( hr ); } //*** DllUnregisterServer() ////////////////////////////////////////////////////////////////////////////// //++ // // DllCanUnloadNow // // Description: // OLE calls this entry point to see if it can unload the DLL. // // Arguments: // None. // // 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: any object or locks #if defined( ENTRY_PREFIX ) else { // // Check with the MIDL generated proxy/stubs. // hr = STHR( __rpc_macro_expand( ENTRY_PREFIX, DllCanUnloadNow )() ); } #endif HRETURN( hr ); } //*** DLlCanUnloadNow() ////////////////////////////////////////////////////////////////////////////// //++ // // HrCoCreateInternalInstance // // Description: // Mimic CoCreateInstance() except that it looks into the DLL table // to see if we can shortcut the CoCreate with a simple CreateInstance // call. // // Arguments: (matches CoCreateInstance) // rclsidIn - Class identifier (CLSID) of the object // pUnkOuterIn - Pointer to controlling IUnknown // dwClsContext - Context for running executable code // riidIn - Reference to the identifier of the interface // ppvOut - Address of output variable that receives // // Return Values: // S_OK - Success. // E_OUTOFMEMORY - Out of memory. // other HRESULT values // ////////////////////////////////////////////////////////////////////////////// HRESULT HrCoCreateInternalInstance( REFCLSID rclsidIn, LPUNKNOWN pUnkOuterIn, DWORD dwClsContextIn, REFIID riidIn, LPVOID * ppvOut ) { 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 && ( pUnkOuterIn == NULL ) // 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(); } // if: got object break; // bail loop } // if: class found } // for: finding class } // if: simple CoCreate() // // 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 ) ); } // if: class not found HRETURN( hr ); } //*** HrClusCoCreateInstance() // // TODO: gpease 27-NOV-1999 // While perusing 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(??). //