/*========================================================================== * * 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(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(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(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(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(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(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(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(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(&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(pvContext); pdwMsgId = reinterpret_cast(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(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(&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; }