/*++ 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); if (m_lpConnection) { 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 { public: // Deconstruct the elements ~DPlayConnectionsInfoVector() { for (int i = 0; i < Size(); ++i) { DPlayConnectionsInfo & deleteMe = Get(i); deleteMe.Erase(); } } // Find an entry that matches this GUID DPlayConnectionsInfo * Find(const GUID & guidSP) { const int size = Size(); DPFN( eDbgLevelSpew, "Find GUID(%08x-%08x-%08x-%08x) Size(%d).", guidSP.Data1, guidSP.Data2, guidSP.Data3, guidSP.Data4, size); for (int i = 0; i < size; ++i) { DPlayConnectionsInfo & dpci = Get(i); DPFN( eDbgLevelSpew, " 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); if (dpci == guidSP) { DPFN( eDbgLevelSpew, "FOUND(%s).", dpci.m_lpName.lpszShortNameA); return &dpci; } } DPFN(eDbgLevelSpew, "NOT FOUND."); return NULL; } // Lookup the GUID and if found, call the callback routine. void CallEnumRoutine(const GUID & guidSP, LPDPENUMCONNECTIONSCALLBACK lpEnumCallback) { DPFN( eDbgLevelSpew, "CallEnumRoutine(%08x) Find GUID(%08x-%08x-%08x-%08x).", lpEnumCallback, guidSP.Data1, guidSP.Data2, guidSP.Data3, guidSP.Data4); DPlayConnectionsInfo * dpci = Find(guidSP); if (dpci) { dpci->CallEnumRoutine(lpEnumCallback); } } }; class DPlayEnumInfo { public: DPlayEnumInfo(LPVOID context, DPlayConnectionsInfoVector * conn) { lpContext = context; dPlayConnection = conn; } LPVOID lpContext; DPlayConnectionsInfoVector * dPlayConnection; }; /*++ 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 ) { DPlayEnumInfo * enumInfo = (DPlayEnumInfo*)lpContext; // Only add it to the list if it is not already there // App calls EnumConnections from inside Enum callback routine. if (!enumInfo->dPlayConnection->Find(*lpguidSP)) { DPFN( eDbgLevelSpew, "EnumConnectionsCallback Add(%d) (%s).", enumInfo->dPlayConnection->Size(), lpName->lpszShortName ); // Store the info for later DPlayConnectionsInfo dpci(lpguidSP, lpConnection, dwConnectionSize, lpName, dwFlags, enumInfo->lpContext); enumInfo->dPlayConnection->Append(dpci); } else { DPFN( eDbgLevelSpew, "EnumConnectionsCallback Already in the list(%s).", lpName->lpszShortName ); } 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 ) { DPFN( eDbgLevelSpew, "======================================"); DPFN( eDbgLevelSpew, "COMHOOK IDirectPlay4A EnumConnections" ); // Don't let a bad callback routine spoil our day if (IsBadCodePtr( (FARPROC) lpEnumCallback)) { return DPERR_INVALIDPARAMS; } 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) { DPFN( eDbgLevelSpew, "EnumConnections(%08x)\n", EnumConnections ); DPlayConnectionsInfoVector dPlayConnection; DPlayEnumInfo enumInfo(lpContext, &dPlayConnection); // Enumerate connections to our own routine. hResult = EnumConnections(pThis, lpguidApplication, EnumConnectionsCallback, (LPVOID)&enumInfo, dwFlags); LOGN( eDbgLevelError, "EnumConnections calling app with ordered connection list of Size(%d).", dPlayConnection.Size()); // Call the application's callback routine with the GUID in the order it expects if (hResult == DP_OK) { dPlayConnection.CallEnumRoutine(DPSPGUID_IPX, lpEnumCallback); dPlayConnection.CallEnumRoutine(DPSPGUID_TCPIP, lpEnumCallback); dPlayConnection.CallEnumRoutine(DPSPGUID_MODEM, lpEnumCallback); dPlayConnection.CallEnumRoutine(DPSPGUID_SERIAL, lpEnumCallback); // Now loop over the list and enum any remaining providers for (int i = 0; i < dPlayConnection.Size(); ++i) { DPlayConnectionsInfo & dpci = dPlayConnection.Get(i); if (!dpci.m_beenUsed) { dpci.CallEnumRoutine(lpEnumCallback); dpci.m_beenUsed = TRUE; } } } } return hResult; } /*++ Do the same thing for DirectPlay3 --*/ HRESULT COMHOOK(IDirectPlay3A, EnumConnections)( PVOID pThis, LPCGUID lpguidApplication, LPDPENUMCONNECTIONSCALLBACK lpEnumCallback, LPVOID lpContext, DWORD dwFlags ) { DPFN( eDbgLevelSpew, "======================================"); DPFN( eDbgLevelSpew, "COMHOOK IDirectPlay3A EnumConnections" ); // Don't let a bad callback routine spoil our day if (IsBadCodePtr( (FARPROC) lpEnumCallback)) { return DPERR_INVALIDPARAMS; } 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) { DPFN( eDbgLevelSpew, "EnumConnections(%08x)\n", EnumConnections ); DPlayConnectionsInfoVector dPlayConnection; DPlayEnumInfo enumInfo(lpContext, &dPlayConnection); // Enumerate connections to our own routine. hResult = EnumConnections(pThis, lpguidApplication, EnumConnectionsCallback, (LPVOID)&enumInfo, dwFlags); LOGN( eDbgLevelError, "EnumConnections calling app with ordered connection list of Size(%d).", dPlayConnection.Size()); // Call the application's callback routine with the GUID in the order it expects if (hResult == DP_OK) { dPlayConnection.CallEnumRoutine(DPSPGUID_IPX, lpEnumCallback); dPlayConnection.CallEnumRoutine(DPSPGUID_TCPIP, lpEnumCallback); dPlayConnection.CallEnumRoutine(DPSPGUID_MODEM, lpEnumCallback); dPlayConnection.CallEnumRoutine(DPSPGUID_SERIAL, lpEnumCallback); // Now loop over the list and enum any remaining providers for (int i = 0; i < dPlayConnection.Size(); ++i) { DPlayConnectionsInfo & dpci = dPlayConnection.Get(i); if (!dpci.m_beenUsed) { dpci.CallEnumRoutine(lpEnumCallback); dpci.m_beenUsed = TRUE; } } } } 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