////////////////////////////////////////////////////////////////////////////// // // Copyright (c) 1999-2002 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. // - THREAD_OPTIMIZATIONS // If defined, disable the thread notification calls when a thread // starts up or goes away. // - IMPLEMENT_COM_SERVER_DLL // If defined, defines entry points necessary for COM servers. // ////////////////////////////////////////////////////////////////////////////// // // DLL Globals // HINSTANCE g_hInstance = NULL; LONG g_cObjects = 0; LONG g_cLock = 0; WCHAR 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, RTL_NUMBER_OF( g_rgTraceControlGuidList ), TRUE ); #else // ! DEBUG_SW_TRACING_ENABLED TraceInitializeProcess( TRUE ); #endif // DEBUG_SW_TRACING_ENABLED TraceFunc( "" ); TraceMessage( TEXT(__FILE__) , __LINE__ , __MODULE__ , mtfDLL , L"DLL: DLL_PROCESS_ATTACH - ThreadID = %#x" , GetCurrentThreadId() ); g_hInstance = hInstIn; #if defined( ENTRY_PREFIX ) hProxyDll = g_hInstance; #endif // ENTRY_PREFIX { BOOL fResult = GetModuleFileName( g_hInstance, g_szDllFilename, RTL_NUMBER_OF( g_szDllFilename ) ); if ( ! fResult ) { TW32MSG( GetLastError(), "GetModuleFileName()" ); } } // // 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 #if defined( DO_MODULE_INIT ) THR( HrLocalProcessInit() ); #endif // // 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 , L"DLL: DLL_PROCESS_DETACH - ThreadID = %#x [ g_cLock=%u, g_cObjects=%u ]" , GetCurrentThreadId() , g_cLock , g_cObjects ); #if defined( DO_MODULE_UNINIT ) THR( HrLocalProcessUninit() ); #endif // // 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, RTL_NUMBER_OF( 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 , L"Thread %#x has started." , GetCurrentThreadId() ); TraceFunc( "" ); TraceMessage( TEXT(__FILE__) , __LINE__ , __MODULE__ , mtfDLL , L"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 , L"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 , L"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 ) #ifdef IMPLEMENT_COM_SERVER_DLL // // The following functions are only required by COM server DLLs. // ////////////////////////////////////////////////////////////////////////////// //++ // // 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" ); CFactory * pClassFactory = NULL; HRESULT hr = CLASS_E_CLASSNOTAVAILABLE; const SPublicClassInfo * pClassInfo = g_DllPublicClasses; if ( ppvOut == NULL ) { hr = THR( E_POINTER ); goto Cleanup; } // if: bad argument *ppvOut = NULL; // // Search the public class table for a matching CLSID. // while ( pClassInfo->pClassID != NULL ) { if ( *( pClassInfo->pClassID ) == rclsidIn ) { TraceMessage( TEXT(__FILE__), __LINE__, __MODULE__, mtfFUNC, L"rclsidIn = %s", pClassInfo->pcszName ); hr = S_OK; break; } // if: class found ++pClassInfo; } // 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( pClassInfo->pfnCreateInstance != NULL ); hr = THR( CFactory::S_HrCreateInstance( pClassInfo->pfnCreateInstance, &pClassFactory ) ); if ( FAILED( hr ) ) { goto Cleanup; } // if: initialization failed // Can't safe type. hr = pClassFactory->QueryInterface( riidIn, ppvOut ); Cleanup: if ( pClassFactory != NULL ) { pClassFactory->Release(); } 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 ) { TraceFunc( "" ); HRESULT hr; hr = THR( HrRegisterDll() ); #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( HrUnregisterDll() ); #if defined( ENTRY_PREFIX ) if ( SUCCEEDED( hr ) ) { hr = THR( __rpc_macro_expand( ENTRY_PREFIX, DllUnregisterServer )() ); } // if: unregister proxy/stub #endif // defined( ENTRY_PREFIX ) if ( hr == HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ) ) { hr = S_OK; } 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 != 0 ) || ( g_cObjects != 0 ) ) { 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 = S_OK; MULTI_QI mqi; mqi.hr = S_OK; mqi.pItf = NULL; mqi.pIID = &riidIn; hr = HrCoCreateInternalInstanceEx( rclsidIn, pUnkOuterIn, dwClsContextIn, NULL, 1, &mqi ); *ppvOut = mqi.pItf; HRETURN( hr ); } //*** HrCoCreateInternalInstance ////////////////////////////////////////////////////////////////////////////// //++ // // HrCoCreateInternalInstanceEx // // Description: // Mimic CoCreateInstanceEx() except that it looks into the DLL table // to see if we can shortcut the CoCreate with a simple CreateInstance // call. // // Arguments: (matches CoCreateInstanceEx) // rclsidIn - Class identifier (CLSID) of the object // pUnkOuterIn - Pointer to controlling IUnknown // dwClsContext - Context for running executable code // pServerInfoIn - Object location; can be NULL // cMultiQIsIn - Number of MULTI_QI structs in prgmqiInOut array // prgmqiInOut - Array to hold queried interfaces on object // // Return Values: // S_OK // Success. // // E_OUTOFMEMORY // Out of memory. // // E_NOINTERFACE // Object created, but supports no requested interfaces. // // CO_S_NOTALLINTERFACES // Object created and supports some, but not all, requested // interfaces. // // CLASS_E_CLASSNOTAVAILABLE // Unknown class ID. // // Other HRESULTs. // ////////////////////////////////////////////////////////////////////////////// HRESULT HrCoCreateInternalInstanceEx( REFCLSID rclsidIn, LPUNKNOWN pUnkOuterIn, DWORD dwClsContextIn, COSERVERINFO * pServerInfoIn, ULONG cMultiQIsIn, MULTI_QI * prgmqiInOut ) { TraceFunc( "" ); Assert( ( prgmqiInOut != NULL ) || ( cMultiQIsIn == 0 ) ); HRESULT hr = CLASS_E_CLASSNOTAVAILABLE; // // Limit simple CoCreate() only works to INPROC and non-aggregatable objects. // if ( ( dwClsContextIn & ( CLSCTX_INPROC_HANDLER | CLSCTX_INPROC_SERVER ) ) // inproc only && ( pServerInfoIn == NULL ) // local machine only && ( pUnkOuterIn == NULL ) // no aggregation ) { PFN_FACTORY_METHOD pfnCreateInstance = NULL; const SPrivateClassInfo * pPrivateInfo = g_PrivateClasses; const SPublicClassInfo * pPublicInfo = g_DllPublicClasses; // // Look up the class ID in the private class table first. // while ( ( pfnCreateInstance == NULL ) && ( pPrivateInfo->pClassID != NULL ) ) { if ( *( pPrivateInfo->pClassID ) == rclsidIn ) { pfnCreateInstance = pPrivateInfo->pfnCreateInstance; } ++pPrivateInfo; } // // If that didn't find it, try the public table. // while ( ( pfnCreateInstance == NULL ) && ( pPublicInfo->pClassID != NULL ) ) { if ( *( pPublicInfo->pClassID ) == rclsidIn ) { pfnCreateInstance = pPublicInfo->pfnCreateInstance; } ++pPublicInfo; } // // If either found a match, use its factory method to create an object. // if ( pfnCreateInstance != NULL ) { IUnknown * punkInstance = NULL; ULONG idxInterface = 0; BOOL fQISucceeded = FALSE; BOOL fQIFailed = FALSE; hr = THR( ( *pfnCreateInstance )( &punkInstance ) ); if ( FAILED( hr ) ) { goto Cleanup; } // // Query for each requested interface. // while ( idxInterface < cMultiQIsIn ) { MULTI_QI * pmqi = prgmqiInOut + idxInterface; // // No THR around the following QI because client might expect some failures. // pmqi->hr = punkInstance->QueryInterface( *( pmqi->pIID ), reinterpret_cast< void** >( &( pmqi->pItf ) ) ); if ( SUCCEEDED( pmqi->hr ) ) { fQISucceeded = TRUE; } else { fQIFailed = TRUE; } idxInterface += 1; } // for each requested interface // // Mimic return values from CoCreateInstanceEx. // if ( fQIFailed ) { if ( fQISucceeded ) { // At least one QI succeeded, and at least one failed. hr = CO_S_NOTALLINTERFACES; } else { // At least one QI failed, and none succeeded. hr = E_NOINTERFACE; } } // Otherwise, leave hr as is. punkInstance->Release(); } // if: creating internal 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 = STHR( CoCreateInstanceEx( rclsidIn, pUnkOuterIn, dwClsContextIn, pServerInfoIn, cMultiQIsIn, prgmqiInOut ) ); } // if: class not found Cleanup: HRETURN( hr ); } //*** HrCoCreateInternalInstanceEx // // 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(??). // #endif // IMPLEMENT_COM_SERVER_DLL