404 lines
11 KiB
C++
404 lines
11 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);
|
|
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<DPlayConnectionsInfo>
|
|
{
|
|
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
|
|
|