/*==========================================================================
 *
 *  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;
}