Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1222 lines
36 KiB

/*==========================================================================
*
* Copyright (C) 2000 Microsoft Corporation. All Rights Reserved.
*
* File: DPLCommon.cpp
* Content: DirectPlay Lobby Common Functions
*@@BEGIN_MSINTERNAL
* History:
* Date By Reason
* ==== == ======
* 02/21/00 mjn Created
* 04/13/00 rmt First pass param validation
* 04/26/00 mjn Removed dwTimeOut from Send() API call
* 05/01/00 rmt Bug #33108 -- Initialize returns DPNERR_NORESPONSE when not lobbied
* 05/03/00 rmt Updated initialize so if lobby launched automatically establishes a
* connection and makes self unavailable. (Also waits for connection).
* 05/16/00 rmt Bug #34734 -- Init Client, Init App, Close App hangs --
* both client and app were using 'C' prefix, should have been 'C' for
* client and 'A' for app.
* 06/14/00 rmt Fixed build break with new compiler (added ')''s).
* 06/15/00 rmt Bug #33617 - Must provide method for providing automatic launch of DirectPlay instances
* 06/28/00 rmt Prefix Bug #38082
* 07/06/00 rmt Bug #38717 ASSERTION when sending data
* 07/08/2000 rmt Bug #38725 - Need to provide method to detect if app was lobby launched
* rmt Bug #38757 - Callback messages for connections may return AFTER WaitForConnection returns
* rmt Bug #38755 - No way to specify player name in Connection Settings
* rmt Bug #38758 - DPLOBBY8.H has incorrect comments
* rmt Bug #38783 - pvUserApplicationContext is only partially implemented
* rmt Added DPLHANDLE_ALLCONNECTIONS and dwFlags (reserved field to couple of funcs).
* 07/13/2000 rmt Fixed memory leak
* 07/14/2000 rmt Bug #39257 - LobbyClient::ReleaseApp returns E_OUTOFMEMORY when called when no one connected
* 07/21/2000 rmt Removed assert which wasn't needed
* 08/03/2000 rmt Removed assert which wasn't needed
* 08/05/2000 RichGr IA64: Use %p format specifier in DPFs for 32/64-bit pointers and handles.
* 08/18/2000 rmt Bug #42751 - DPLOBBY8: Prohibit more than one lobby client or lobby app per process
* 08/24/2000 rmt Bug #43317 - DP8LOBBY: Occasionally when closing Lobby App right after releasing handles, causes assertion.
* 02/06/2001 rodtoll WINBUG #293871: DPLOBBY8: [IA64] Lobby launching a 64-bit
* app from 64-bit lobby launcher crashes with unaligned memory error.
*
*@@END_MSINTERNAL
*
***************************************************************************/
#include "dnlobbyi.h"
//**********************************************************************
// Constant definitions
//**********************************************************************
//**********************************************************************
// Macro definitions
//**********************************************************************
//**********************************************************************
// Structure definitions
//**********************************************************************
//**********************************************************************
// Variable definitions
//**********************************************************************
LONG volatile g_lLobbyAppCount = 0;
LONG volatile g_lLobbyClientCount = 0;
//**********************************************************************
// Function prototypes
//**********************************************************************
//**********************************************************************
// Function definitions
//**********************************************************************
// DPL_GetConnectionSettings
//
// Retrieves the pdplSessionInfo (if any) associated with the specified connection. This method
// is shared between the client and app interfaces.
//
#undef DPF_MODNAME
#define DPF_MODNAME "DPL_GetConnectionSettings"
STDMETHODIMP DPL_GetConnectionSettings(LPVOID lpv,const DPNHANDLE hLobbyClient, DPL_CONNECTION_SETTINGS * const pdplSessionInfo, DWORD *pdwInfoSize, const DWORD dwFlags )
{
HRESULT hResultCode;
DIRECTPLAYLOBBYOBJECT *pdpLobbyObject;
DPFX(DPFPREP, 3,"Parameters: hTarget [0x%lx], pdplSessionInfo [0x%p], pdwInfoSize [%p], dwFlags [0x%lx]",
hLobbyClient,pdplSessionInfo,pdwInfoSize,dwFlags);
#ifndef DPNBUILD_NOPARAMVAL
TRY
{
#endif // !DPNBUILD_NOPARAMVAL
pdpLobbyObject = static_cast<DIRECTPLAYLOBBYOBJECT*>(GET_OBJECT_FROM_INTERFACE(lpv));
#ifndef DPNBUILD_NOPARAMVAL
if( pdpLobbyObject->dwFlags & DPL_OBJECT_FLAG_PARAMVALIDATION )
{
if( FAILED( hResultCode = DPL_ValidateGetConnectionSettings( lpv, hLobbyClient, pdplSessionInfo, pdwInfoSize, dwFlags ) ) )
{
DPFX(DPFPREP, 0, "Error validating getconnectsettings params hr=[0x%lx]", hResultCode );
DPF_RETURN( hResultCode );
}
}
// Ensure we've been initialized
if (pdpLobbyObject->pReceiveQueue == NULL)
{
DPFERR("Not initialized");
DPF_RETURN(DPNERR_UNINITIALIZED);
}
}
EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
DPFERR("Invalid object" );
DPF_RETURN(DPNERR_INVALIDOBJECT);
}
#endif // !DPNBUILD_NOPARAMVAL
// Attempt to retrieve connection settings.
hResultCode = DPLConnectionGetConnectSettings( pdpLobbyObject, hLobbyClient, pdplSessionInfo, pdwInfoSize );
DPF_RETURN( hResultCode );
}
// DPL_SetConnectionSettings
//
// Sets the pdplSessionInfo structure associated with the specified connection. This method
// is shared between the client and app interfaces.
//
// This function will generate a DPL_MSGID_CONNECTION_SETTINGS message to be sent to the specified
// connection.
//
#undef DPF_MODNAME
#define DPF_MODNAME "DPL_SetConnectionSettings"
STDMETHODIMP DPL_SetConnectionSettings(LPVOID lpv,const DPNHANDLE hLobbyClient, const DPL_CONNECTION_SETTINGS * const pdplSessionInfo, const DWORD dwFlags )
{
HRESULT hResultCode;
DPL_CONNECTION *pdplConnection;
DIRECTPLAYLOBBYOBJECT *pdpLobbyObject;
DPNHANDLE *hTargets = NULL;
DWORD dwNumTargets = 0;
DWORD dwTargetIndex = 0;
CConnectionSettings *pConnectionSettings = NULL;
DPFX(DPFPREP, 3,"Parameters: hLobbyClient [0x%lx], pBuffer [0x%p], dwFlags [0x%lx]",
hLobbyClient,pdplSessionInfo,dwFlags);
#ifndef DPNBUILD_NOPARAMVAL
TRY
{
#endif // !DPNBUILD_NOPARAMVAL
pdpLobbyObject = static_cast<DIRECTPLAYLOBBYOBJECT*>(GET_OBJECT_FROM_INTERFACE(lpv));
#ifndef DPNBUILD_NOPARAMVAL
if( pdpLobbyObject->dwFlags & DPL_OBJECT_FLAG_PARAMVALIDATION )
{
if( FAILED( hResultCode = DPL_ValidateSetConnectionSettings( lpv, hLobbyClient, pdplSessionInfo, dwFlags ) ) )
{
DPFX(DPFPREP, 0, "Error validating setconnectsettings params hr=[0x%lx]", hResultCode );
DPF_RETURN( hResultCode );
}
}
// Ensure we've been initialized
if (pdpLobbyObject->pReceiveQueue == NULL)
{
DPFERR("Not initialized");
DPF_RETURN(DPNERR_UNINITIALIZED);
}
}
EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
DPFERR("Invalid object" );
DPF_RETURN(DPNERR_INVALIDOBJECT);
}
#endif // !DPNBUILD_NOPARAMVAL
if( hLobbyClient == DPLHANDLE_ALLCONNECTIONS )
{
dwNumTargets = 0;
// We need loop so if someone adds a connection during our run
// it gets added to our list
//
while( 1 )
{
hResultCode = DPLConnectionEnum( pdpLobbyObject, hTargets, &dwNumTargets );
if( hResultCode == DPNERR_BUFFERTOOSMALL )
{
if( hTargets )
{
delete [] hTargets;
}
hTargets = new DPNHANDLE[dwNumTargets];
if( hTargets == NULL )
{
DPFERR("Error allocating memory" );
dwNumTargets = 0;
hResultCode = DPNERR_OUTOFMEMORY;
goto SETCONNECT_EXIT;
}
continue;
}
else if( FAILED( hResultCode ) )
{
DPFX(DPFPREP, 0, "Error getting list of connections hr=0x%x", hResultCode );
break;
}
else
{
break;
}
}
// Failed getting connection information
if( FAILED( hResultCode ) )
{
if( hTargets )
{
delete [] hTargets;
hTargets = NULL;
}
dwNumTargets = 0;
goto SETCONNECT_EXIT;
}
}
else
{
hTargets = new DPNHANDLE[1]; // We use array delete below so we need array new
if( hTargets == NULL )
{
DPFERR("Error allocating memory" );
dwNumTargets = 0;
hResultCode = DPNERR_OUTOFMEMORY;
goto SETCONNECT_EXIT;
}
dwNumTargets = 1;
hTargets[0] = hLobbyClient;
}
for( dwTargetIndex = 0; dwTargetIndex < dwNumTargets; dwTargetIndex++ )
{
if ((hResultCode = DPLConnectionFind(pdpLobbyObject,hTargets[dwTargetIndex],&pdplConnection,TRUE)) != DPN_OK)
{
DPFERR("Invalid send target");
DisplayDNError(0,hResultCode);
continue;
}
if( pdplSessionInfo )
{
pConnectionSettings = new CConnectionSettings();
if( !pConnectionSettings )
{
DPFERR("Error allocating memory" );
hResultCode = DPNERR_OUTOFMEMORY;
goto SETCONNECT_EXIT;
}
hResultCode = pConnectionSettings->InitializeAndCopy( pdplSessionInfo );
if( FAILED( hResultCode ) )
{
DPFX( DPFPREP, 0, "Error setting up connection settings hr [0x%x]", hResultCode );
goto SETCONNECT_EXIT;
}
}
// Attempt to set connection settings.
hResultCode = DPLConnectionSetConnectSettings( pdpLobbyObject, hTargets[dwTargetIndex], pConnectionSettings );
if( FAILED( hResultCode ) )
{
DPFX(DPFPREP, 0, "Error setting connct settings for 0x%x hr=0x%x", hTargets[dwTargetIndex], hResultCode );
delete pConnectionSettings;
}
hResultCode = DPLSendConnectionSettings( pdpLobbyObject, hTargets[dwTargetIndex] );
if( FAILED( hResultCode ) )
{
DPFX(DPFPREP, 0, "Error sending connection settings to client 0x%x hr=0x%x", hTargets[dwTargetIndex], hResultCode );
}
pConnectionSettings = NULL;
}
SETCONNECT_EXIT:
for( dwTargetIndex = 0; dwTargetIndex < dwNumTargets; dwTargetIndex++ )
{
if( hTargets[dwTargetIndex] )
DPLConnectionRelease(pdpLobbyObject,hTargets[dwTargetIndex]);
}
if( hTargets )
delete [] hTargets;
DPF_RETURN(hResultCode);
}
#undef DPF_MODNAME
#define DPF_MODNAME "DPL_RegisterMessageHandlerClient"
STDMETHODIMP DPL_RegisterMessageHandlerClient(PVOID pv,
const PVOID pvUserContext,
const PFNDPNMESSAGEHANDLER pfn,
const DWORD dwFlags)
{
return DPL_RegisterMessageHandler( pv, pvUserContext, pfn, NULL, dwFlags );
}
// HRESULT DPL_RegisterMessageHandler
// PVOID pv Interface pointer
// PVOID pvUserContext User context
// PFNDPNMESSAGEHANDLER pfn User supplied message handler
// DWORD dwFlags Not Used
//
// Returns
// DPN_OK If the message handler was registered without incident
// DPNERR_INVALIDPARAM If there was an invalid parameter
// DPNERR_GENERIC If there were any problems
//
// Notes
// This function registers a user supplied message handler function. This function should
// only be called once, even in cases where a game is being re-connected (i.e. after ending)
//
// This will set up the required message queues, handshake the lobby client's PID (if supplied on the
// command line) and spawn the application's receive message queue thread.
#undef DPF_MODNAME
#define DPF_MODNAME "DPL_RegisterMessageHandler"
STDMETHODIMP DPL_RegisterMessageHandler(PVOID pv,
const PVOID pvUserContext,
const PFNDPNMESSAGEHANDLER pfn,
DPNHANDLE * const pdpnhConnection,
const DWORD dwFlags)
{
HRESULT hResultCode = DPN_OK;
DWORD dwCurrentPid;
DWORD dwThreadId;
PDIRECTPLAYLOBBYOBJECT pdpLobbyObject;
char cSuffix;
DPFX(DPFPREP, 3,"Parameters: pv [0x%p], pfn [0x%p], dwFlags [%lx]",pv,pfn,dwFlags);
#ifndef DPNBUILD_NOPARAMVAL
TRY
{
#endif // !DPNBUILD_NOPARAMVAL
pdpLobbyObject = static_cast<DIRECTPLAYLOBBYOBJECT*>(GET_OBJECT_FROM_INTERFACE(pv));
#ifndef DPNBUILD_NOPARAMVAL
// TODO: MASONB: Why no paramval flag wrapping this?
if( FAILED( hResultCode = DPL_ValidateRegisterMessageHandler( pv, pvUserContext, pfn, pdpnhConnection, dwFlags ) ) )
{
DPFX(DPFPREP, 0, "Error validating register message handler params hr=[0x%lx]", hResultCode );
DPF_RETURN( hResultCode );
}
// Ensure we've been initialized
if (pdpLobbyObject->pReceiveQueue != NULL)
{
DPFERR("Already initialized");
DPF_RETURN(DPNERR_ALREADYINITIALIZED);
}
}
EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
DPFERR("Invalid object" );
DPF_RETURN(DPNERR_INVALIDOBJECT);
}
#endif // !DPNBUILD_NOPARAMVAL
if (pdpLobbyObject->dwFlags & DPL_OBJECT_FLAG_LOBBIEDAPPLICATION )
{
// If we weren't at zero complain, otherwise put us at 1
if( DNInterlockedCompareExchange((LONG*)&g_lLobbyAppCount, 1, 0) != 0 )
{
DPFERR( "You can only start one lobbied application per process!" );
DPF_RETURN( DPNERR_NOTALLOWED );
}
}
else
{
DNASSERT(pdpLobbyObject->dwFlags & DPL_OBJECT_FLAG_LOBBYCLIENT);
// If we weren't at zero complain, otherwise put us at 1
if( DNInterlockedCompareExchange((LONG*)&g_lLobbyClientCount, 1, 0) != 0 )
{
DPFERR( "You can only start one lobby client per process!" );
DPF_RETURN( DPNERR_NOTALLOWED );
}
}
#ifndef DPNBUILD_NOPARAMVAL
// Disable parameter validation flag if DPNINITIALIZE_DISABLEPARAMVAL
// is specified
if( dwFlags & DPLINITIALIZE_DISABLEPARAMVAL )
{
pdpLobbyObject->dwFlags &= ~(DPL_OBJECT_FLAG_PARAMVALIDATION);
}
#endif // !DPNBUILD_NOPARAMVAL
pdpLobbyObject->pfnMessageHandler = pfn;
pdpLobbyObject->pvUserContext = pvUserContext;
pdpLobbyObject->pReceiveQueue = new CMessageQueue;
if( pdpLobbyObject->pReceiveQueue == NULL )
{
DPFX(DPFPREP, 0, "Error allocating receive queue" );
hResultCode = DPNERR_OUTOFMEMORY;
goto ERROR_DPL_RegisterMessageHandler;
}
pdpLobbyObject->pReceiveQueue->SetMessageHandler(static_cast<PVOID>(pdpLobbyObject),DPLMessageHandler);
if (pdpLobbyObject->dwFlags & DPL_OBJECT_FLAG_LOBBIEDAPPLICATION)
{
cSuffix = DPL_MSGQ_OBJECT_SUFFIX_APPLICATION;
}
else
{
cSuffix = DPL_MSGQ_OBJECT_SUFFIX_CLIENT;
}
// Open application receive message queue
dwCurrentPid = GetCurrentProcessId();
if ((hResultCode = pdpLobbyObject->pReceiveQueue->Open(dwCurrentPid,
cSuffix,DPL_MSGQ_SIZE,DPL_MSGQ_TIMEOUT_IDLE,0)) != DPN_OK)
{
DPFERR("Could not open App Rec Q");
goto ERROR_DPL_RegisterMessageHandler;
}
if ((pdpLobbyObject->hReceiveThread =
DNCreateThread(NULL,(DWORD)NULL,(LPTHREAD_START_ROUTINE)DPLProcessMessageQueue,
static_cast<void*>(pdpLobbyObject->pReceiveQueue),(DWORD)NULL,&dwThreadId)) == NULL)
{
DPFERR("CreateThread() failed");
hResultCode = DPNERR_GENERIC;
pdpLobbyObject->pReceiveQueue->Close();
goto ERROR_DPL_RegisterMessageHandler;
}
pdpLobbyObject->pReceiveQueue->WaitForReceiveThread(INFINITE);
if (pdpLobbyObject->dwFlags & DPL_OBJECT_FLAG_LOBBIEDAPPLICATION)
{
DPFX(DPFPREP, 5,"Attempt lobby connection");
hResultCode = DPLAttemptLobbyConnection(pdpLobbyObject);
if ( hResultCode == DPN_OK)
{
if( pdpnhConnection )
*pdpnhConnection = pdpLobbyObject->dpnhLaunchedConnection;
DPFX(DPFPREP, 5,"Application was lobby launched");
DPFX(DPFPREP, 5,"Waiting for true connect notification" );
DWORD dwReturnValue = DNWaitForSingleObject( pdpLobbyObject->hConnectEvent, DPL_LOBBYLAUNCHED_CONNECT_TIMEOUT );
DNASSERT( dwReturnValue == WAIT_OBJECT_0 );
}
else if( hResultCode != DPNERR_TIMEDOUT )
{
DPFX(DPFPREP, 5,"Application was not lobby launched");
if( pdpnhConnection )
*pdpnhConnection = NULL;
// Need to reset return code to OK.. this is not an error
hResultCode = DPN_OK;
}
else
{
DPFERR( "App was lobby launched but timed out establishing a connection" );
if( pdpnhConnection )
*pdpnhConnection = NULL;
}
}
EXIT_DPL_RegisterMessageHandler:
DPF_RETURN(hResultCode);
ERROR_DPL_RegisterMessageHandler:
if (pdpLobbyObject->dwFlags & DPL_OBJECT_FLAG_LOBBIEDAPPLICATION )
{
DNInterlockedExchange((LONG*)&g_lLobbyAppCount, 0);
}
else
{
DNASSERT(pdpLobbyObject->dwFlags & DPL_OBJECT_FLAG_LOBBYCLIENT);
DNInterlockedExchange((LONG*)&g_lLobbyClientCount, 0);
}
goto EXIT_DPL_RegisterMessageHandler;
}
#undef DPF_MODNAME
#define DPF_MODNAME "DPL_Close"
STDMETHODIMP DPL_Close(PVOID pv, const DWORD dwFlags )
{
HRESULT hResultCode;
DWORD dwNumHandles;
DPNHANDLE *prgHandles;
DWORD dw;
DIRECTPLAYLOBBYOBJECT *pdpLobbyObject;
DPL_CONNECTION *pConnection;
DPFX(DPFPREP, 3,"Parameters: (none)");
#ifndef DPNBUILD_NOPARAMVAL
TRY
{
#endif // !DPNBUILD_NOPARAMVAL
pdpLobbyObject = static_cast<DIRECTPLAYLOBBYOBJECT*>(GET_OBJECT_FROM_INTERFACE(pv));
#ifndef DPNBUILD_NOPARAMVAL
// TODO: MASONB: Why no paramval flag wrapping this?
if( FAILED( hResultCode = DPL_ValidateClose( pv, dwFlags ) ) )
{
DPFX(DPFPREP, 0, "Error validating close params hr=[0x%lx]", hResultCode );
return hResultCode;
}
// Ensure we've been initialized
if (pdpLobbyObject->pReceiveQueue == NULL)
{
DPFERR("Already closed");
return DPNERR_UNINITIALIZED;
}
}
EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
DPFERR("Invalid object" );
return DPNERR_INVALIDOBJECT;
}
#endif // !DPNBUILD_NOPARAMVAL
// Shutdown the queue first to ensure that we don't end up shutting down a connection
// twice! (E.g. disconnect comes in as we are disconnecting it).
if (pdpLobbyObject->pReceiveQueue)
{
if (pdpLobbyObject->pReceiveQueue->IsOpen())
{
// Ask receive thread to terminate
DPFX(DPFPREP, 5,"Terminate Receive Msg Thread");
pdpLobbyObject->pReceiveQueue->Terminate();
// Wait for termination to occur
if (DNWaitForSingleObject(pdpLobbyObject->hReceiveThread,INFINITE) != WAIT_OBJECT_0)
{
hResultCode = DPNERR_GENERIC;
DPFERR("WaitForSingleObject failed");
}
pdpLobbyObject->pReceiveQueue->Close();
if (pdpLobbyObject->pReceiveQueue)
{
delete pdpLobbyObject->pReceiveQueue;
pdpLobbyObject->pReceiveQueue = NULL;
}
if (pdpLobbyObject->hReceiveThread)
{
DNCloseHandle(pdpLobbyObject->hReceiveThread);
pdpLobbyObject->hReceiveThread = NULL;
}
if (pdpLobbyObject->hConnectEvent)
{
DNCloseHandle(pdpLobbyObject->hConnectEvent);
pdpLobbyObject->hConnectEvent = NULL;
}
if (pdpLobbyObject->hLobbyLaunchConnectEvent)
{
DNCloseHandle(pdpLobbyObject->hLobbyLaunchConnectEvent);
pdpLobbyObject->hLobbyLaunchConnectEvent = NULL;
}
}
}
// Enumerate handles outstanding
dwNumHandles = 0;
prgHandles = NULL;
hResultCode = DPLConnectionEnum(pdpLobbyObject,prgHandles,&dwNumHandles);
while (hResultCode == DPNERR_BUFFERTOOSMALL)
{
if (prgHandles)
DNFree(prgHandles);
if ((prgHandles = static_cast<DPNHANDLE*>(DNMalloc(dwNumHandles*sizeof(DPNHANDLE)))) != NULL)
{
hResultCode = DPLConnectionEnum(pdpLobbyObject,prgHandles,&dwNumHandles);
}
else
{
DPFERR("Could not allocate space for handle array");
hResultCode = DPNERR_OUTOFMEMORY;
break;
}
}
// Send DISCONNECTs to all attached msg queues, for which there are handles
if (hResultCode == DPN_OK)
{
for (dw = 0 ; dw < dwNumHandles ; dw++)
{
hResultCode = DPLConnectionFind(pdpLobbyObject,prgHandles[dw],&pConnection,TRUE );
if( SUCCEEDED( hResultCode ) )
{
hResultCode = DPLConnectionDisconnect(pdpLobbyObject,prgHandles[dw]);
if( FAILED( hResultCode ) )
{
DPFX(DPFPREP, 0, "Error disconnecting connection 0x%x", hResultCode );
}
DPLConnectionRelease( pdpLobbyObject,prgHandles[dw]);
}
}
// Errors above are irrelevant, it's quite possible after building the list of outstanding
// connections that before we attempt to close the list one has gone away.
//
hResultCode = DPN_OK;
}
if (prgHandles)
{
DNFree(prgHandles);
prgHandles = NULL;
}
if (pdpLobbyObject->dwFlags & DPL_OBJECT_FLAG_LOBBIEDAPPLICATION )
{
DNInterlockedExchange((LONG*)&g_lLobbyAppCount, 0);
}
else
{
DNASSERT(pdpLobbyObject->dwFlags & DPL_OBJECT_FLAG_LOBBYCLIENT);
DNInterlockedExchange((LONG*)&g_lLobbyClientCount, 0);
}
DPF_RETURN( hResultCode );
}
#undef DPF_MODNAME
#define DPF_MODNAME "DPL_Send"
STDMETHODIMP DPL_Send(PVOID pv,
const DPNHANDLE hTarget,
BYTE *const pBuffer,
const DWORD dwBufferSize,
const DWORD dwFlags)
{
HRESULT hResultCode;
DPL_CONNECTION *pdplConnection;
DIRECTPLAYLOBBYOBJECT *pdpLobbyObject;
DPNHANDLE *hTargets = NULL;
DWORD dwNumTargets = 0;
DWORD dwTargetIndex = 0;
DPFX(DPFPREP, 3,"Parameters: hTarget [0x%lx], pBuffer [0x%p], dwBufferSize [%ld], dwFlags [0x%lx]",
hTarget,pBuffer,dwBufferSize,dwFlags);
#ifndef DPNBUILD_NOPARAMVAL
TRY
{
#endif // !DPNBUILD_NOPARAMVAL
pdpLobbyObject = static_cast<DIRECTPLAYLOBBYOBJECT*>(GET_OBJECT_FROM_INTERFACE(pv));
#ifndef DPNBUILD_NOPARAMVAL
if( pdpLobbyObject->dwFlags & DPL_OBJECT_FLAG_PARAMVALIDATION )
{
if( FAILED( hResultCode = DPL_ValidateSend( pv, hTarget, pBuffer, dwBufferSize, dwFlags ) ) )
{
DPFX(DPFPREP, 0, "Error validating send params hr=[0x%lx]", hResultCode );
DPF_RETURN( hResultCode );
}
}
// Ensure we've been initialized
if (pdpLobbyObject->pReceiveQueue == NULL)
{
DPFERR("Not initialized");
DPF_RETURN(DPNERR_UNINITIALIZED);
}
}
EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
DPFERR("Invalid object" );
DPF_RETURN(DPNERR_INVALIDOBJECT);
}
#endif // !DPNBUILD_NOPARAMVAL
if( hTarget == DPLHANDLE_ALLCONNECTIONS )
{
dwNumTargets = 0;
// We need loop so if someone adds a connection during our run
// it gets added to our list
//
while( 1 )
{
hResultCode = DPLConnectionEnum( pdpLobbyObject, hTargets, &dwNumTargets );
if( hResultCode == DPNERR_BUFFERTOOSMALL )
{
if( hTargets )
{
delete [] hTargets;
}
hTargets = new DPNHANDLE[dwNumTargets];
if( hTargets == NULL )
{
DPFERR("Error allocating memory" );
dwNumTargets = 0;
hResultCode = DPNERR_OUTOFMEMORY;
goto EXIT_AND_CLEANUP;
}
memset( hTargets, 0x00, sizeof(DPNHANDLE)*dwNumTargets);
continue;
}
else if( FAILED( hResultCode ) )
{
DPFX(DPFPREP, 0, "Error getting list of connections hr=0x%x", hResultCode );
break;
}
else
{
break;
}
}
// Failed getting connection information
if( FAILED( hResultCode ) )
{
if( hTargets )
{
delete [] hTargets;
hTargets = NULL;
}
dwNumTargets = 0;
goto EXIT_AND_CLEANUP;
}
}
else
{
hTargets = new DPNHANDLE[1]; // We use array delete below so we need array new
if( hTargets == NULL )
{
DPFERR("Error allocating memory" );
dwNumTargets = 0;
hResultCode = DPNERR_OUTOFMEMORY;
goto EXIT_AND_CLEANUP;
}
dwNumTargets = 1;
hTargets[0] = hTarget;
}
for( dwTargetIndex = 0; dwTargetIndex < dwNumTargets; dwTargetIndex++ )
{
if ((hResultCode = DPLConnectionFind(pdpLobbyObject,hTargets[dwTargetIndex],&pdplConnection,TRUE)) != DPN_OK)
{
DPFERR("Invalid send target");
DisplayDNError(0,hResultCode);
hResultCode = DPNERR_INVALIDHANDLE;
goto EXIT_AND_CLEANUP;
}
DNASSERT(pdplConnection->pSendQueue != NULL);
if (!pdplConnection->pSendQueue->IsReceiving())
{
DPFERR("Other side is not listening");
DPLConnectionRelease(pdpLobbyObject,hTarget);
hResultCode = DPNERR_INVALIDHANDLE;
goto EXIT_AND_CLEANUP;
}
hResultCode = pdplConnection->pSendQueue->Send(pBuffer,dwBufferSize,INFINITE,DPL_MSGQ_MSGFLAGS_USER2,0);
if( FAILED( hResultCode ) )
{
DPFX(DPFPREP, 0, "Error sending to connection 0x%x hr=0x%x", hTargets[dwTargetIndex], hResultCode );
}
}
EXIT_AND_CLEANUP:
for( dwTargetIndex = 0; dwTargetIndex < dwNumTargets; dwTargetIndex++ )
{
if( hTargets[dwTargetIndex] )
DPLConnectionRelease(pdpLobbyObject,hTargets[dwTargetIndex]);
}
if( hTargets )
delete [] hTargets;
DPF_RETURN(hResultCode);
}
#undef DPF_MODNAME
#define DPF_MODNAME "DPLReceiveIdleTimeout"
HRESULT DPLReceiveIdleTimeout(DIRECTPLAYLOBBYOBJECT *const pdpLobbyObject,
const DPNHANDLE hSender)
{
DPL_CONNECTION *pConnection;
CBilink* pblTemp;
CBilink blRemove;
blRemove.Initialize();
DPFX(DPFPREP, 6, "(%p) Enumerating processes, checking for exit", pdpLobbyObject );
DNEnterCriticalSection(&pdpLobbyObject->m_cs);
// Go through the list of all connections and build up a list of the ones that
// need to be removed.
pblTemp = pdpLobbyObject->m_blConnections.GetNext();
while (pblTemp != &pdpLobbyObject->m_blConnections)
{
pConnection = CONTAINING_OBJECT(pblTemp, DPL_CONNECTION, m_blLobbyObjectLinkage);
pblTemp = pblTemp->GetNext();
DWORD dwExitCode=0;
if (DNGetExitCodeProcess(pConnection->hTargetProcess, &dwExitCode)==FALSE || dwExitCode!=STILL_ACTIVE)
{
DPFX(DPFPREP, 5, "(%p) Process exit detected hTargetProcess %u dwExitCode %u GetLastError %u",
pdpLobbyObject, HANDLE_FROM_DNHANDLE(pConnection->hTargetProcess), dwExitCode, GetLastError());
// Take the connection off of the Lobby Object's list and on a temporary list
pConnection->m_blLobbyObjectLinkage.RemoveFromList();
pdpLobbyObject->m_dwConnectionCount--;
pConnection->m_blLobbyObjectLinkage.InsertBefore(&blRemove);
}
}
DNLeaveCriticalSection(&pdpLobbyObject->m_cs);
// Go through the list of removed connections and signal the user
pblTemp = blRemove.GetNext();
while (pblTemp != &blRemove)
{
pConnection = CONTAINING_OBJECT(pblTemp, DPL_CONNECTION, m_blLobbyObjectLinkage);
pblTemp = pblTemp->GetNext();
pConnection->m_blLobbyObjectLinkage.RemoveFromList();
// Process has exited..
DPFX(DPFPREP, 6, "(%p) Process has exited PID %u hProcess", pdpLobbyObject,
pConnection->dwTargetProcessIdentity, HANDLE_FROM_DNHANDLE(pConnection->hTargetProcess ));
DPLConnectionReceiveDisconnect( pdpLobbyObject, pConnection->hConnect, NULL, DPNERR_CONNECTIONLOST );
}
return DPN_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "DPLReceiveUserMessage"
HRESULT DPLReceiveUserMessage(DIRECTPLAYLOBBYOBJECT *const pdpLobbyObject,
const DPNHANDLE hSender,
BYTE *const pBuffer,
const DWORD dwBufferSize)
{
HRESULT hResultCode;
DPL_MESSAGE_RECEIVE Msg;
Msg.dwSize = sizeof(DPL_MESSAGE_RECEIVE);
Msg.pBuffer = pBuffer;
Msg.dwBufferSize = dwBufferSize;
Msg.hSender = hSender;
hResultCode = DPLConnectionGetContext( pdpLobbyObject, hSender, &Msg.pvConnectionContext );
// Failed to get the connection's context -- strange, but we're going to indicate anyhow.
//
if( FAILED( hResultCode ) )
{
DPFX(DPFPREP, 0, "Failed getting connection context hResultCode = 0x%x", hResultCode );
}
hResultCode = (pdpLobbyObject->pfnMessageHandler)(pdpLobbyObject->pvUserContext,
DPL_MSGID_RECEIVE,
reinterpret_cast<BYTE*>(&Msg));
DPFX(DPFPREP, 3,"Returning: [0x%lx]",hResultCode);
return(hResultCode);
}
#undef DPF_MODNAME
#define DPF_MODNAME "DPLMessageHandler"
HRESULT DPLMessageHandler(PVOID pvContext,
const DPNHANDLE hSender,
DWORD dwMessageFlags,
BYTE *const pBuffer,
const DWORD dwBufferSize)
{
DIRECTPLAYLOBBYOBJECT *pdpLobbyObject;
HRESULT hResultCode;
DWORD *pdwMsgId;
DPFX(DPFPREP, 3,"Parameters: hSender [0x%x], pBuffer [0x%p], dwBufferSize [%ld]",
hSender,pBuffer,dwBufferSize);
DNASSERT(pBuffer != NULL);
/*if (dwBufferSize < sizeof(DWORD))
{
DPFERR("Invalid message");
return(DPNERR_GENERIC);
}*/
pdpLobbyObject = static_cast<DIRECTPLAYLOBBYOBJECT*>(pvContext);
pdwMsgId = reinterpret_cast<DWORD*>(pBuffer);
if( dwMessageFlags & DPL_MSGQ_MSGFLAGS_USER1 )
{
DPFX(DPFPREP, 5,"Received INTERNAL message");
switch(*pdwMsgId)
{
case DPL_MSGID_INTERNAL_IDLE_TIMEOUT:
{
DPFX(DPFPREP, 5,"Received: DPL_MSGID_INTERNAL_IDLE_TIMEOUT" );
DPLReceiveIdleTimeout(pdpLobbyObject,hSender);
break;
}
case DPL_MSGID_INTERNAL_DISCONNECT:
{
DPFX(DPFPREP, 5,"Received: DPL_MSGID_INTERNAL_DISCONNECT");
DPLConnectionReceiveDisconnect(pdpLobbyObject,hSender,pBuffer,DPN_OK);
break;
}
case DPL_MSGID_INTERNAL_CONNECT_REQ:
{
DPFX(DPFPREP, 5,"Received: DPL_MSGID_INTERNAL_CONNECT_REQ");
DPLConnectionReceiveREQ(pdpLobbyObject,pBuffer);
break;
}
case DPL_MSGID_INTERNAL_CONNECT_ACK:
{
DPFX(DPFPREP, 5,"Received: DPL_MSGID_INTERNAL_CONNECT_ACK");
DPLConnectionReceiveACK(pdpLobbyObject,hSender,pBuffer);
break;
}
case DPL_MSGID_INTERNAL_UPDATE_STATUS:
{
DPFX(DPFPREP, 5,"Received: DPL_MSGID_INTERNAL_UPDATE_STATUS");
DPLUpdateAppStatus(pdpLobbyObject,hSender,pBuffer);
break;
}
case DPL_MSGID_INTERNAL_CONNECTION_SETTINGS:
{
DPFX(DPFPREP, 5,"Received: DPL_MSGID_INTERNAL_CONNECTION_SETTINGS");
DPLUpdateConnectionSettings(pdpLobbyObject,hSender,pBuffer);
break;
}
default:
{
DPFX(DPFPREP, 5,"Received: Unknown message [0x%lx]",*pdwMsgId);
DNASSERT(FALSE);
break;
}
}
}
else if( dwMessageFlags & DPL_MSGQ_MSGFLAGS_USER2 )
{
DNASSERT( !(dwMessageFlags & DPL_MSGQ_MSGFLAGS_QUEUESYSTEM) );
DPFX(DPFPREP, 5,"Received USER message");
DPLReceiveUserMessage(pdpLobbyObject,hSender,pBuffer,dwBufferSize);
}
hResultCode = DPN_OK;
DPFX(DPFPREP, 3,"Returning: [0x%lx]",hResultCode);
return(hResultCode);
}
// DPLSendConnectionSettings
//
// This function is used to send a connection settings update message
#undef DPF_MODNAME
#define DPF_MODNAME "DPLSendConnectionSettings"
HRESULT DPLSendConnectionSettings( DIRECTPLAYLOBBYOBJECT * const pdpLobbyObject,
const DPNHANDLE hConnection )
{
BYTE *pbTransmitBuffer = NULL;
DWORD dwTransmitBufferSize = 0;
DPL_INTERNAL_CONNECTION_SETTINGS_UPDATE *pdplMsgSettings = NULL;
DPL_CONNECTION *pdplConnection = NULL;
CPackedBuffer PackBuffer;
HRESULT hResultCode = DPN_OK;
hResultCode = DPLConnectionFind(pdpLobbyObject, hConnection, &pdplConnection, TRUE );
if( FAILED( hResultCode ) )
{
DPFERR( "Unable to find specified connection" );
return hResultCode;
}
// Grab lock to prevent other people from interfering
DNEnterCriticalSection( &pdplConnection->csLock );
PackBuffer.Initialize( NULL, 0 );
PackBuffer.AddToFront( NULL, sizeof( DPL_INTERNAL_CONNECTION_SETTINGS_UPDATE_HEADER ) );
if( pdplConnection->pConnectionSettings )
{
pdplConnection->pConnectionSettings->BuildWireStruct( &PackBuffer );
}
dwTransmitBufferSize = PackBuffer.GetSizeRequired();
pbTransmitBuffer = new BYTE[ dwTransmitBufferSize ];
if( !pbTransmitBuffer )
{
DPFX( DPFPREP, 0, "Error allocating memory" );
hResultCode = DPNERR_OUTOFMEMORY;
goto DPLSENDCONNECTSETTINGS_DONE;
}
pdplMsgSettings = (DPL_INTERNAL_CONNECTION_SETTINGS_UPDATE *) pbTransmitBuffer;
PackBuffer.Initialize( pbTransmitBuffer, dwTransmitBufferSize );
DNASSERT( pdplMsgSettings );
hResultCode = PackBuffer.AddToFront( NULL, sizeof( DPL_INTERNAL_CONNECTION_SETTINGS_UPDATE_HEADER ) );
if( FAILED( hResultCode ) )
{
DPFX( DPFPREP, 0, "Error adding main struct hr [0x%x]", hResultCode );
goto DPLSENDCONNECTSETTINGS_DONE;
}
if( pdplConnection->pConnectionSettings )
{
hResultCode = pdplConnection->pConnectionSettings->BuildWireStruct( &PackBuffer );
if( FAILED( hResultCode ) )
{
DPFX( DPFPREP, 0, "Error adding connect struct hr [0x%x]", hResultCode );
goto DPLSENDCONNECTSETTINGS_DONE;
}
pdplMsgSettings->dwConnectionSettingsSize = 1;
}
else
{
pdplMsgSettings->dwConnectionSettingsSize = 0;
}
pdplMsgSettings->dwMsgId = DPL_MSGID_INTERNAL_CONNECTION_SETTINGS;
if (!pdplConnection->pSendQueue->IsReceiving())
{
DPFERR("Other side is not receiving");
goto DPLSENDCONNECTSETTINGS_DONE;
}
hResultCode = pdplConnection->pSendQueue->Send(reinterpret_cast<BYTE*>(pdplMsgSettings),
PackBuffer.GetSizeRequired(),
INFINITE,
DPL_MSGQ_MSGFLAGS_USER1,
0);
if ( FAILED( hResultCode ) )
{
DPFX(DPFPREP, 0, "Could not send connect settings hr [0x%x]", hResultCode );
goto DPLSENDCONNECTSETTINGS_DONE;
}
hResultCode = DPN_OK;
DPLSENDCONNECTSETTINGS_DONE:
if( pbTransmitBuffer )
delete [] pbTransmitBuffer;
DNLeaveCriticalSection( &pdplConnection->csLock );
DPLConnectionRelease(pdpLobbyObject,hConnection);
return hResultCode;
}
// DPLUpdateConnectionSettings
//
// This function is called when a connection settings update message has been received.
//
#undef DPF_MODNAME
#define DPF_MODNAME "DPLUpdateConnectionSettings"
HRESULT DPLUpdateConnectionSettings(DIRECTPLAYLOBBYOBJECT *const pdpLobbyObject,
const DPNHANDLE hSender,
BYTE *const pBuffer )
{
HRESULT hr;
DPL_MESSAGE_CONNECTION_SETTINGS MsgConnectionSettings;
DWORD dwSettingsBufferSize = 0;
BOOL fAddressReferences = FALSE;
CConnectionSettings *pConnectionSettings = NULL;
DPL_INTERNAL_CONNECTION_SETTINGS_UPDATE *pConnectionSettingsMsg = NULL;
DPFX(DPFPREP, 3,"Parameters: pBuffer [0x%p]",pBuffer);
DNASSERT(pdpLobbyObject != NULL);
DNASSERT(pBuffer != NULL);
pConnectionSettingsMsg = (DPL_INTERNAL_CONNECTION_SETTINGS_UPDATE *) pBuffer;
if( pConnectionSettingsMsg->dwConnectionSettingsSize )
{
pConnectionSettings = new CConnectionSettings();
if( !pConnectionSettings )
{
DPFX( DPFPREP, 0, "Error allocating connection settings" );
hr = DPNERR_OUTOFMEMORY;
goto UPDATESETTINGS_FAILURE;
}
hr = pConnectionSettings->Initialize( &pConnectionSettingsMsg->dplConnectionSettings, (UNALIGNED BYTE *) pConnectionSettingsMsg );
if( FAILED( hr ) )
{
DPFX( DPFPREP, 0, "Error building structure from wire struct hr [0x%x]", hr );
goto UPDATESETTINGS_FAILURE;
}
}
// Set the connection settings on the object
hr = DPLConnectionSetConnectSettings( pdpLobbyObject, hSender, pConnectionSettings );
if( FAILED( hr ) )
{
DPFX(DPFPREP, 0, "Error setting connection settings hr = 0x%x", hr );
goto UPDATESETTINGS_FAILURE;
}
// Setup message to indicate to user
MsgConnectionSettings.dwSize = sizeof(DPL_MESSAGE_CONNECTION_SETTINGS);
MsgConnectionSettings.hSender = hSender;
if( pConnectionSettings )
MsgConnectionSettings.pdplConnectionSettings = pConnectionSettings->GetConnectionSettings();
else
MsgConnectionSettings.pdplConnectionSettings = NULL;
hr = DPLConnectionGetContext( pdpLobbyObject, hSender, &MsgConnectionSettings.pvConnectionContext );
if( FAILED( hr ) )
{
DPFX(DPFPREP, 0, "Error getting connection's context value" );
goto UPDATESETTINGS_FAILURE;
}
hr = (pdpLobbyObject->pfnMessageHandler)(pdpLobbyObject->pvUserContext,
DPL_MSGID_CONNECTION_SETTINGS,
reinterpret_cast<BYTE*>(&MsgConnectionSettings));
if( FAILED( hr ) )
{
DPFX(DPFPREP, 1, "Error returned from user callback -- ignored hr [0x%x]", hr );
}
return DPN_OK;
UPDATESETTINGS_FAILURE:
if( pConnectionSettings )
delete pConnectionSettings;
return hr;
}