438 lines
12 KiB
C++
438 lines
12 KiB
C++
|
/*++
|
||
|
|
||
|
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 <Dplay.h>
|
||
|
|
||
|
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<DPlayConnectionsInfo>
|
||
|
{
|
||
|
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
|
||
|
|