/*************************************************************************** * * Copyright (C) 2001-2002 Microsoft Corporation. All Rights Reserved. * * File: dp8simdllmain.cpp * * Content: DP8SIM DLL entry points. * * History: * Date By Reason * ======== ======== ========= * 04/23/01 VanceO Created. * ***************************************************************************/ #include "dp8simi.h" //============================================================================= // External globals //============================================================================= volatile LONG g_lOutstandingInterfaceCount = 0; // number of outstanding interfaces HINSTANCE g_hDLLInstance = NULL; // handle to this DLL instance DNCRITICAL_SECTION g_csGlobalsLock; // lock protecting all of the following globals CBilink g_blDP8SimSPObjs; // bilink of all the DP8SimSP interface objects CBilink g_blDP8SimControlObjs; // bilink of all the DP8SimControl interface objects UINT g_uiRandShr3 = 0; // global holding value for Shr3 random number sequence generator UINT g_uiRandCong = 0; // global holding value for congruential random number sequence generator //============================================================================= // Supported SPs table //============================================================================= typedef struct _SUPPORTEDSP { const CLSID * pclsidFakeSP; // pointer to class ID for fake SP const CLSID * pclsidRealSP; // pointer to class ID for real SP const WCHAR * pwszVerIndProgID; // version independent prog ID for fake SP COM object, must match the sub key for pwszServiceProviderKey const WCHAR * pwszProgID; // prog ID for fake SP COM object const WCHAR * pwszDesc; // description for fake SP COM object const WCHAR * pwszServiceProviderKey; // service provider key string, sub key must match pwszVerIndProgID UINT uiFriendlyNameResourceID; // ID of fake SP's name string resource } SUPPORTEDSP; const SUPPORTEDSP c_aSupportedSPs[] = { { (&CLSID_NETWORKSIMULATOR_DP8SP_TCPIP), (&CLSID_DP8SP_TCPIP), L"DP8SimTCPIP", L"DP8SimTCPIP.1", L"DirectPlay8 Network Simulator TCP/IP Service Provider", DPN_REG_LOCAL_SP_SUBKEY L"\\DP8SimTCPIP", IDS_FRIENDLYNAME_TCPIP, }, }; #define NUM_SUPPORTED_SPS (sizeof(c_aSupportedSPs) / sizeof(c_aSupportedSPs[0])) //============================================================================= // Defines //============================================================================= #define MAX_RESOURCE_STRING_LENGTH _MAX_PATH //============================================================================= // Macros //============================================================================= // 3-shift register generator // // Original comments: // SHR3 is a 3-shift-register generator with period 2^32-1. It uses // y(n)=y(n-1)(I+L^17)(I+R^13)(I+L^5), with the y's viewed as binary vectors, // L the 32x32 binary matrix that shifts a vector left 1, and R its transpose. // SHR3 seems to pass all except those related to the binary rank test, since // 32 successive values, as binary vectors, must be linearly independent, // while 32 successive truly random 32-bit integers, viewed as binary vectors, // will be linearly independent only about 29% of the time. #define RANDALG_SHR3() (g_uiRandShr3 = g_uiRandShr3 ^ (g_uiRandShr3 << 17), g_uiRandShr3 = g_uiRandShr3 ^ (g_uiRandShr3 >> 13), g_uiRandShr3 = g_uiRandShr3 ^ (g_uiRandShr3 << 5)) // Congruential generator // // Original comments: // CONG is a congruential generator with the widely used 69069 multiplier: // x(n)=69069x(n-1)+1234567. It has period 2^32. The leading half of its 32 // bits seem to pass tests, but bits in the last half are too regular. #define RANDALG_CONG() (g_uiRandCong = 69069UL * g_uiRandCong + 1234567UL) //============================================================================= // Local prototypes //============================================================================= BOOL InitializeProcessGlobals(void); void CleanupProcessGlobals(void); HRESULT LoadAndAllocString(UINT uiResourceID, WCHAR ** pwszString); #undef DPF_MODNAME #define DPF_MODNAME "DllMain" //============================================================================= // DllMain //----------------------------------------------------------------------------- // // Description: DLL entry point. // // Arguments: // HINSTANCE hDllInst - Handle to this DLL module. // DWORD dwReason - Reason for calling this function. // LPVOID lpvReserved - Reserved. // // Returns: TRUE if all goes well, FALSE otherwise. //============================================================================= BOOL WINAPI DllMain(HINSTANCE hDllInst, DWORD dwReason, LPVOID lpvReserved) { switch (dwReason) { case DLL_PROCESS_ATTACH: { DPFX(DPFPREP, 2, "====> ENTER: DLLMAIN(%p): Process Attach: %08lx, tid=%08lx", DllMain, GetCurrentProcessId(), GetCurrentThreadId()); DNASSERT(g_hDLLInstance == NULL); g_hDLLInstance = hDllInst; // // Attempt to initialize the OS abstraction layer. // if (! DNOSIndirectionInit(0)) { DPFX(DPFPREP, 0, "Failed to initialize OS indirection layer!"); return FALSE; } // // Attempt to initialize COM. // if (FAILED(COM_Init())) { DPFX(DPFPREP, 0, "Failed to initialize COM indirection layer!"); DNOSIndirectionDeinit(); return FALSE; } // // Attempt to initialize process-global items. // if (! InitializeProcessGlobals()) { DPFX(DPFPREP, 0, "Failed to initialize globals!"); COM_Free(); DNOSIndirectionDeinit(); return FALSE; } // // We don't need thread attach/detach messages. // DisableThreadLibraryCalls(hDllInst); return TRUE; break; } case DLL_PROCESS_DETACH: { DPFX(DPFPREP, 2, "====> EXIT: DLLMAIN(%p): Process Detach %08lx, tid=%08lx", DllMain, GetCurrentProcessId(), GetCurrentThreadId()); DNASSERT(g_hDLLInstance != NULL); g_hDLLInstance = NULL; CleanupProcessGlobals(); COM_Free(); DNOSIndirectionDeinit(); return TRUE; break; } default: { DNASSERT(FALSE); break; } } return FALSE; } // DllMain #undef DPF_MODNAME #define DPF_MODNAME "DllRegisterServer" //============================================================================= // DllRegisterServer //----------------------------------------------------------------------------- // // Description: Registers the DP8Sim COM object. // // Arguments: None. // // Returns: HRESULT // S_OK - Successfully registered DP8Sim. // E_FAIL - Failed registering DP8Sim. //============================================================================= HRESULT WINAPI DllRegisterServer(void) { HRESULT hr; CRegistry RegObject; DWORD dwLength; DWORD dwSimulatedSP; char szLocalPath[_MAX_PATH + 1]; WCHAR wszLocalPath[_MAX_PATH + 1]; WCHAR * pwszFriendlyName = NULL; // // Retrieve the location of this DLL. // dwLength = GetModuleFileNameA(g_hDLLInstance, szLocalPath, _MAX_PATH); if (dwLength == 0) { DPFX(DPFPREP, 0, "Couldn't read local path!"); hr = E_FAIL; goto Failure; } // // Include NULL termination. // szLocalPath[dwLength] = '\0'; dwLength++; // // Convert it to Unicode. // hr = STR_AnsiToWide(szLocalPath, dwLength, wszLocalPath, &dwLength); if (hr != S_OK) { DPFX(DPFPREP, 0, "Could not convert ANSI path to Unicode!"); goto Failure; } // // Register the control COM object CLSID. // if (! RegObject.Register(L"DP8SimControl.1", L"DirectPlay8 Network Simulator Control Object", wszLocalPath, &CLSID_DP8SimControl, L"DP8SimControl")) { DPFX(DPFPREP, 0, "Could not register DP8SimControl object!"); hr = E_FAIL; goto Failure; } // // Register all of the simulated SPs. // for(dwSimulatedSP = 0; dwSimulatedSP < NUM_SUPPORTED_SPS; dwSimulatedSP++) { if (! RegObject.Register(c_aSupportedSPs[dwSimulatedSP].pwszProgID, c_aSupportedSPs[dwSimulatedSP].pwszDesc, wszLocalPath, c_aSupportedSPs[dwSimulatedSP].pclsidFakeSP, c_aSupportedSPs[dwSimulatedSP].pwszVerIndProgID)) { DPFX(DPFPREP, 0, "Could not register simulated SP %u object!", dwSimulatedSP); hr = E_FAIL; goto Failure; } hr = LoadAndAllocString(c_aSupportedSPs[dwSimulatedSP].uiFriendlyNameResourceID, &pwszFriendlyName); if (FAILED(hr)) { DPFX(DPFPREP, 0, "Could not load friendly name string (err = 0x%lx)!", hr); goto Failure; } if (! RegObject.Open(HKEY_LOCAL_MACHINE, c_aSupportedSPs[dwSimulatedSP].pwszServiceProviderKey, FALSE, TRUE)) { DPFX(DPFPREP, 0, "Could not open service provider key!"); hr = E_FAIL; goto Failure; } RegObject.WriteString(DPN_REG_KEYNAME_FRIENDLY_NAME, pwszFriendlyName); RegObject.WriteGUID(DPN_REG_KEYNAME_GUID, *(c_aSupportedSPs[dwSimulatedSP].pclsidFakeSP)); RegObject.Close(); DNFree(pwszFriendlyName); pwszFriendlyName = NULL; } hr = S_OK; Exit: return hr; Failure: if (pwszFriendlyName != NULL) { DNFree(pwszFriendlyName); pwszFriendlyName = NULL; } goto Exit; } // DllRegisterServer #undef DPF_MODNAME #define DPF_MODNAME "DllUnregisterServer" //============================================================================= // DllUnregisterServer //----------------------------------------------------------------------------- // // Description: Unregisters the DP8Sim COM object. // // Arguments: None. // // Returns: HRESULT // S_OK - Successfully unregistered DP8Sim. // E_FAIL - Failed unregistering DP8Sim. //============================================================================= STDAPI DllUnregisterServer(void) { HRESULT hr; CRegistry RegObject; DWORD dwSimulatedSP; // // Unregister the control class. // if (! RegObject.UnRegister(&CLSID_DP8SimControl)) { DPFX(DPFPREP, 0, "Failed to unregister DP8Sim control object!"); hr = E_FAIL; goto Failure; } // // Unregister all of the simulated SPs. // if (! RegObject.Open(HKEY_LOCAL_MACHINE, DPN_REG_LOCAL_SP_SUBKEY, FALSE, FALSE, FALSE)) { DPFX(DPFPREP, 0, "Could not open HKEY_LOCAL_MACHINE!"); hr = E_FAIL; goto Failure; } for(dwSimulatedSP = 0; dwSimulatedSP < NUM_SUPPORTED_SPS; dwSimulatedSP++) { if (! RegObject.UnRegister(c_aSupportedSPs[dwSimulatedSP].pclsidFakeSP)) { DPFX(DPFPREP, 0, "Could not unregister simulated SP %u object!", dwSimulatedSP); hr = E_FAIL; goto Failure; } if (! RegObject.DeleteSubKey(c_aSupportedSPs[dwSimulatedSP].pwszVerIndProgID)) { DPFX(DPFPREP, 0, "Could not delete simulated SP %u's key!", dwSimulatedSP); hr = E_FAIL; goto Failure; } } RegObject.Close(); hr = S_OK; Exit: return hr; Failure: // // Rely on RegObject destructure to close registry key. // goto Exit; } // DllUnregisterServer #undef DPF_MODNAME #define DPF_MODNAME "InitializeProcessGlobals" //============================================================================= // InitializeProcessGlobals //----------------------------------------------------------------------------- // // Description: Initialize global items needed for the DLL to operate. // // Arguments: None. // // Returns: TRUE if successful, FALSE if an error occurred. //============================================================================= BOOL InitializeProcessGlobals(void) { BOOL fReturn = TRUE; BOOL fInittedGlobalLock = FALSE; if (! DNInitializeCriticalSection(&g_csGlobalsLock)) { DPFX(DPFPREP, 0, "Failed to initialize global lock!"); goto Failure; } fInittedGlobalLock = TRUE; // // Don't allow critical section reentry. // DebugSetCriticalSectionRecursionCount(&g_csGlobalsLock, 0); if (!InitializePools()) { DPFX(DPFPREP, 0, "Failed initializing pools!"); goto Failure; } g_blDP8SimSPObjs.Initialize(); g_blDP8SimControlObjs.Initialize(); // // Seed the random number generator with the current time. // InitializeGlobalRand(GETTIMESTAMP()); Exit: return fReturn; Failure: if (fInittedGlobalLock) { DNDeleteCriticalSection(&g_csGlobalsLock); } fReturn = FALSE; goto Exit; } // InitializeProcessGlobals #undef DPF_MODNAME #define DPF_MODNAME "CleanupProcessGlobals" //============================================================================= // CleanupProcessGlobals //----------------------------------------------------------------------------- // // Description: Releases global items used by DLL. // // Arguments: None. // // Returns: None. //============================================================================= void CleanupProcessGlobals(void) { CBilink * pBilink; CDP8SimSP * pDP8SimSP; CDP8SimControl * pDP8SimControl; if (! g_blDP8SimSPObjs.IsEmpty()) { DNASSERT(! "DP8Sim DLL unloading without all SP objects having been released!"); // // Force close all the objects still outstanding. // pBilink = g_blDP8SimSPObjs.GetNext(); while (pBilink != &g_blDP8SimSPObjs) { pDP8SimSP = DP8SIMSP_FROM_BILINK(pBilink); pBilink = pBilink->GetNext(); DPFX(DPFPREP, 0, "Forcefully releasing SP object 0x%p!", pDP8SimSP); pDP8SimSP->Close(); // ignore error // // Forcefully remove it from the list and delete it instead of // using pDP8SimSP->Release(). // pDP8SimSP->m_blList.RemoveFromList(); pDP8SimSP->UninitializeObject(); delete pDP8SimSP; } } if (! g_blDP8SimControlObjs.IsEmpty()) { DNASSERT(! "DP8Sim DLL unloading without all Control objects having been released!"); // // Force close all the objects still outstanding. // pBilink = g_blDP8SimControlObjs.GetNext(); while (pBilink != &g_blDP8SimControlObjs) { pDP8SimControl = DP8SIMCONTROL_FROM_BILINK(pBilink); pBilink = pBilink->GetNext(); DPFX(DPFPREP, 0, "Forcefully releasing Control object 0x%p!", pDP8SimControl); pDP8SimControl->Close(0); // ignore error // // Forcefully remove it from the list and delete it instead of // using pDP8SimControl->Release(). // pDP8SimControl->m_blList.RemoveFromList(); pDP8SimControl->UninitializeObject(); delete pDP8SimControl; } } CleanupPools(); DNDeleteCriticalSection(&g_csGlobalsLock); } // CleanupProcessGlobals #undef DPF_MODNAME #define DPF_MODNAME "LoadAndAllocString" //============================================================================= // LoadAndAllocString //----------------------------------------------------------------------------- // // Description: DNMallocs a wide character string from the given resource ID. // // Arguments: // UINT uiResourceID - Resource ID to load. // WCHAR ** pwszString - Place to store pointer to allocated string. // // Returns: HRESULT //============================================================================= HRESULT LoadAndAllocString(UINT uiResourceID, WCHAR ** pwszString) { HRESULT hr = DPN_OK; int iLength; if (DNGetOSType() == VER_PLATFORM_WIN32_NT) { WCHAR wszTmpBuffer[MAX_RESOURCE_STRING_LENGTH]; iLength = LoadStringW(g_hDLLInstance, uiResourceID, wszTmpBuffer, MAX_RESOURCE_STRING_LENGTH ); if (iLength == 0) { hr = GetLastError(); DPFX(DPFPREP, 0, "Unable to load resource ID %d error 0x%x", uiResourceID, hr); (*pwszString) = NULL; goto Exit; } (*pwszString) = (WCHAR*) DNMalloc((iLength + 1) * sizeof(WCHAR)); if ((*pwszString) == NULL) { DPFX(DPFPREP, 0, "Memory allocation failure!"); hr = DPNERR_OUTOFMEMORY; goto Exit; } wcscpy((*pwszString), wszTmpBuffer); } else { char szTmpBuffer[MAX_RESOURCE_STRING_LENGTH]; iLength = LoadStringA(g_hDLLInstance, uiResourceID, szTmpBuffer, MAX_RESOURCE_STRING_LENGTH ); if (iLength == 0) { hr = GetLastError(); DPFX(DPFPREP, 0, "Unable to load resource ID %u (err =0x%lx)!", uiResourceID, hr); (*pwszString) = NULL; goto Exit; } (*pwszString) = (WCHAR*) DNMalloc((iLength + 1) * sizeof(WCHAR)); if ((*pwszString) == NULL) { DPFX(DPFPREP, 0, "Memory allocation failure!"); hr = DPNERR_OUTOFMEMORY; goto Exit; } hr = STR_jkAnsiToWide((*pwszString), szTmpBuffer, (iLength + 1)); if (hr == DPN_OK) { hr = GetLastError(); DPFX(DPFPREP, 0, "Unable to convert from ANSI to Unicode (err =0x%lx)!", hr); goto Exit; } } Exit: return hr; } // LoadAndAllocString #undef DPF_MODNAME #define DPF_MODNAME "InitializeGlobalRand" //============================================================================= // InitializeGlobalRand //----------------------------------------------------------------------------- // // Description: Initializes the global psuedo-random number generator, using // the given seed value. // // Based off algorithms posted to usenet by George Marsaglia. // // Arguments: // DWORD dwSeed - Seed to use. // // Returns: None. //============================================================================= void InitializeGlobalRand(const DWORD dwSeed) { // // We don't need to hold a lock, since this should only be done once, // during initialization time. // g_uiRandShr3 = dwSeed; g_uiRandCong = dwSeed; } // InitializeGlobalRand #undef DPF_MODNAME #define DPF_MODNAME "GetGlobalRand" //============================================================================= // GetGlobalRand //----------------------------------------------------------------------------- // // Description: Generates a pseudo-random positive double between 0.0 and // 1.0, inclusive. // // Based off algorithms posted to usenet by George Marsaglia. // // Arguments: None. // // Returns: Pseudo-random number. //============================================================================= double GetGlobalRand(void) { double dResult; DNEnterCriticalSection(&g_csGlobalsLock); dResult = (RANDALG_CONG() + RANDALG_SHR3()) * 2.328306e-10f; DNLeaveCriticalSection(&g_csGlobalsLock); return dResult; } // GetGlobalRand #undef DPF_MODNAME #define DPF_MODNAME "DoCreateInstance" //============================================================================= // DoCreateInstance //----------------------------------------------------------------------------- // // Description: Creates an instance of an interface. Required by the general // purpose class factory functions. // // Arguments: // LPCLASSFACTORY This - Pointer to class factory. // LPUNKNOWN pUnkOuter - Pointer to unknown interface. // REFCLSID rclsid - Reference of GUID of desired interface. // REFIID riid - Reference to another GUID? // LPVOID * ppvObj - Pointer to pointer to interface. // // Returns: HRESULT //============================================================================= HRESULT DoCreateInstance(LPCLASSFACTORY This, LPUNKNOWN pUnkOuter, REFCLSID rclsid, REFIID riid, LPVOID * ppvObj) { HRESULT hr; CDP8SimSP * pDP8SimSP = NULL; CDP8SimControl * pDP8SimControl = NULL; DWORD dwSimulatedSP; DNASSERT(ppvObj != NULL); // // See if it's the control object. // if (IsEqualCLSID(rclsid, CLSID_DP8SimControl)) { // // Create the object instance. // pDP8SimControl = new CDP8SimControl; if (pDP8SimControl == NULL) { hr = E_OUTOFMEMORY; goto Failure; } // // Initialize the base object (which might fail). // hr = pDP8SimControl->InitializeObject(); if (hr != S_OK) { delete pDP8SimControl; pDP8SimControl = NULL; goto Failure; } // // Add it to the global list. // DNEnterCriticalSection(&g_csGlobalsLock); pDP8SimControl->m_blList.InsertBefore(&g_blDP8SimControlObjs); g_lOutstandingInterfaceCount++; // update count so DllCanUnloadNow works correctly DNLeaveCriticalSection(&g_csGlobalsLock); // // Get the right interface for the caller and bump the refcount. // hr = pDP8SimControl->QueryInterface(riid, ppvObj); if (hr != S_OK) { goto Failure; } } else { // // Look up the real SP we're replacing. // for(dwSimulatedSP = 0; dwSimulatedSP < NUM_SUPPORTED_SPS; dwSimulatedSP++) { if (IsEqualCLSID(rclsid, *(c_aSupportedSPs[dwSimulatedSP].pclsidFakeSP))) { break; } } // // If we didn't find it // if (dwSimulatedSP >= NUM_SUPPORTED_SPS) { DPFX(DPFPREP, 0, "Unrecognized service provider class ID!"); hr = E_UNEXPECTED; goto Failure; } // // Create the object instance. // pDP8SimSP = new CDP8SimSP(c_aSupportedSPs[dwSimulatedSP].pclsidFakeSP, c_aSupportedSPs[dwSimulatedSP].pclsidRealSP); if (pDP8SimSP == NULL) { hr = E_OUTOFMEMORY; goto Failure; } // // Initialize the base object (which might fail). // hr = pDP8SimSP->InitializeObject(); if (hr != S_OK) { DPFX(DPFPREP, 0, "Couldn't initialize object!"); delete pDP8SimSP; pDP8SimSP = NULL; goto Failure; } // // Add it to the global list. // DNEnterCriticalSection(&g_csGlobalsLock); pDP8SimSP->m_blList.InsertBefore(&g_blDP8SimSPObjs); g_lOutstandingInterfaceCount++; // update count so DllCanUnloadNow works correctly DNLeaveCriticalSection(&g_csGlobalsLock); // // Get the right interface for the caller and bump the refcount. // hr = pDP8SimSP->QueryInterface(riid, ppvObj); if (hr != S_OK) { goto Failure; } } Exit: // // Release the local reference to the objec(s)t. If this function was // successful, there's still a reference in ppvObj. // if (pDP8SimSP != NULL) { pDP8SimSP->Release(); pDP8SimSP = NULL; } if (pDP8SimControl != NULL) { pDP8SimControl->Release(); pDP8SimControl = NULL; } return hr; Failure: // // Make sure we don't hand back a pointer. // (*ppvObj) = NULL; goto Exit; } // DoCreateInstance #undef DPF_MODNAME #define DPF_MODNAME "IsClassImplemented" //============================================================================= // IsClassImplemented //----------------------------------------------------------------------------- // // Description: Determine if a class is implemented in this DLL. Required by // the general purpose class factory functions. // // Arguments: // REFCLSID rclsid - Reference to class GUID. // // Returns: BOOL // TRUE - This DLL implements the class. // FALSE - This DLL doesn't implement the class. //============================================================================= BOOL IsClassImplemented(REFCLSID rclsid) { DWORD dwSimulatedSP; if (IsEqualCLSID(rclsid, CLSID_DP8SimControl)) { return TRUE; } // // Check if this is a valid simulated SP. // for(dwSimulatedSP = 0; dwSimulatedSP < NUM_SUPPORTED_SPS; dwSimulatedSP++) { if (IsEqualCLSID(rclsid, *(c_aSupportedSPs[dwSimulatedSP].pclsidFakeSP))) { return TRUE; } } return FALSE; } // IsClassImplemented