/*++ Copyright (c) 2000 Microsoft Corporation Module Name: DirectPlayEnumOrder.cpp Abstract: Certain applications (Midtown Madness) expects the DPLAY providers to enumerate in a specific order. History: 04/25/2000 robkenny --*/ #include "precomp.h" #include "CharVector.h" #include IMPLEMENT_SHIM_BEGIN(DirectPlayEnumOrder) #include "ShimHookMacro.h" APIHOOK_ENUM_BEGIN APIHOOK_ENUM_ENTRY_DIRECTX_COMSERVER() APIHOOK_ENUM_END IMPLEMENT_DIRECTX_COMSERVER_HOOKS() // A class that makes it easy to store DPlay::EnumConnections information. class DPlayConnectionsInfo { public: BOOL m_beenUsed; GUID m_lpguidSP; LPVOID m_lpConnection; DWORD m_dwConnectionSize; DPNAME m_lpName; DWORD m_dwFlags; LPVOID m_lpContext; // Construct our object, saveing all these values. DPlayConnectionsInfo( LPCGUID lpguidSP, LPVOID lpConnection, DWORD dwConnectionSize, LPCDPNAME lpName, DWORD dwFlags, LPVOID lpContext ) { m_beenUsed = FALSE; m_lpguidSP = *lpguidSP; m_lpConnection = malloc(dwConnectionSize); memcpy(m_lpConnection, lpConnection, dwConnectionSize); m_dwConnectionSize = dwConnectionSize; m_lpName = *lpName; m_lpName.lpszShortNameA = StringDuplicateA(lpName->lpszShortNameA); m_dwFlags = dwFlags; m_lpContext = lpContext; } // Free our allocated space, and erase values. void Erase() { free(m_lpConnection); free(m_lpName.lpszShortNameA); m_lpConnection = NULL; m_dwConnectionSize = 0; m_lpName.lpszShortNameA = NULL; m_dwFlags = 0; m_lpContext = 0; } // Do we match this GUID? BOOL operator == (const GUID & guidSP) { return IsEqualGUID(guidSP, m_lpguidSP); } // Call the callback routine with this saved information void CallEnumRoutine(LPDPENUMCONNECTIONSCALLBACK lpEnumCallback) { lpEnumCallback( &m_lpguidSP, m_lpConnection, m_dwConnectionSize, &m_lpName, m_dwFlags, m_lpContext ); m_beenUsed = TRUE; } }; // A list of DPlay connections class DPlayConnectionsInfoVector : public VectorT { static DPlayConnectionsInfoVector * g_DPlayConnectionsInfoVector; public: // Get us a pointer to the one and only DPlayConnectionsInfoVector static DPlayConnectionsInfoVector * GetVector() { if (g_DPlayConnectionsInfoVector == NULL) g_DPlayConnectionsInfoVector = new DPlayConnectionsInfoVector; return g_DPlayConnectionsInfoVector; }; // Deconstruct the elements, then Erase the list void FreeAndErase() { for (int i = 0; i < Size(); ++i) { DPlayConnectionsInfo & deleteMe = Get(i); deleteMe.Erase(); } Erase(); } // Find an entry that matches this GUID DPlayConnectionsInfo * Find(const GUID & guidSP) { const int size = Size(); #if DBG DPFN( eDbgLevelInfo, "Find GUID(%08x-%08x-%08x-%08x) Size(%d).", guidSP.Data1, guidSP.Data2, guidSP.Data3, guidSP.Data4, size); #endif for (int i = 0; i < size; ++i) { DPlayConnectionsInfo & dpci = Get(i); #if DBG DPFN( eDbgLevelInfo, " Compare[%02d] = GUID(%08x-%08x-%08x-%08x) (%s).", i, dpci.m_lpguidSP.Data1, dpci.m_lpguidSP.Data2, dpci.m_lpguidSP.Data3, dpci.m_lpguidSP.Data4, dpci.m_lpName.lpszShortNameA); #endif if (dpci == guidSP) { #if DBG DPFN( eDbgLevelInfo, "FOUND(%s).", dpci.m_lpName.lpszShortNameA); #endif return &dpci; } } #if DBG DPFN( eDbgLevelInfo, "NOT FOUND."); #endif return NULL; } // Lookup the GUID and if found, call the callback routine. void CallEnumRoutine(const GUID & guidSP, LPDPENUMCONNECTIONSCALLBACK lpEnumCallback) { #if DBG DPFN( eDbgLevelInfo, "CallEnumRoutine(%08x) Find GUID(%08x-%08x-%08x-%08x).", lpEnumCallback, guidSP.Data1, guidSP.Data2, guidSP.Data3, guidSP.Data4); #endif DPlayConnectionsInfo * dpci = DPlayConnectionsInfoVector::GetVector()->Find(guidSP); if (dpci) { dpci->CallEnumRoutine(lpEnumCallback); } } }; // The global list of DPlay Connections DPlayConnectionsInfoVector * DPlayConnectionsInfoVector::g_DPlayConnectionsInfoVector = NULL; /*++ Our private callback for IDirectPlay4::EnumConnections. We simply save all the connections in our private list for later use. --*/ BOOL FAR PASCAL EnumConnectionsCallback( LPCGUID lpguidSP, LPVOID lpConnection, DWORD dwConnectionSize, LPCDPNAME lpName, DWORD dwFlags, LPVOID lpContext ) { // Only add it to the list if it is not already there // App calls EnumConnections from inside Enum callback routine. if (!DPlayConnectionsInfoVector::GetVector()->Find(*lpguidSP)) { #if DBG LOGN( eDbgLevelError, "EnumConnectionsCallback Add(%d) (%s).", DPlayConnectionsInfoVector::GetVector()->Size(), lpName->lpszShortName ); #endif // Store the info for later DPlayConnectionsInfo dpci(lpguidSP, lpConnection, dwConnectionSize, lpName, dwFlags, lpContext); DPlayConnectionsInfoVector::GetVector()->Append(dpci); } #if DBG else { DPFN( eDbgLevelInfo, "EnumConnectionsCallback Already in the list(%s).", lpName->lpszShortName ); } #endif return TRUE; } /*++ Win9x Direct play enumerates hosts in this order: DPSPGUID_IPX, DPSPGUID_TCPIP, DPSPGUID_MODEM, DPSPGUID_SERIAL, IXP, TCP, Modem, Serial. Have EnumConnections call our callback routine to gather the host list, sort it, then call the app's callback routine. --*/ HRESULT COMHOOK(IDirectPlay4A, EnumConnections)( PVOID pThis, LPCGUID lpguidApplication, LPDPENUMCONNECTIONSCALLBACK lpEnumCallback, LPVOID lpContext, DWORD dwFlags ) { #if DBG DPFN( eDbgLevelInfo, "======================================"); DPFN( eDbgLevelInfo, "COMHOOK IDirectPlay4A EnumConnections" ); #endif HRESULT hResult = DPERR_CONNECTIONLOST; typedef HRESULT (*_pfn_IDirectPlay4_EnumConnections)( PVOID pThis, LPCGUID lpguidApplication, LPDPENUMCONNECTIONSCALLBACK lpEnumCallback, LPVOID lpContext, DWORD dwFlags); _pfn_IDirectPlay4A_EnumConnections EnumConnections = ORIGINAL_COM( IDirectPlay4A, EnumConnections, pThis); if (EnumConnections) { static bool alreadyHere = false; if (!alreadyHere) { alreadyHere = true; #if DBG LOGN( eDbgLevelError, "EnumConnections(%08x)\n", EnumConnections ); #endif // Enumerate connections to our own routine. hResult = EnumConnections(pThis, lpguidApplication, EnumConnectionsCallback, lpContext, dwFlags); #if DBG LOGN( eDbgLevelError, "Done EnumConnections Start calling app Size(%d).", DPlayConnectionsInfoVector::GetVector()->Size()); #endif // Call the application's callback routine with the GUID in the order it expects if (hResult == DP_OK) { DPlayConnectionsInfoVector::GetVector()->CallEnumRoutine(DPSPGUID_IPX, lpEnumCallback); DPlayConnectionsInfoVector::GetVector()->CallEnumRoutine(DPSPGUID_TCPIP, lpEnumCallback); DPlayConnectionsInfoVector::GetVector()->CallEnumRoutine(DPSPGUID_MODEM, lpEnumCallback); DPlayConnectionsInfoVector::GetVector()->CallEnumRoutine(DPSPGUID_SERIAL, lpEnumCallback); // Now loop over the list and enum any remaining providers for (int i = 0; i < DPlayConnectionsInfoVector::GetVector()->Size(); ++i) { DPlayConnectionsInfo & dpci = DPlayConnectionsInfoVector::GetVector()->Get(i); if (!dpci.m_beenUsed) { dpci.CallEnumRoutine(lpEnumCallback); dpci.m_beenUsed = TRUE; } } } alreadyHere = false; } #if DBG else { DPFN( eDbgLevelInfo, "EnumConnections Recursive." ); } #endif } // All done with the list, clean up. DPlayConnectionsInfoVector::GetVector()->FreeAndErase(); return hResult; } /*++ Do the same thing for DirectPlay3 --*/ HRESULT COMHOOK(IDirectPlay3A, EnumConnections)( PVOID pThis, LPCGUID lpguidApplication, LPDPENUMCONNECTIONSCALLBACK lpEnumCallback, LPVOID lpContext, DWORD dwFlags ) { #if DBG DPFN( eDbgLevelInfo, "======================================"); DPFN( eDbgLevelInfo, "COMHOOK IDirectPlay3A EnumConnections" ); #endif HRESULT hResult = DPERR_CONNECTIONLOST; typedef HRESULT (*_pfn_IDirectPlay3A_EnumConnections)( PVOID pThis, LPCGUID lpguidApplication, LPDPENUMCONNECTIONSCALLBACK lpEnumCallback, LPVOID lpContext, DWORD dwFlags); _pfn_IDirectPlay3A_EnumConnections EnumConnections = ORIGINAL_COM( IDirectPlay3A, EnumConnections, pThis); if (EnumConnections) { static bool alreadyHere = false; if (!alreadyHere) { alreadyHere = true; #if DBG LOGN( eDbgLevelError, "EnumConnections(%08x).", EnumConnections ); #endif // Enumerate connections to our own routine. hResult = EnumConnections(pThis, lpguidApplication, EnumConnectionsCallback, lpContext, dwFlags); #if DBG LOGN( eDbgLevelError, "Done EnumConnections Start calling app Size(%d).", DPlayConnectionsInfoVector::GetVector()->Size()); #endif // Call the application's callback routine with the GUID in the order it expects if (hResult == DP_OK) { DPlayConnectionsInfoVector::GetVector()->CallEnumRoutine(DPSPGUID_IPX, lpEnumCallback); DPlayConnectionsInfoVector::GetVector()->CallEnumRoutine(DPSPGUID_TCPIP, lpEnumCallback); DPlayConnectionsInfoVector::GetVector()->CallEnumRoutine(DPSPGUID_MODEM, lpEnumCallback); DPlayConnectionsInfoVector::GetVector()->CallEnumRoutine(DPSPGUID_SERIAL, lpEnumCallback); // Now loop over the list and enum any remaining providers for (int i = 0; i < DPlayConnectionsInfoVector::GetVector()->Size(); ++i) { DPlayConnectionsInfo & dpci = DPlayConnectionsInfoVector::GetVector()->Get(i); if (!dpci.m_beenUsed) { dpci.CallEnumRoutine(lpEnumCallback); dpci.m_beenUsed = TRUE; } } } alreadyHere = false; } #if DBG else { DPFN( eDbgLevelInfo, "EnumConnections Recursive." ); } #endif } // All done with the list, clean up. DPlayConnectionsInfoVector::GetVector()->FreeAndErase(); return hResult; } /*++ Register hooked functions --*/ HOOK_BEGIN APIHOOK_ENTRY_DIRECTX_COMSERVER() COMHOOK_ENTRY(DirectPlay, IDirectPlay4A, EnumConnections, 35) COMHOOK_ENTRY(DirectPlay, IDirectPlay3A, EnumConnections, 35) HOOK_END IMPLEMENT_SHIM_END