/*========================================================================== * * Copyright (C) 2000 Microsoft Corporation. All Rights Reserved. * * File: DPLApp.cpp * Content: DirectPlay Lobbied Application Functions *@@BEGIN_MSINTERNAL * History: * Date By Reason * ==== == ====== * 02/21/00 mjn Created * 03/22/2000 jtk Changed interface names * 04/18/2000 rmt Added additional parameter validation * 04/25/2000 rmt Bug #s 33138, 33145, 33150 * 04/26/00 mjn Removed dwTimeOut from Send() API call * 05/03/00 rmt DPL_UnRegister was not implemented!! * 05/08/00 rmt Bug #34301 - Add flag to SetAppAvail to allow for multiple connects * 06/15/00 rmt Bug #33617 - Must provide method for providing automatic launch of DirectPlay instances * 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/14/2000 rmt Bug #39257 - LobbyClient::ReleaseApp returns E_OUTOFMEMORY when called when no one connected * rmt Bug #39487 - Remove WaitForConnect * 08/05/2000 RichGr IA64: Use %p format specifier in DPFs for 32/64-bit pointers and handles. * 08/15/2000 rmt Bug #42273 - DPLAY8: Samples sometimes get a DPNERR_ALREADYREGISTERED error. (Double connections) * 08/18/2000 rmt Bug #42751 - DPLOBBY8: Prohibit more than one lobby client or lobby app per process *@@END_MSINTERNAL * ***************************************************************************/ #include "dnlobbyi.h" //********************************************************************** // Macro definitions //********************************************************************** //********************************************************************** // Structure definitions //********************************************************************** //********************************************************************** // Variable definitions //********************************************************************** typedef STDMETHODIMP AppQueryInterface(IDirectPlay8LobbiedApplication *pInterface,REFIID ridd,PVOID *ppvObj); typedef STDMETHODIMP_(ULONG) AppAddRef(IDirectPlay8LobbiedApplication *pInterface); typedef STDMETHODIMP_(ULONG) AppRelease(IDirectPlay8LobbiedApplication *pInterface); typedef STDMETHODIMP AppRegisterMessageHandler(IDirectPlay8LobbiedApplication *pInterface,const PVOID pvUserContext,const PFNDPNMESSAGEHANDLER pfn, DPNHANDLE * const pdpnhConnection, const DWORD dwFlags); typedef STDMETHODIMP AppSend(IDirectPlay8LobbiedApplication *pInterface,const DPNHANDLE hTarget,BYTE *const pBuffer,const DWORD pBufferSize,const DWORD dwFlags); typedef STDMETHODIMP AppClose(IDirectPlay8LobbiedApplication *pInterface, const DWORD dwFlags); typedef STDMETHODIMP AppGetConnectionSettings(IDirectPlay8LobbiedApplication *pInterface, const DPNHANDLE hLobbyClient, DPL_CONNECTION_SETTINGS * const pdplSessionInfo, DWORD *pdwInfoSize, const DWORD dwFlags ); typedef STDMETHODIMP AppSetConnectionSettings(IDirectPlay8LobbiedApplication *pInterface, const DPNHANDLE hTarget, const DPL_CONNECTION_SETTINGS * const pdplSessionInfo, const DWORD dwFlags ); IDirectPlay8LobbiedApplicationVtbl DPL_8LobbiedApplicationVtbl = { (AppQueryInterface*) DPL_QueryInterface, (AppAddRef*) DPL_AddRef, (AppRelease*) DPL_Release, (AppRegisterMessageHandler*) DPL_RegisterMessageHandler, DPL_RegisterProgram, DPL_UnRegisterProgram, (AppSend*) DPL_Send, DPL_SetAppAvailable, DPL_UpdateStatus, (AppClose*) DPL_Close, (AppGetConnectionSettings*) DPL_GetConnectionSettings, (AppSetConnectionSettings*) DPL_SetConnectionSettings }; //********************************************************************** // Function prototypes //********************************************************************** //********************************************************************** // Function definitions //********************************************************************** #undef DPF_MODNAME #define DPF_MODNAME "DPL_RegisterProgram" STDMETHODIMP DPL_RegisterProgram(IDirectPlay8LobbiedApplication *pInterface, DPL_PROGRAM_DESC *const pdplProgramDesc, const DWORD dwFlags) { HRESULT hResultCode; DIRECTPLAYLOBBYOBJECT *pdpLobbyObject; DPFX(DPFPREP, 3,"Parameters: pInterface [0x%p], pdplProgramDesc [0x%p], dwFlags [0x%lx]", pInterface,pdplProgramDesc,dwFlags); TRY { pdpLobbyObject = static_cast(GET_OBJECT_FROM_INTERFACE(pInterface)); if( pdpLobbyObject->dwFlags & DPL_OBJECT_FLAG_PARAMVALIDATION ) { if( FAILED( hResultCode = DPL_ValidateRegisterProgram( pInterface , pdplProgramDesc, dwFlags ) ) ) { DPFX(DPFPREP, 0, "Error validating register params hr=[0x%lx]", hResultCode ); DPF_RETURN( hResultCode ); } } } EXCEPT(EXCEPTION_EXECUTE_HANDLER) { DPFERR("Invalid object" ); DPF_RETURN(DPNERR_INVALIDOBJECT); } hResultCode = DPLWriteProgramDesc(pdplProgramDesc); DPF_RETURN(hResultCode); } #undef DPF_MODNAME #define DPF_MODNAME "DPL_UnRegisterProgram" STDMETHODIMP DPL_UnRegisterProgram(IDirectPlay8LobbiedApplication *pInterface, GUID *const pGuidApplication, const DWORD dwFlags) { HRESULT hResultCode; DIRECTPLAYLOBBYOBJECT *pdpLobbyObject; DPFX(DPFPREP, 3,"Parameters: pInterface [0x%p], pGuidApplication [0x%p], dwFlags [0x%lx]", pInterface,pGuidApplication,dwFlags); TRY { pdpLobbyObject = static_cast(GET_OBJECT_FROM_INTERFACE(pInterface)); if( pdpLobbyObject->dwFlags & DPL_OBJECT_FLAG_PARAMVALIDATION ) { if( FAILED( hResultCode = DPL_ValidateUnRegisterProgram( pInterface , pGuidApplication, dwFlags ) ) ) { DPFX(DPFPREP, 0, "Error validating unregister params hr=[0x%lx]", hResultCode ); DPF_RETURN( hResultCode ); } } } EXCEPT(EXCEPTION_EXECUTE_HANDLER) { DPFERR("Invalid object" ); DPF_RETURN(DPNERR_INVALIDOBJECT); } hResultCode = DPLDeleteProgramDesc( pGuidApplication ); DPF_RETURN(hResultCode); } #undef DPF_MODNAME #define DPF_MODNAME "DPL_SetAppAvailable" STDMETHODIMP DPL_SetAppAvailable(IDirectPlay8LobbiedApplication *pInterface, const BOOL fAvailable, const DWORD dwFlags ) { DIRECTPLAYLOBBYOBJECT *pdpLobbyObject; HRESULT hResultCode; DPFX(DPFPREP, 3,"Parameters: (none)"); TRY { pdpLobbyObject = static_cast(GET_OBJECT_FROM_INTERFACE(pInterface)); if( pdpLobbyObject->dwFlags & DPL_OBJECT_FLAG_PARAMVALIDATION ) { if( FAILED( hResultCode = DPL_ValidateSetAppAvailable( pInterface, fAvailable, dwFlags ) ) ) { DPFX(DPFPREP, 0, "Error validating makeappavail 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); } if( fAvailable ) { // Indicate that we are waiting pdpLobbyObject->pReceiveQueue->MakeAvailable(); if( dwFlags & DPLAVAILABLE_ALLOWMULTIPLECONNECT ) { pdpLobbyObject->dwFlags |= DPL_OBJECT_FLAG_MULTICONNECT; } else { pdpLobbyObject->dwFlags &= ~(DPL_OBJECT_FLAG_MULTICONNECT); } } else { pdpLobbyObject->pReceiveQueue->MakeUnavailable(); } hResultCode = DPN_OK; DPF_RETURN(hResultCode); } // DPL_UpdateStatus // // Send session status information to the lobby client. This should be called whenever // the lobbied application connects to the game, fails to connect, disconnects, or is // terminated (booted). #undef DPF_MODNAME #define DPF_MODNAME "DPL_UpdateStatus" STDMETHODIMP DPL_UpdateStatus(IDirectPlay8LobbiedApplication *pInterface, const DPNHANDLE hLobbyClient, const DWORD dwStatus, const DWORD dwFlags ) { HRESULT hResultCode; DIRECTPLAYLOBBYOBJECT *pdpLobbyObject; DPL_CONNECTION *pdplConnection; DPL_INTERNAL_MESSAGE_UPDATE_STATUS Msg; DPNHANDLE *hTargets = NULL; DWORD dwNumTargets = 0; DWORD dwTargetIndex = 0; DPFX(DPFPREP, 3,"Parameters: pInterface [0x%p], hLobbyClient [0x%lx], dwStatus [0x%lx]", pInterface,hLobbyClient,dwStatus); TRY { pdpLobbyObject = static_cast(GET_OBJECT_FROM_INTERFACE(pInterface)); if( pdpLobbyObject->dwFlags & DPL_OBJECT_FLAG_PARAMVALIDATION ) { if( FAILED( hResultCode = DPL_ValidateUpdateStatus( pInterface, hLobbyClient, dwStatus, dwFlags ) ) ) { DPFX(DPFPREP, 0, "Error validating updatestatus 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); } Msg.dwMsgId = DPL_MSGID_INTERNAL_UPDATE_STATUS; Msg.dwStatus = dwStatus; 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" ); hResultCode = DPNERR_OUTOFMEMORY; dwNumTargets = 0; 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" ); hResultCode = DPNERR_OUTOFMEMORY; dwNumTargets = 0; goto EXIT_AND_CLEANUP; } 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); hResultCode = DPNERR_INVALIDHANDLE; goto EXIT_AND_CLEANUP; } DNASSERT(pdplConnection->pSendQueue != NULL); if (!pdplConnection->pSendQueue->IsReceiving()) { DPFERR("Other side is not listening"); hResultCode = DPNERR_INVALIDHANDLE; goto EXIT_AND_CLEANUP; } hResultCode = pdplConnection->pSendQueue->Send(reinterpret_cast(&Msg), sizeof(DPL_INTERNAL_MESSAGE_UPDATE_STATUS), INFINITE, DPL_MSGQ_MSGFLAGS_USER1, 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 "DPLAttemptLobbyConnection" HRESULT DPLAttemptLobbyConnection(DIRECTPLAYLOBBYOBJECT *const pdpLobbyObject) { PSTR pszCommandLine; char *c; DWORD dwCommandLineSize; CHAR pszObjectName[(sizeof(DWORD)*2)*2 + 1 + 1]; HANDLE hSyncEvent; HRESULT hResultCode; HANDLE hFileMap; DPL_SHARED_CONNECT_BLOCK *pSharedBlock; DWORD dwError; DWORD dwReturnValue; DPFX(DPFPREP, 3,"Parameters: (none)"); // Need a copy of the command line dwCommandLineSize = strlen(GetCommandLineA()) + 1; if ((pszCommandLine = static_cast(DNMalloc(dwCommandLineSize))) == NULL) { return(DPNERR_NORESPONSE); } strcpy(pszCommandLine,GetCommandLineA()); DPFX(DPFPREP, 5,"Got command line [%s]",pszCommandLine); // Try to find Lobby Launch ID string c = strstr(pszCommandLine,DPL_ID_STR_A); if (c == NULL) { DNFree(pszCommandLine); return(DPNERR_NORESPONSE); } c += strlen(DPL_ID_STR_A); c--; strncpy(pszObjectName,c,(sizeof(DWORD)*2)*2 + 1); pszObjectName[(sizeof(DWORD)*2)*2 + 1] = '\0'; // Ensure null terminated DPFX(DPFPREP, 5,"Got object name [%s]",pszObjectName); DNFree(pszCommandLine); // Try to open shared memory *pszObjectName = DPL_MSGQ_OBJECT_IDCHAR_FILEMAP; hFileMap = CreateFileMappingA(INVALID_HANDLE_VALUE,(LPSECURITY_ATTRIBUTES) NULL, PAGE_READWRITE,(DWORD)0,sizeof(DPL_SHARED_CONNECT_BLOCK),pszObjectName); if (hFileMap == NULL) { DPFERR("CreateFileMapping() failed"); dwError = GetLastError(); DNASSERT(FALSE); return(DPNERR_NORESPONSE); } // Ensure it existed already dwError = GetLastError(); if (dwError != ERROR_ALREADY_EXISTS) { DPFERR("File mapping did not already exist"); DNASSERT(FALSE); CloseHandle(hFileMap); return(DPNERR_NORESPONSE); } // Map file pSharedBlock = reinterpret_cast(MapViewOfFile(hFileMap,FILE_MAP_ALL_ACCESS,0,0,0)); if (pSharedBlock == NULL) { DPFERR("MapViewOfFile() failed"); dwError = GetLastError(); DNASSERT(FALSE); CloseHandle(hFileMap); return(DPNERR_NORESPONSE); } // Try to open connection event *pszObjectName = DPL_MSGQ_OBJECT_IDCHAR_EVENT; hSyncEvent = OpenEventA(EVENT_MODIFY_STATE,FALSE,pszObjectName); if (hSyncEvent == NULL) { DPFERR("OpenEvent() failed"); dwError = GetLastError(); DNASSERT(FALSE); UnmapViewOfFile(pSharedBlock); CloseHandle(hFileMap); return(DPNERR_NORESPONSE); } DPFX(DPFPREP, 5,"Opened sync event"); ResetEvent(pdpLobbyObject->hConnectEvent); // Look for lobby launch -- set lobby launch value if connection is received pdpLobbyObject->dwFlags |= DPL_OBJECT_FLAG_LOOKINGFORLOBBYLAUNCH; // Make application available for connection by lobby client DNASSERT(pdpLobbyObject->pReceiveQueue != NULL); // Signal lobby client pSharedBlock->dwPID = pdpLobbyObject->dwPID; SetEvent(hSyncEvent); dwReturnValue = WaitForSingleObject(pdpLobbyObject->hConnectEvent,DPL_LOBBYLAUNCHED_CONNECT_TIMEOUT); // Turn off the looking for lobby launch flag pdpLobbyObject->dwFlags &= ~(DPL_OBJECT_FLAG_LOOKINGFORLOBBYLAUNCH); if (dwReturnValue == WAIT_OBJECT_0) hResultCode = DPN_OK; else hResultCode = DPNERR_TIMEDOUT; // Clean up CloseHandle(hSyncEvent); UnmapViewOfFile(pSharedBlock); CloseHandle(hFileMap); DPFX(DPFPREP, 3,"Returning: [0x%lx]",hResultCode); return(hResultCode); } //------------------------------------------------------------------------