/*========================================================================== * * Copyright (C) 2000 Microsoft Corporation. All Rights Reserved. * * File: DNLClient.cpp * Content: DirectNet Lobby Client Functions *@@BEGIN_MSINTERNAL * History: * Date By Reason * ==== == ====== * 02/21/00 mjn Created * 03/22/2000 jtk Changed interface names * 04/05/2000 jtk Changed GetValueSize to GetValueLength * 04/13/00 rmt First pass param validation * 04/25/2000 rmt Bug #s 33138, 33145, 33150 * 04/26/00 mjn Removed dwTimeOut from Send() API call * 05/01/2000 rmt Bug #33678 * 05/03/00 rmt Bug #33879 -- Status messsage missing from field * 05/30/00 rmt Bug #35618 -- ConnectApp with ShortTimeout returns DPN_OK * 06/07/00 rmt Bug #36452 -- Calling ConnectApplication twice could result in disconnection * 06/15/00 rmt Bug #33617 - Must provide method for providing automatic launch of DirectPlay instances * 07/06/00 rmt Updated for new registry parameters * 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 * 07/21/2000 rmt Bug #39578 - LobbyClient sample errors and quits -- memory corruption due to length vs. size problem * 08/05/2000 RichGr IA64: Use %p format specifier in DPFs for 32/64-bit pointers and handles. * 12/15/2000 rmt Bug #48445 - Specifying empty launcher name results in error * 04/19/2001 simonpow Bug #369842 - Altered CreateProcess calls to take app name and cmd * line as 2 separate arguments rather than one. * 06/16/2001 rodtoll WINBUG #416983 - RC1: World has full control to HKLM\Software\Microsoft\DirectPlay\Applications on Personal * Implementing mirror of keys into HKCU. Algorithm is now: * - Read of entries tries HKCU first, then HKLM * - Enum of entires is combination of HKCU and HKLM entries with duplicates removed. HKCU takes priority. * - Write of entries is HKLM and HKCU. (HKLM may fail, but is ignored). *@@END_MSINTERNAL * ***************************************************************************/ #include "dnlobbyi.h" //********************************************************************** // Constant definitions //********************************************************************** //********************************************************************** // Macro definitions //********************************************************************** //********************************************************************** // Structure definitions //********************************************************************** //********************************************************************** // Variable definitions //********************************************************************** typedef STDMETHODIMP ClientQueryInterface(IDirectPlay8LobbyClient *pInterface,REFIID ridd,PVOID *ppvObj); typedef STDMETHODIMP_(ULONG) ClientAddRef(IDirectPlay8LobbyClient *pInterface); typedef STDMETHODIMP_(ULONG) ClientRelease(IDirectPlay8LobbyClient *pInterface); typedef STDMETHODIMP ClientRegisterMessageHandler(IDirectPlay8LobbyClient *pInterface,const PVOID pvUserContext,const PFNDPNMESSAGEHANDLER pfn,const DWORD dwFlags); typedef STDMETHODIMP ClientSend(IDirectPlay8LobbyClient *pInterface,const DPNHANDLE hTarget,BYTE *const pBuffer,const DWORD pBufferSize,const DWORD dwFlags); typedef STDMETHODIMP ClientClose(IDirectPlay8LobbyClient *pInterface,const DWORD dwFlags); typedef STDMETHODIMP ClientGetConnectionSettings(IDirectPlay8LobbyClient *pInterface, const DPNHANDLE hLobbyClient, DPL_CONNECTION_SETTINGS * const pdplSessionInfo, DWORD *pdwInfoSize, const DWORD dwFlags ); typedef STDMETHODIMP ClientSetConnectionSettings(IDirectPlay8LobbyClient *pInterface, const DPNHANDLE hTarget, const DPL_CONNECTION_SETTINGS * const pdplSessionInfo, const DWORD dwFlags ); IDirectPlay8LobbyClientVtbl DPL_Lobby8ClientVtbl = { (ClientQueryInterface*) DPL_QueryInterface, (ClientAddRef*) DPL_AddRef, (ClientRelease*) DPL_Release, (ClientRegisterMessageHandler*) DPL_RegisterMessageHandlerClient, DPL_EnumLocalPrograms, DPL_ConnectApplication, (ClientSend*) DPL_Send, DPL_ReleaseApplication, (ClientClose*) DPL_Close, (ClientGetConnectionSettings*) DPL_GetConnectionSettings, (ClientSetConnectionSettings*) DPL_SetConnectionSettings }; //********************************************************************** // Function prototypes //********************************************************************** //********************************************************************** // Function definitions //********************************************************************** #define DPL_ENUM_APPGUID_BUFFER_INITIAL 8 #define DPL_ENUM_APPGUID_BUFFER_GROWBY 4 #undef DPF_MODNAME #define DPF_MODNAME "DPL_EnumLocalPrograms" STDMETHODIMP DPL_EnumLocalPrograms(IDirectPlay8LobbyClient *pInterface, GUID *const pGuidApplication, BYTE *const pEnumData, DWORD *const pdwEnumDataSize, DWORD *const pdwEnumDataItems, const DWORD dwFlags ) { HRESULT hResultCode; CMessageQueue MessageQueue; CPackedBuffer PackedBuffer; CRegistry RegistryEntry; CRegistry SubEntry; DWORD dwSizeRequired; DWORD dwMaxKeyLen; PWSTR pwszKeyName = NULL; // Application name variables PWSTR pwszApplicationName = NULL; DWORD dwMaxApplicationNameLength; // Includes null terminator DWORD dwApplicationNameLength; // Includes null terminator // Executable name variables PWSTR pwszExecutableFilename = NULL; DWORD dwMaxExecutableFilenameLength; // Includes null terminator DWORD dwExecutableFilenameLength; // Includes null terminator DWORD *pdwPID; DWORD dwMaxPID; DWORD dwNumPID; DWORD dwEnumIndex; DWORD dwEnumCount; DWORD dwKeyLen; DWORD dw; DPL_APPLICATION_INFO dplAppInfo; DIRECTPLAYLOBBYOBJECT *pdpLobbyObject; GUID *pAppLoadedList = NULL; // List of GUIDs of app's we've enumerated DWORD dwSizeAppLoadedList = 0; // size of list pAppLoadedList DWORD dwLengthAppLoadedList = 0; // # of elements in list HKEY hkCurrentBranch = HKEY_LOCAL_MACHINE; DPFX(DPFPREP, 3,"Parameters: pInterface [0x%p], pGuidApplication [0x%p], pEnumData [0x%p], pdwEnumDataSize [0x%p], pdwEnumDataItems [0x%p], dwFlags [0x%lx]", pInterface,pGuidApplication,pEnumData,pdwEnumDataSize,pdwEnumDataItems,dwFlags); TRY { pdpLobbyObject = static_cast(GET_OBJECT_FROM_INTERFACE(pInterface)); if( pdpLobbyObject->dwFlags & DPL_OBJECT_FLAG_PARAMVALIDATION ) { if( FAILED( hResultCode = DPL_ValidateEnumLocalPrograms( pInterface, pGuidApplication, pEnumData, pdwEnumDataSize, pdwEnumDataItems, dwFlags ) ) ) { DPFX(DPFPREP, 0, "Error validating enum local programs 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); } dwSizeRequired = *pdwEnumDataSize; PackedBuffer.Initialize(pEnumData,dwSizeRequired); pwszApplicationName = NULL; pwszExecutableFilename = NULL; pdwPID = NULL; dwMaxPID = 0; dwLengthAppLoadedList = 0; dwSizeAppLoadedList = DPL_ENUM_APPGUID_BUFFER_INITIAL; pAppLoadedList = static_cast(DNMalloc(sizeof(GUID)*dwSizeAppLoadedList)); if( !pAppLoadedList ) { DPFERR("Failed allocating memory" ); hResultCode = DPNERR_OUTOFMEMORY; goto EXIT_DPL_EnumLocalPrograms; } dwEnumCount = 0; for( DWORD dwIndex = 0; dwIndex < 2; dwIndex++ ) { if( dwIndex == 0 ) { hkCurrentBranch = HKEY_CURRENT_USER; } else { hkCurrentBranch = HKEY_LOCAL_MACHINE; } if (!RegistryEntry.Open(hkCurrentBranch,DPL_REG_LOCAL_APPL_SUBKEY,TRUE,FALSE,TRUE,DPL_REGISTRY_READ_ACCESS)) { DPFX(DPFPREP,1,"On pass %i could not find app key", dwIndex); continue; } // Set up to enumerate if (!RegistryEntry.GetMaxKeyLen(dwMaxKeyLen)) { DPFERR("RegistryEntry.GetMaxKeyLen() failed"); hResultCode = DPNERR_GENERIC; goto EXIT_DPL_EnumLocalPrograms; } dwMaxKeyLen++; // Null terminator DPFX(DPFPREP, 7,"dwMaxKeyLen = %ld",dwMaxKeyLen); if ((pwszKeyName = static_cast(DNMalloc(dwMaxKeyLen*sizeof(WCHAR)))) == NULL) { DPFERR("DNMalloc() failed"); hResultCode = DPNERR_OUTOFMEMORY; goto EXIT_DPL_EnumLocalPrograms; } dwMaxApplicationNameLength = dwMaxKeyLen * sizeof(WCHAR); dwMaxExecutableFilenameLength = dwMaxApplicationNameLength; if ((pwszApplicationName = static_cast(DNMalloc(dwMaxApplicationNameLength*sizeof(WCHAR)))) == NULL) // Seed Application name size { DPFERR("DNMalloc() failed"); hResultCode = DPNERR_OUTOFMEMORY; goto EXIT_DPL_EnumLocalPrograms; } if ((pwszExecutableFilename = static_cast(DNMalloc(dwMaxExecutableFilenameLength*sizeof(WCHAR)))) == NULL) { DPFERR("DNMalloc() failed"); hResultCode = DPNERR_OUTOFMEMORY; goto EXIT_DPL_EnumLocalPrograms; } dwEnumIndex = 0; dwKeyLen = dwMaxKeyLen; // Enumerate ! while (RegistryEntry.EnumKeys(pwszKeyName,&dwKeyLen,dwEnumIndex)) { DPFX(DPFPREP, 7,"%ld - %S (%ld)",dwEnumIndex,pwszKeyName,dwKeyLen); // Get Application name and GUID from each sub key if (!SubEntry.Open(RegistryEntry,pwszKeyName,TRUE,FALSE)) { DPFX(DPFPREP, 7,"skipping %S",pwszKeyName); goto LOOP_END; } // // Minara, double-check size vs. length for names // if (!SubEntry.GetValueLength(DPL_REG_KEYNAME_APPLICATIONNAME,&dwApplicationNameLength)) { DPFX(DPFPREP, 7,"Could not get ApplicationName size. Skipping [%S]",pwszKeyName); goto LOOP_END; } // To include null terminator dwApplicationNameLength++; if (dwApplicationNameLength > dwMaxApplicationNameLength) { // grow buffer (taking into account that the reg functions always return WCHAR) and try again DPFX(DPFPREP, 7,"Need to grow pwszApplicationName from %ld to %ld",dwMaxApplicationNameLength,dwApplicationNameLength); if (pwszApplicationName != NULL) { DNFree(pwszApplicationName); pwszApplicationName = NULL; } if ((pwszApplicationName = static_cast(DNMalloc(dwApplicationNameLength*sizeof(WCHAR)))) == NULL) { DPFERR("DNMalloc() failed"); hResultCode = DPNERR_OUTOFMEMORY; goto EXIT_DPL_EnumLocalPrograms; } dwMaxApplicationNameLength = dwApplicationNameLength; } if (!SubEntry.ReadString(DPL_REG_KEYNAME_APPLICATIONNAME,pwszApplicationName,&dwApplicationNameLength)) { DPFX(DPFPREP, 7,"Could not read ApplicationName. Skipping [%S]",pwszKeyName); goto LOOP_END; } DPFX(DPFPREP, 7,"ApplicationName = %S (%ld WCHARs)",pwszApplicationName,dwApplicationNameLength); if (!SubEntry.ReadGUID(DPL_REG_KEYNAME_GUID,dplAppInfo.guidApplication)) { DPFERR("SubEntry.ReadGUID failed - skipping entry"); goto LOOP_END; } for( DWORD dwGuidSearchIndex = 0; dwGuidSearchIndex < dwLengthAppLoadedList; dwGuidSearchIndex++ ) { if( pAppLoadedList[dwGuidSearchIndex] == dplAppInfo.guidApplication ) { DPFX(DPFPREP, 1, "Ignoring local machine entry for current user version of entry [%S]", pwszApplicationName ); goto LOOP_END; } } if ((pGuidApplication == NULL) || (*pGuidApplication == dplAppInfo.guidApplication)) { // Get process count - need executable filename // // Minara, check size vs. length // if (!SubEntry.GetValueLength(DPL_REG_KEYNAME_EXECUTABLEFILENAME,&dwExecutableFilenameLength)) { DPFX(DPFPREP, 7,"Could not get ExecutableFilename size. Skipping [%S]",pwszKeyName); goto LOOP_END; } // So we include null terminator dwExecutableFilenameLength++; if (dwExecutableFilenameLength > dwMaxExecutableFilenameLength) { // grow buffer (noting that all strings from the registry are WCHAR) and try again DPFX(DPFPREP, 7,"Need to grow pwszExecutableFilename from %ld to %ld",dwMaxExecutableFilenameLength,dwExecutableFilenameLength); if (pwszExecutableFilename != NULL) { DNFree(pwszExecutableFilename); pwszExecutableFilename = NULL; } if ((pwszExecutableFilename = static_cast(DNMalloc(dwExecutableFilenameLength*sizeof(WCHAR)))) == NULL) { DPFERR("DNMalloc() failed"); hResultCode = DPNERR_OUTOFMEMORY; goto EXIT_DPL_EnumLocalPrograms; } dwMaxExecutableFilenameLength = dwExecutableFilenameLength; } if (!SubEntry.ReadString(DPL_REG_KEYNAME_EXECUTABLEFILENAME,pwszExecutableFilename,&dwExecutableFilenameLength)) { DPFX(DPFPREP, 7,"Could not read ExecutableFilename. Skipping [%S]",pwszKeyName); goto LOOP_END; } DPFX(DPFPREP, 7,"ExecutableFilename [%S]",pwszExecutableFilename); // Count running apps dwNumPID = dwMaxPID; while ((hResultCode = DPLGetProcessList(pwszExecutableFilename,pdwPID,&dwNumPID, pdpLobbyObject->bIsUnicodePlatform)) == DPNERR_BUFFERTOOSMALL) { if (pdwPID) { DNFree(pdwPID); pdwPID = NULL; } dwMaxPID = dwNumPID; if ((pdwPID = static_cast(DNMalloc(dwNumPID*sizeof(DWORD)))) == NULL) { DPFERR("DNMalloc() failed"); hResultCode = DPNERR_OUTOFMEMORY; goto EXIT_DPL_EnumLocalPrograms; } } if (hResultCode != DPN_OK) { DPFERR("DPLGetProcessList() failed"); DisplayDNError(0,hResultCode); hResultCode = DPNERR_GENERIC; goto EXIT_DPL_EnumLocalPrograms; } // Count waiting apps dplAppInfo.dwNumWaiting = 0; for (dw = 0 ; dw < dwNumPID ; dw++) { if ((hResultCode = MessageQueue.Open( pdwPID[dw], DPL_MSGQ_OBJECT_SUFFIX_APPLICATION, DPL_MSGQ_SIZE, DPL_MSGQ_OPEN_FLAG_NO_CREATE, INFINITE)) == DPN_OK) { if (MessageQueue.IsAvailable()) { dplAppInfo.dwNumWaiting++; } MessageQueue.Close(); } } hResultCode = PackedBuffer.AddWCHARStringToBack(pwszApplicationName); dplAppInfo.pwszApplicationName = (PWSTR)(PackedBuffer.GetTailAddress()); dplAppInfo.dwFlags = 0; dplAppInfo.dwNumRunning = dwNumPID; hResultCode = PackedBuffer.AddToFront(&dplAppInfo,sizeof(DPL_APPLICATION_INFO)); if( dwLengthAppLoadedList+1 > dwSizeAppLoadedList ) { GUID *pTmpArray = NULL; pTmpArray = static_cast(DNMalloc(sizeof(GUID)*(dwSizeAppLoadedList+DPL_ENUM_APPGUID_BUFFER_GROWBY))); if( !pTmpArray ) { DPFERR("DNMalloc() failed"); hResultCode = DPNERR_OUTOFMEMORY; goto EXIT_DPL_EnumLocalPrograms; } memcpy( pTmpArray, pAppLoadedList, sizeof(GUID)*dwLengthAppLoadedList); dwSizeAppLoadedList += DPL_ENUM_APPGUID_BUFFER_GROWBY; DNFree(pAppLoadedList); pAppLoadedList = pTmpArray; } pAppLoadedList[dwLengthAppLoadedList] = dplAppInfo.guidApplication; dwLengthAppLoadedList++; dwEnumCount++; } LOOP_END: SubEntry.Close(); dwEnumIndex++; dwKeyLen = dwMaxKeyLen; } RegistryEntry.Close(); if( pwszKeyName ) { DNFree(pwszKeyName); pwszKeyName= NULL; } if( pwszApplicationName ) { DNFree(pwszApplicationName); pwszApplicationName = NULL; } if( pwszExecutableFilename ) { DNFree(pwszExecutableFilename); pwszExecutableFilename = NULL; } } dwSizeRequired = PackedBuffer.GetSizeRequired(); if (dwSizeRequired > *pdwEnumDataSize) { DPFX(DPFPREP, 7,"Buffer too small"); *pdwEnumDataSize = dwSizeRequired; hResultCode = DPNERR_BUFFERTOOSMALL; } else { *pdwEnumDataItems = dwEnumCount; } if( pGuidApplication != NULL && dwEnumCount == 0 ) { DPFX(DPFPREP, 0, "Specified application was not registered" ); hResultCode = DPNERR_DOESNOTEXIST; } EXIT_DPL_EnumLocalPrograms: if (pwszKeyName != NULL) DNFree(pwszKeyName); if (pwszApplicationName != NULL) DNFree(pwszApplicationName); if (pwszExecutableFilename != NULL) DNFree(pwszExecutableFilename); if (pdwPID != NULL) DNFree(pdwPID); if( pAppLoadedList ) DNFree(pAppLoadedList); DPF_RETURN(hResultCode); } // DPL_ConnectApplication // // Try to connect to a lobbied application. Based on DPL_CONNECT_INFO flags, // we may have to launch an application. // // If we have to launch an application, we will need to handshake the PID of the // application (as it may be ripple launched). We will pass the LobbyClient's PID on the // command line to the application launcher and expect it to be passed down to the // application. The application will open a named shared memory block using the PID and // write its PID there, and then signal a named event (using the LobbyClient's PID again). // When the waiting LobbyClient is signaled by this event, it continues its connection // process as if this was an existing running and available application. #undef DPF_MODNAME #define DPF_MODNAME "DPL_ConnectApplication" STDMETHODIMP DPL_ConnectApplication(IDirectPlay8LobbyClient *pInterface, DPL_CONNECT_INFO *const pdplConnectInfo, void *pvConnectionContext, DPNHANDLE *const hApplication, const DWORD dwTimeOut, const DWORD dwFlags) { HRESULT hResultCode = DPN_OK; DWORD dwSize = 0; BYTE *pBuffer = NULL; DPL_PROGRAM_DESC *pdplProgramDesc; DWORD *pdwProcessList = NULL; DWORD dwNumProcesses = 0; DWORD dwPID = 0; DWORD dw = 0; DPNHANDLE handle = NULL; DPL_CONNECTION *pdplConnection = NULL; DIRECTPLAYLOBBYOBJECT *pdpLobbyObject = NULL; DPFX(DPFPREP, 3,"Parameters: pdplConnectInfo [0x%p], pvUserAppContext [0x%p], hApplication [0x%lx], dwFlags [0x%lx]", pdplConnectInfo,pvConnectionContext,hApplication,dwFlags); TRY { pdpLobbyObject = static_cast(GET_OBJECT_FROM_INTERFACE(pInterface)); if( pdpLobbyObject->dwFlags & DPL_OBJECT_FLAG_PARAMVALIDATION ) { if( FAILED( hResultCode = DPL_ValidateConnectApplication( pInterface, pdplConnectInfo, pvConnectionContext, hApplication, dwTimeOut, dwFlags ) ) ) { DPFX(DPFPREP, 0, "Error validating connect application 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); } // Get program description dwSize = 0; pBuffer = NULL; hResultCode = DPLGetProgramDesc(&pdplConnectInfo->guidApplication,pBuffer,&dwSize); if (hResultCode != DPNERR_BUFFERTOOSMALL) { DPFERR("Could not get Program Description"); goto EXIT_DPL_ConnectApplication; } if ((pBuffer = static_cast(DNMalloc(dwSize))) == NULL) { DPFERR("Could not allocate space for buffer"); hResultCode = DPNERR_OUTOFMEMORY; goto EXIT_DPL_ConnectApplication; } if ((hResultCode = DPLGetProgramDesc(&pdplConnectInfo->guidApplication,pBuffer,&dwSize)) != DPN_OK) { DPFERR("Could not get Program Description"); DisplayDNError(0,hResultCode); goto EXIT_DPL_ConnectApplication; } pdplProgramDesc = reinterpret_cast(pBuffer); dwPID = 0; dwNumProcesses = 0; pdwProcessList = NULL; if (!(pdplConnectInfo->dwFlags & DPLCONNECT_LAUNCHNEW)) // Only if not forcing launch { // Get process list hResultCode = DPLGetProcessList(pdplProgramDesc->pwszExecutableFilename,NULL,&dwNumProcesses, pdpLobbyObject->bIsUnicodePlatform); if (hResultCode != DPN_OK && hResultCode != DPNERR_BUFFERTOOSMALL) { DPFERR("Could not retrieve process list"); DisplayDNError(0,hResultCode); goto EXIT_DPL_ConnectApplication; } if (hResultCode == DPNERR_BUFFERTOOSMALL) { if ((pdwProcessList = static_cast(DNMalloc(dwNumProcesses*sizeof(DWORD)))) == NULL) { DPFERR("Could not create process list buffer"); hResultCode = DPNERR_OUTOFMEMORY; goto EXIT_DPL_ConnectApplication; } if ((hResultCode = DPLGetProcessList(pdplProgramDesc->pwszExecutableFilename,pdwProcessList, &dwNumProcesses,pdpLobbyObject->bIsUnicodePlatform)) != DPN_OK) { DPFERR("Could not get process list"); DisplayDNError(0,hResultCode); goto EXIT_DPL_ConnectApplication; } } // Try to connect to an already running application for (dw = 0 ; dw < dwNumProcesses ; dw++) { if ((hResultCode = DPLMakeApplicationUnavailable(pdwProcessList[dw])) == DPN_OK) { DPFX(DPFPREP, 1, "Found Existing Process=%d", pdwProcessList[dw] ); dwPID = pdwProcessList[dw]; break; } } if (pdwProcessList) { DNFree(pdwProcessList); pdwProcessList = NULL; } } // Launch application if none are ready to connect if ((dwPID == 0) && (pdplConnectInfo->dwFlags & (DPLCONNECT_LAUNCHNEW | DPLCONNECT_LAUNCHNOTFOUND))) { if ((hResultCode = DPLLaunchApplication(pdpLobbyObject,pdplProgramDesc,&dwPID,dwTimeOut)) != DPN_OK) { DPFERR("Could not launch application"); DisplayDNError(0,hResultCode); goto EXIT_DPL_ConnectApplication; } else { DPFX(DPFPREP, 1, "Launched process dwID=%d", dwPID ); } } if (dwPID == 0) // Could not make any connection { DPFERR("Could not connect to an existing application or launch a new one"); hResultCode = DPNERR_NOCONNECTION; DisplayDNError( 0, hResultCode ); goto EXIT_DPL_ConnectApplication; } handle = NULL; // Create connection if ((hResultCode = DPLConnectionNew(pdpLobbyObject,&handle,&pdplConnection)) != DPN_OK) { DPFERR("Could not create connection entry"); DisplayDNError(0,hResultCode); goto EXIT_DPL_ConnectApplication; } pdplConnection->dwTargetPID = dwPID; DPFX(DPFPREP, 0, "PID=%d", dwPID ); // Set the context for this connection if ((hResultCode = DPLConnectionSetContext( pdpLobbyObject, handle, pvConnectionContext )) != DPN_OK ) { DPFERR( "Could not set contect for connection" ); DisplayDNError(0,hResultCode); goto EXIT_DPL_ConnectApplication; } // Connect to selected application instance if ((hResultCode = DPLConnectionConnect(pdpLobbyObject,handle,dwPID,TRUE)) != DPN_OK) { DPFERR("Could not connect to application"); DisplayDNError(0,hResultCode); goto EXIT_DPL_ConnectApplication; } ResetEvent(pdplConnection->hConnectEvent); // Pass lobby client info to application if ((hResultCode = DPLConnectionSendREQ(pdpLobbyObject,handle,pdpLobbyObject->dwPID, pdplConnectInfo)) != DPN_OK) { DPFERR("Could not send connection request"); DisplayDNError(0,hResultCode); goto EXIT_DPL_ConnectApplication; } if (WaitForSingleObject(pdplConnection->hConnectEvent,INFINITE) != WAIT_OBJECT_0) { DPFERR("Wait for connection terminated"); hResultCode = DPNERR_GENERIC; goto EXIT_DPL_ConnectApplication; } *hApplication = handle; hResultCode = DPN_OK; EXIT_DPL_ConnectApplication: if( FAILED(hResultCode) && handle ) { DPLConnectionDisconnect(pdpLobbyObject,handle); DPLConnectionRelease(pdpLobbyObject,handle); } if (pBuffer) DNFree(pBuffer); if (pdwProcessList) DNFree(pdwProcessList); DPF_RETURN(hResultCode); } #undef DPF_MODNAME #define DPF_MODNAME "DPL_ReleaseApplication" STDMETHODIMP DPL_ReleaseApplication(IDirectPlay8LobbyClient *pInterface, const DPNHANDLE hApplication, const DWORD dwFlags ) { HRESULT hResultCode; DIRECTPLAYLOBBYOBJECT *pdpLobbyObject; DPNHANDLE *hTargets = NULL; DWORD dwNumTargets = 0; DWORD dwTargetIndex = 0; DPFX(DPFPREP, 3,"Parameters: hApplication [0x%lx]",hApplication); TRY { pdpLobbyObject = static_cast(GET_OBJECT_FROM_INTERFACE(pInterface)); if( pdpLobbyObject->dwFlags & DPL_OBJECT_FLAG_PARAMVALIDATION ) { if( FAILED( hResultCode = DPL_ValidateReleaseApplication( pInterface, hApplication, dwFlags ) ) ) { DPFX(DPFPREP, 0, "Error validating release application 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( hApplication == 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] = hApplication; } for( dwTargetIndex = 0; dwTargetIndex < dwNumTargets; dwTargetIndex++ ) { hResultCode = DPLConnectionDisconnect(pdpLobbyObject,hTargets[dwTargetIndex]); if( FAILED( hResultCode ) ) { DPFX(DPFPREP, 0, "Error disconnecting connection 0x%x hr=0x%x", hTargets[dwTargetIndex], hResultCode ); } } EXIT_AND_CLEANUP: if( hTargets ) delete [] hTargets; DPF_RETURN(hResultCode); } // DPLLaunchApplication // // Launch the application with a command-line argument of: // DPLID=PIDn PID=Lobby Client PID, n=launch counter (each launch increases it) // Wait for the application to signal the event (or die) #undef DPF_MODNAME #define DPF_MODNAME "DPLLaunchApplication" HRESULT DPLLaunchApplication(DIRECTPLAYLOBBYOBJECT *const pdpLobbyObject, DPL_PROGRAM_DESC *const pdplProgramDesc, DWORD *const pdwPID, const DWORD dwTimeOut) { HRESULT hResultCode; DWORD dwAppNameLen=0; //Length of the application full name (path+exe) PWSTR pwszAppName=NULL; //Unicode version of application full name DWORD dwCmdLineLen=0; //Length of the command line string PWSTR pwszCmdLine=NULL; //Unicode version of command line to supply CHAR * pszAppName=NULL; //Ascii version of application full name CHAR * pszCmdLine=NULL; //Acii version of command line string LONG lc; STARTUPINFOW siW; // Unicode startup info (place holder) STARTUPINFOA siA; PROCESS_INFORMATION pi; DWORD dwError; HANDLE hSyncEvents[2] = { NULL, NULL }; WCHAR pwszObjectName[(sizeof(DWORD)*2)*2 + 1]; CHAR pszObjectName[(sizeof(DWORD)*2)*2 + 1 + 1]; DPL_SHARED_CONNECT_BLOCK *pSharedBlock = NULL; HANDLE hFileMap = NULL; DWORD dwPID; CHAR *pszDefaultDir = NULL; WCHAR *wszToLaunchPath = NULL; WCHAR *wszToLaunchExecutable = NULL; DWORD dwToLaunchPathLen; // Are we launching the launcher or the executable? if( !pdplProgramDesc->pwszLauncherFilename || wcslen(pdplProgramDesc->pwszLauncherFilename) == 0 ) { wszToLaunchPath = pdplProgramDesc->pwszExecutablePath; wszToLaunchExecutable = pdplProgramDesc->pwszExecutableFilename; } else { wszToLaunchPath = pdplProgramDesc->pwszLauncherPath; wszToLaunchExecutable = pdplProgramDesc->pwszLauncherFilename; } DPFX(DPFPREP, 3,"Parameters: pdplProgramDesc [0x%p]",pdplProgramDesc); DNASSERT(pdplProgramDesc != NULL); // Increment launch count lc = InterlockedIncrement(&pdpLobbyObject->lLaunchCount); // Synchronization event and shared memory names swprintf(pwszObjectName,L"%lx%lx",pdpLobbyObject->dwPID,lc); sprintf(pszObjectName,"-%lx%lx",pdpLobbyObject->dwPID,lc); // Compute the size of the full application name string (combination of path and exe name) if (wszToLaunchPath) dwAppNameLen += (wcslen(wszToLaunchPath) + 1); if (wszToLaunchExecutable) dwAppNameLen += (wcslen(wszToLaunchExecutable) + 1); // Compute the size of the command line string dwCmdLineLen=dwAppNameLen+1; if (pdplProgramDesc->pwszCommandLine) dwCmdLineLen += wcslen(pdplProgramDesc->pwszCommandLine); dwCmdLineLen += (1 + wcslen(DPL_ID_STR_W) + (sizeof(DWORD)*2*2) + 1); DPFX(DPFPREP, 5,"Application full name string length [%ld] WCHARs", dwAppNameLen); DPFX(DPFPREP, 5,"Command Line string length [%ld] WCHARs", dwCmdLineLen); // Allocate memory to hold the full app name and command line + check allocation was OK pwszAppName=static_cast(DNMalloc(dwAppNameLen * sizeof(WCHAR))); pwszCmdLine=static_cast(DNMalloc(dwCmdLineLen * sizeof(WCHAR))); if (pwszAppName==NULL || pwszCmdLine==NULL) { DPFERR("Could not allocate strings for app name and command line"); hResultCode = DPNERR_OUTOFMEMORY; goto CLEANUP_DPLLaunch; } // Build the application full name by combining launch path with exe name *pwszAppName = L'\0'; if (wszToLaunchPath) { dwToLaunchPathLen = wcslen(wszToLaunchPath); if (dwToLaunchPathLen > 0) { wcscat(pwszAppName,wszToLaunchPath); if (wszToLaunchPath[dwToLaunchPathLen - 1] != L'\\') { wcscat(pwszAppName,L"\\"); } } } if (wszToLaunchExecutable) { wcscat(pwszAppName,wszToLaunchExecutable); } //Build the command line from app name, program description and the lobby related parameters wcscpy(pwszCmdLine, pwszAppName); wcscat(pwszCmdLine,L" "); if (pdplProgramDesc->pwszCommandLine) { wcscat(pwszCmdLine,pdplProgramDesc->pwszCommandLine); wcscat(pwszCmdLine,L" "); } wcscat(pwszCmdLine,DPL_ID_STR_W); wcscat(pwszCmdLine,pwszObjectName); DPFX(DPFPREP, 5,"Application full name string [%S]",pwszAppName); DPFX(DPFPREP, 5,"Command Line string [%S]",pwszCmdLine); // Create shared connect block to receive Application's PID *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) { dwError = GetLastError(); DPFX(DPFPREP, 0, "CreateFileMapping() failed dwLastError [0x%lx]", dwError ); hResultCode = DPNERR_CANTLAUNCHAPPLICATION; goto CLEANUP_DPLLaunch; } // Map file pSharedBlock = reinterpret_cast(MapViewOfFile(hFileMap,FILE_MAP_ALL_ACCESS,0,0,0)); if (pSharedBlock == NULL) { dwError = GetLastError(); DPFX(DPFPREP, 0,"MapViewOfFile() failed dwLastError [0x%lx]", dwError); hResultCode = DPNERR_CANTLAUNCHAPPLICATION; goto CLEANUP_DPLLaunch; } // Create synchronization event *pszObjectName = DPL_MSGQ_OBJECT_IDCHAR_EVENT; if ((hSyncEvents[0] = CreateEventA(NULL,TRUE,FALSE,pszObjectName)) == NULL) { dwError = GetLastError(); DPFX(DPFPREP, 0, "Create Event Failed dwLastError [0x%lx]", dwError ); hResultCode = DPNERR_CANTLAUNCHAPPLICATION; goto CLEANUP_DPLLaunch; } if( DNGetOSType() == VER_PLATFORM_WIN32_NT ) { // More setup siW.cb = sizeof(STARTUPINFO); siW.lpReserved = NULL; siW.lpDesktop = NULL; siW.lpTitle = NULL; siW.dwFlags = 0; siW.cbReserved2 = 0; siW.lpReserved2 = NULL; // Launch ! if (CreateProcessW(pwszAppName, pwszCmdLine, NULL,NULL,FALSE,NORMAL_PRIORITY_CLASS,NULL, pdplProgramDesc->pwszCurrentDirectory,&siW,&pi) == 0) { dwError = GetLastError(); DPFX(DPFPREP, 0, "CreateProcess Failed dwLastError [0x%lx]", dwError ); hResultCode = DPNERR_CANTLAUNCHAPPLICATION; goto CLEANUP_DPLLaunch; } } else { // More setup siA.cb = sizeof(STARTUPINFO); siA.lpReserved = NULL; siA.lpDesktop = NULL; siA.lpTitle = NULL; siA.dwFlags = 0; siA.cbReserved2 = 0; siA.lpReserved2 = NULL; DPFX(DPFPREP, 1, "Detected 9x, Doing Ansi launch" ); //Convert full app name, command line and default dir from unicode to ascii format if( FAILED( hResultCode = STR_AllocAndConvertToANSI( &pszAppName, pwszAppName ) ) ) { dwError = GetLastError(); DPFX(DPFPREP, 0, "String conversion failed dwError = [0x%lx]", dwError ); hResultCode = DPNERR_CONVERSION; goto CLEANUP_DPLLaunch; } if( FAILED( hResultCode = STR_AllocAndConvertToANSI( &pszCmdLine, pwszCmdLine ) ) ) { dwError = GetLastError(); DPFX(DPFPREP, 0, "String conversion failed dwError = [0x%lx]", dwError ); hResultCode = DPNERR_CONVERSION; goto CLEANUP_DPLLaunch; } if( FAILED( hResultCode = STR_AllocAndConvertToANSI( &pszDefaultDir, pdplProgramDesc->pwszCurrentDirectory ) ) ) { dwError = GetLastError(); DPFX(DPFPREP, 0, "String conversion failed dwError = [0x%lx]", dwError ); hResultCode = DPNERR_CONVERSION; goto CLEANUP_DPLLaunch; } // Launch ! if (CreateProcessA(pszAppName,pszCmdLine,NULL,NULL,FALSE,NORMAL_PRIORITY_CLASS,NULL, pszDefaultDir,&siA,&pi) == 0) { dwError = GetLastError(); DPFX(DPFPREP, 0, "CreateProcess Failed dwLastError [0x%lx]", dwError ); hResultCode = DPNERR_CANTLAUNCHAPPLICATION; goto CLEANUP_DPLLaunch; } } hSyncEvents[1] = pi.hProcess; // Wait for connection or application termination dwError = WaitForMultipleObjects(2,hSyncEvents,FALSE,dwTimeOut); // Immediately clean up dwPID = pSharedBlock->dwPID; /* CloseHandle(hSyncEvents[0]); UnmapViewOfFile(pSharedBlock); CloseHandle(hFileMap); */ // Ensure we can continue if (dwError - WAIT_OBJECT_0 > 1) { if (dwError == WAIT_TIMEOUT) { DPFERR("Wait for application connection timed out"); hResultCode = DPNERR_TIMEDOUT; goto CLEANUP_DPLLaunch; } else { DPFERR("Wait for application connection terminated mysteriously"); hResultCode = DPNERR_CANTLAUNCHAPPLICATION; goto CLEANUP_DPLLaunch; } } // Check if application terminated if (dwError == 1) { DPFERR("Application was terminated"); hResultCode = DPNERR_CANTLAUNCHAPPLICATION; goto CLEANUP_DPLLaunch; } *pdwPID = dwPID; hResultCode = DPN_OK; CLEANUP_DPLLaunch: if( hSyncEvents[0] != NULL ) CloseHandle( hSyncEvents[0] ); if( pSharedBlock != NULL ) UnmapViewOfFile(pSharedBlock); if( hFileMap != NULL ) CloseHandle( hFileMap ); if( pwszAppName != NULL ) DNFree( pwszAppName ); if (pwszCmdLine!=NULL) DNFree( pwszCmdLine ); if( pszAppName != NULL ) delete[] pszAppName; if (pszCmdLine!=NULL) delete[] pszCmdLine; if( pszDefaultDir != NULL ) delete [] pszDefaultDir; DPF_RETURN(hResultCode); } HRESULT DPLUpdateAppStatus(DIRECTPLAYLOBBYOBJECT *const pdpLobbyObject, const DPNHANDLE hSender, BYTE *const pBuffer) { HRESULT hResultCode; DPL_INTERNAL_MESSAGE_UPDATE_STATUS *pStatus; DPL_MESSAGE_SESSION_STATUS MsgStatus; DPFX(DPFPREP, 3,"Parameters: pBuffer [0x%p]",pBuffer); DNASSERT(pdpLobbyObject != NULL); DNASSERT(pBuffer != NULL); pStatus = reinterpret_cast(pBuffer); MsgStatus.dwSize = sizeof(DPL_MESSAGE_SESSION_STATUS); MsgStatus.dwStatus = pStatus->dwStatus; MsgStatus.hSender = hSender; // Return code is irrelevant, at this point we're going to indicate regardless hResultCode = DPLConnectionGetContext( pdpLobbyObject, hSender, &MsgStatus.pvConnectionContext ); if( FAILED( hResultCode ) ) { DPFX(DPFPREP, 0, "Error getting connection context for 0x%x hr=0x%x", hSender, hResultCode ); } hResultCode = (pdpLobbyObject->pfnMessageHandler)(pdpLobbyObject->pvUserContext, DPL_MSGID_SESSION_STATUS, reinterpret_cast(&MsgStatus)); DPFX(DPFPREP, 3,"Returning: [0x%lx]",hResultCode); return(hResultCode); } // ---------------------------------------------------------------------------- #if 0 // HRESULT DNL_RegisterLcMessageHandler // LPVOID lpv Interface pointer // LPFNDNLCMESSAGEHANDLER lpfn Pointer to user supplied lobby client message handler function // 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 lobby client message handler function. This function should // only be called once, when the lobby client is launched. // // This will set up the required message queues, and spawn the lobby client's receive message queue thread. // HRESULT DNL_EnumLocalPrograms // LPVOID lpv Interface pointer // LPGUID lpGuidApplication GUID of application to enumerate (optional) // LPVOID lpvEnumData Buffer to be filled with DNAPPINFO structs and Unicode strings // LPDWORD lpdwEnumData Size of lpvEnumData and number of bytes needed on return // LPDWORD lpdwItems Number of DNAPPINFO structs in lpvEnumData // // Returns // DPN_OK If the key was added successfully // DPNERR_INVALIDPARAM If there was a problem with a parameter // DPNERR_OUTOFMEMORY If it could not allocate memory // DPNERR_DOESNOTEXIST If there was a problem opening reading a registry key // // Notes // - This is ugly because of the need to support both WinNT and Win9x at runtime. // WinNT registry is kept in Unicode, whereas Win9x is in ANSI. Application root key // names should be ANSI compatible (or their GUIDs are used instead) i.e. Will be stored // in Unicode under WinNT, but only if convertable to ANSI // - Strings are placed in the buffer, starting at the end of the buffer and working // backwards. // - In most cases, errors will cause the current app being enumerated to be ignored // and not included in the enumeration buffer. // HRESULT DNL_ConnectApplication // LPVOID lpv Interface pointer // LPDNCONNECTINFO lpdnConnectionInfo Pointer to connection info structure // LPVOID lpvUserApplicationContext User supplied application context value // LPDWORD lpdwAppId Pointer to receive application ID (handle) in // DWORD dwFlags Flags // // Returns // DPN_OK If the application was connected to without incident // DPNERR_INVALIDPARAM If there was an invalid parameter // DPNERR_OUTOFMEMORY If there were any memory allocation problems // DPNERR_GENERIC If there were any problems // // Notes // This function connects the lobby client to a user specified application. If successfull, the // DNLobby assigned application ID will be returned in lpdwAppId. #undef DPF_MODNAME #define DPF_MODNAME "DNL_ConnectApplication" STDMETHODIMP DNL_ConnectApplication(LPVOID lpv,LPDNCONNECTINFO lpdnConnectionInfo, LPVOID lpvUserApplicationContext,LPDWORD lpdwAppId, DWORD dwFlags) { LPDIRECTPLAYLOBBYOBJECT lpdnLobbyObject; HRESULT hResultCode = DPN_OK; DNPROGRAMDESC dnProgramDesc; // Program descriptor for launching applications DWORD dwNumApps; // Number of applications currently running LPDWORD lpdwProcessList = NULL; // Pointer to process list DWORD dwProcessId; // Process ID of target applications DWORD dw; // Counter DWORD dwStrLen; // Various string lengths LPWSTR lpwszCommandLine = NULL; // Expanded Unicode command line LPWSTR lpwszCurrentDirectory = NULL; // Expanded Unicode current directory LPSTR lpszCommandLine = NULL; // Expanded ANSI command line LPSTR lpszCurrentDirectory = NULL; // Expanded ANSI current directory LPWSTR lpwszUnexpanded = NULL; // Unexpanded command line LPSTR lpszUnexpanded = NULL; // Unexpanded ANSI strings converted from Unicode STARTUPINFOA siA; // ANSI startup info (place holder) STARTUPINFOW siW; // Unicode startup info (place holder) PROCESS_INFORMATION pi; UUID uuid; RPC_STATUS rpcStatus; DWORD dwHandle; LPDN_APP_HANDLE_ENTRY lpdnAppHandleEntry; LPDN_MESSAGE_STRUCT lpdnMsg = NULL; DPFX(DPFPREP, 9,"Parameters: lpv [%p], lpdnConnectionInfo [%p], lpvUserApplicationContext [%p], lpdwAppId [%p], dwFlags [%lx]", lpv,lpdnConnectionInfo,lpvUserApplicationContext,lpdwAppId,dwFlags); // Parameter validation TRY { if (lpv == NULL || lpdnConnectionInfo == NULL || lpdwAppId == NULL) return(DPNERR_INVALIDPARAM); } EXCEPT (EXCEPTION_EXECUTE_HANDLER) { DPFERR("Exception encountered validating parameters"); return(DPNERR_INVALIDPARAM); } lpdnLobbyObject = (LPDIRECTPLAYLOBBYOBJECT) GET_OBJECT_FROM_INTERFACE(lpv); // Get program description from registry if ((hResultCode = DnGetProgramDesc(lpdnConnectionInfo->lpGuidApplication,&dnProgramDesc)) != DPN_OK) { goto EXIT_DNL_ConnectApplication; } hResultCode = DnGetProcessListW(dnProgramDesc.lpwszFilename,NULL,&dwNumApps); // Num apps if (hResultCode != DPN_OK && hResultCode != DPNERR_BUFFERTOOSMALL) { DnFreeProgramDesc(&dnProgramDesc); goto EXIT_DNL_ConnectApplication; } while (hResultCode == DPNERR_BUFFERTOOSMALL) // New processes may get launched ! { if (lpdwProcessList != NULL) GlobalFree(lpdwProcessList); if ((lpdwProcessList = (LPDWORD)GLOBALALLOC(dwNumApps*sizeof(DWORD))) == NULL) { hResultCode = DPNERR_OUTOFMEMORY; DnFreeProgramDesc(&dnProgramDesc); goto EXIT_DNL_ConnectApplication; } hResultCode = DnGetProcessListW(dnProgramDesc.lpwszFilename,lpdwProcessList,&dwNumApps); } if (hResultCode != DPN_OK) // Some sort of error occurred { DnFreeProgramDesc(&dnProgramDesc); goto EXIT_DNL_ConnectApplication; } dwProcessId = 0; for (dw = 0; dw < dwNumApps ; dw++) { if (DnCheckMsgQueueWaiting(lpdwProcessList[dw])) { if (DnSetMsgQueueWaiting(lpdwProcessList[dw],FALSE,TRUE) == DPN_OK) { // This is the process ID of the existing app to connect to ! dwProcessId = lpdwProcessList[dw]; break; } } } if (dwProcessId == 0) // Could not find an app to connect to - launch a new one { dwStrLen = wcslen(dnProgramDesc.lpwszPath) + 1 + wcslen(dnProgramDesc.lpwszApplicationLauncher) + 1 + wcslen(dnProgramDesc.lpwszCommandLine) + 1 + DN_LC_ID_STR_LEN + DN_GUID_STR_LEN + 1; if ((lpwszUnexpanded = (LPWSTR)GLOBALALLOC(dwStrLen*sizeof(WCHAR))) == NULL) { DnFreeProgramDesc(&dnProgramDesc); hResultCode = DPNERR_OUTOFMEMORY; goto EXIT_DNL_ConnectApplication; } *lpwszUnexpanded = L'\0'; if (wcslen(dnProgramDesc.lpwszPath) > 0) { wcscpy(lpwszUnexpanded,dnProgramDesc.lpwszPath); wcscat(lpwszUnexpanded,L"\\"); } wcscat(lpwszUnexpanded,dnProgramDesc.lpwszApplicationLauncher); wcscat(lpwszUnexpanded,L" "); wcscat(lpwszUnexpanded,dnProgramDesc.lpwszCommandLine); wcscat(lpwszUnexpanded,L" "); wcscat(lpwszUnexpanded,DN_LC_ID_WSTR); rpcStatus = CoCreateGuid(&uuid); if (rpcStatus != RPC_S_OK && rpcStatus != RPC_S_UUID_LOCAL_ONLY) { DnFreeProgramDesc(&dnProgramDesc); hResultCode = DPNERR_GENERIC; goto EXIT_DNL_ConnectApplication; } StringFromGUID(&uuid,lpwszUnexpanded+wcslen(lpwszUnexpanded),DN_GUID_STR_LEN+1); if (DN_RUNNING_NT) // Unicode on WinNT { // Expand environment strings if ((hResultCode = DnExpandEnvStringW(lpwszUnexpanded,&lpwszCommandLine)) != DPN_OK) { DPFX(DPFPREP, 0,"DnExpandEnvStringW() failed [%lX]",hResultCode); DnFreeProgramDesc(&dnProgramDesc); goto EXIT_DNL_ConnectApplication; } if ((hResultCode = DnExpandEnvStringW(dnProgramDesc.lpwszCurrentDirectory, &lpwszCurrentDirectory)) != DPN_OK) { DPFX(DPFPREP, 0,"DnExpandEnvStringW() failed [%lX]",hResultCode); DnFreeProgramDesc(&dnProgramDesc); goto EXIT_DNL_ConnectApplication; } // Set up to Launch application siW.cb = sizeof(STARTUPINFO); siW.lpReserved = NULL; siW.lpDesktop = NULL; siW.lpTitle = NULL; siW.dwFlags = (DWORD)NULL; siW.cbReserved2 = (BYTE)NULL; siW.lpReserved2 = NULL; // Launch application (finally !) if (CreateProcessW(NULL,lpwszCommandLine,NULL,NULL,FALSE,NORMAL_PRIORITY_CLASS,NULL, lpwszCurrentDirectory,&siW,&pi) == 0) { hResultCode = DPNERR_CANTLAUNCHAPPLICATION; DnFreeProgramDesc(&dnProgramDesc); goto EXIT_DNL_ConnectApplication; } } else // ANSI on Win9x { // Convert command line from Unicode to ANSI if ((lpszUnexpanded = (LPSTR)GLOBALALLOC(dwStrLen)) == NULL) { DnFreeProgramDesc(&dnProgramDesc); hResultCode = DPNERR_OUTOFMEMORY; goto EXIT_DNL_ConnectApplication; } WideToAnsi(lpszUnexpanded,lpwszUnexpanded,dwStrLen); DPFX(DPFPREP, 7,"Unexpanded Command Line [%s]",lpszUnexpanded); // Expand command line environment strings if ((hResultCode = DnExpandEnvStringA(lpszUnexpanded,&lpszCommandLine)) != DPN_OK) { DPFX(DPFPREP, 0,"DnExpandEnvStringA() failed [%lX]",hResultCode); DnFreeProgramDesc(&dnProgramDesc); goto EXIT_DNL_ConnectApplication; } DPFX(DPFPREP, 7,"Expanded Command Line [%s]",lpszCommandLine); // Convert current directory from Unicode to ANSI GlobalFree(lpszUnexpanded); lpszUnexpanded = NULL; dwStrLen = wcslen(dnProgramDesc.lpwszCurrentDirectory); if ((lpszUnexpanded = (LPSTR)GLOBALALLOC(dwStrLen+1)) == NULL) { DnFreeProgramDesc(&dnProgramDesc); hResultCode = DPNERR_OUTOFMEMORY; goto EXIT_DNL_ConnectApplication; } WideToAnsi(lpszUnexpanded,dnProgramDesc.lpwszCurrentDirectory,dwStrLen+1); DPFX(DPFPREP, 7,"Unexpanded Current Directory [%s]",lpszUnexpanded); // Expand current directory environment strings if ((hResultCode = DnExpandEnvStringA(lpszUnexpanded,&lpszCurrentDirectory)) != DPN_OK) { DPFX(DPFPREP, 0,"DnExpandEnvStringA() failed [%lX]",hResultCode); DnFreeProgramDesc(&dnProgramDesc); goto EXIT_DNL_ConnectApplication; } DPFX(DPFPREP, 7,"Expanded Current Directory [%s]",lpszCurrentDirectory); // Set up to Launch application siA.cb = sizeof(STARTUPINFO); siA.lpReserved = NULL; siA.lpDesktop = NULL; siA.lpTitle = NULL; siA.dwFlags = (DWORD)NULL; siA.cbReserved2 = (BYTE)NULL; siA.lpReserved2 = NULL; // Launch application (finally !) if (CreateProcessA(NULL,lpszCommandLine,NULL,NULL,FALSE,NORMAL_PRIORITY_CLASS,NULL, lpszCurrentDirectory,&siA,&pi) == 0) { DPFERR("CreateProcess() failed !"); hResultCode = DPNERR_CANTLAUNCHAPPLICATION; DnFreeProgramDesc(&dnProgramDesc); goto EXIT_DNL_ConnectApplication; } } // Get Application Process ID if ((hResultCode = DnHandShakeAppPid(TRUE,&uuid,&dwProcessId)) != DPN_OK) { DPFERR("Hand shake to retrieve App PID failed !"); DnFreeProgramDesc(&dnProgramDesc); goto EXIT_DNL_ConnectApplication; } DPFX(DPFPREP, 0,"Application PID = [0x%08lX]",dwProcessId); } DnFreeProgramDesc(&dnProgramDesc); // Create application handle if ((lpdnAppHandleEntry = (LPDN_APP_HANDLE_ENTRY)GLOBALALLOC( sizeof(DN_APP_HANDLE_ENTRY))) == NULL) { hResultCode = DPNERR_OUTOFMEMORY; goto EXIT_DNL_ConnectApplication; } lpdnAppHandleEntry->dwAppProcessId = dwProcessId; if ((lpdnAppHandleEntry->lpmqSendMsgQueue = (DN_MSG_QUEUE_STRUCT *)GLOBALALLOC(sizeof(DN_MSG_QUEUE_STRUCT))) == NULL) { hResultCode = DPNERR_OUTOFMEMORY; goto EXIT_DNL_ConnectApplication; } if ((hResultCode = CreateHandle(lpdnLobbyObject->lphsApplicationHandles,&dwHandle,lpdnAppHandleEntry)) != DPN_OK) { goto EXIT_DNL_ConnectApplication; } lpdnAppHandleEntry->dwHandle = dwHandle; lpdnAppHandleEntry->lpvUserApplicationContext = lpvUserApplicationContext; *lpdwAppId = dwHandle; // Connect to Application's receive message queue if ((hResultCode = DnOpenMsgQueue(lpdnAppHandleEntry->lpmqSendMsgQueue,dwProcessId, DN_MSG_OBJECT_SUFFIX_APPLICATION,DN_MSG_QUEUE_SIZE,(DWORD)NULL)) != DPN_OK) { goto EXIT_DNL_ConnectApplication; } // Pass lobby client connection info to application if ((hResultCode = DnSendLcInfo(lpdnAppHandleEntry->lpmqSendMsgQueue,dwHandle, GetCurrentProcessId())) != DPN_OK) { goto EXIT_DNL_ConnectApplication; } // Pass connection info - TODO check if valid DPFX(DPFPREP, 9,"Sending Connection Info ..."); // Get string lengths dwStrLen = ((wcslen(lpdnConnectionInfo->lpwszPlayerName)+ 1 ) * sizeof(WCHAR)) + strlen(lpdnConnectionInfo->lpszDNAddressLocalSettings) + 1 + strlen(lpdnConnectionInfo->lpszDNAddressRemote) + 1 + sizeof(GUID); dw = sizeof(DN_MESSAGE_STRUCT) + dwStrLen; // Create message if ((lpdnMsg = (LPDN_MESSAGE_STRUCT)GLOBALALLOC(dw)) == NULL) { hResultCode = DPNERR_OUTOFMEMORY; goto EXIT_DNL_ConnectApplication; } lpdnMsg->dwMessageType = DN_MSGTYPE_CONNECTION_SETTINGS; lpdnMsg->dwParam1 = 0; lpdnMsg->dwParam2 = (wcslen(lpdnConnectionInfo->lpwszPlayerName) + 1) * sizeof(WCHAR); lpdnMsg->dwParam3 = lpdnMsg->dwParam2 + strlen(lpdnConnectionInfo->lpszDNAddressRemote) + 1; lpdnMsg->dwParam4 = lpdnMsg->dwParam3 + strlen(lpdnConnectionInfo->lpszDNAddressLocalSettings) + 1; lpdnMsg->dwTagLen = dwStrLen; memcpy(&lpdnMsg->cTags + lpdnMsg->dwParam1,lpdnConnectionInfo->lpwszPlayerName, lpdnMsg->dwParam2); memcpy(&lpdnMsg->cTags + lpdnMsg->dwParam2,lpdnConnectionInfo->lpszDNAddressRemote, lpdnMsg->dwParam3-lpdnMsg->dwParam2); memcpy(&lpdnMsg->cTags + lpdnMsg->dwParam3,lpdnConnectionInfo->lpszDNAddressLocalSettings, lpdnMsg->dwParam4-lpdnMsg->dwParam3); memcpy(&lpdnMsg->cTags + lpdnMsg->dwParam4,lpdnConnectionInfo->lpGuidApplication, dwStrLen-lpdnMsg->dwParam4); if ((hResultCode = DnSendMsg(lpdnAppHandleEntry->lpmqSendMsgQueue,lpdnMsg,dw,FALSE, 0)) != DPN_OK) { GlobalFree(lpdnMsg); goto EXIT_DNL_ConnectApplication; } GlobalFree(lpdnMsg); EXIT_DNL_ConnectApplication: if (lpwszUnexpanded != NULL) GlobalFree(lpwszUnexpanded); if (lpwszCommandLine != NULL) GlobalFree(lpwszCommandLine); if (lpwszCurrentDirectory != NULL) GlobalFree(lpwszCurrentDirectory); if (lpszUnexpanded != NULL) GlobalFree(lpszUnexpanded); if (lpszCommandLine != NULL) GlobalFree(lpszCommandLine); if (lpszCurrentDirectory != NULL) GlobalFree(lpszCurrentDirectory); if (lpdwProcessList != NULL) GlobalFree(lpdwProcessList); return(hResultCode); } // HRESULT DnProcessLcMessage // LPDIRECTNETLOBBYOBJECT lpdnLobbyObject Pointer to lobby object // LPVOID lpvUserHandler Pointer to user message handler routine // LPVOID lpvMsgBuff Pointer to message buffer // DWORD dwMsgLen Length of message buffer // DWORD dwId Application ID which sent the message // LPVOID lpvContext User context value associated with the app ID // // Returns // DPN_OK If the message was processed without incident // DPNERR_GENERIC If there were any problems from the user supplied handler // // Notes // This function processes messages received by the lobby client from the applications it is connected to. // One Lobby Client may be connected to one or more applications. Internal messages are processed here, // and all others are passed to the user supplied message handler. #undef DPF_MODNAME #define DPF_MODNAME "DnProcessLcMessage" HRESULT DnProcessLcMessage(LPDIRECTNETLOBBYOBJECT lpdnLobbyObject,LPVOID lpvUserHandler, LPVOID lpvMsgBuff, DWORD dwMsgLen,DWORD dwId,LPVOID lpvContext) { HRESULT hResultCode = DPN_OK; LPDN_MESSAGE_STRUCT lpdnMsg; DPFX(DPFPREP, 9,"Parameters: lpdnLobbyObject [0x%p], lpvUserHandler [0x%p], lpvMsgBuff [0x%p], dwMsgLen [%lx], dwId [%lx], lpvContext [0x%p]", lpdnLobbyObject,lpvUserHandler,lpvMsgBuff,dwMsgLen,dwId,lpvContext); lpdnMsg = (LPDN_MESSAGE_STRUCT)lpvMsgBuff; switch(lpdnMsg->dwMessageType) { case DN_MSGTYPE_TERMINATE: DPFX(DPFPREP, 9,"Received Terminate"); ExitThread(0); break; default: // Pass message to user message handler DPFX(DPFPREP, 9,"User Message ..."); hResultCode = ((LPFNDNLCMESSAGEHANDLER)lpvUserHandler)(dwId,lpvContext,lpdnMsg->dwMessageType,lpdnMsg->dwUserToken, lpdnMsg->dwParam1,lpdnMsg->dwParam2,lpdnMsg->dwParam3,lpdnMsg->dwParam4, lpdnMsg->dwTagLen,&(lpdnMsg->cTags)); break; } return(hResultCode); } // HRESULT DNL_SendToApplication // LPVOID lpv Interface pointer // DWORD dwAppId Application ID to send message to // DWORD dwMessageId Message ID code // DWORD dwUserToken User supplied token // DWORD dwParam1 // DWORD dwParam2 // DWORD dwParam3 // DWORD dwParam4 // DWORD dwFlags // DWORD dwTagLen Length of tags field (in bytes) // LPVOID lpvTags Tag field // // Returns // DPN_OK If the message was sent without incident // DPNERR_INVALIDPARAM If there was an invalid parameter // DPNERR_OUTOFMEMORY If there were any memory allocation problems // DPNERR_GENERIC If there were any problems // // Notes // This function sends messages from the lobby client to a specified application. #undef DPF_MODNAME #define DPF_MODNAME "DNL_SendToApplication" STDMETHODIMP DNL_SendToApplication(LPVOID lpv,DWORD dwAppId,DWORD dwMessageId,DWORD dwUserToken, DWORD dwParam1,DWORD dwParam2,DWORD dwParam3,DWORD dwParam4, DWORD dwFlags,DWORD dwTagLen,LPVOID lpvTags) { LPDIRECTNETLOBBYOBJECT lpdnLobbyObject; HRESULT hResultCode = DPN_OK; LPDN_APP_HANDLE_ENTRY lpdnAppHandleEntry; LPDN_MESSAGE_STRUCT lpdnMessage; DN_MESSAGE_STRUCT dnMessage; DWORD dwMsgLen; DPFX(DPFPREP, 9,"Parameters: lpv [%p], dwAppId [%lx], dwMessageId [%lx], dwUserToken [%lx], dwParams [%lx], [%lx], [%lx], [%lx], dwFlags [%lx], dwTagLen [%lx], lpvTags [%p]", lpv,dwAppId,dwMessageId,dwUserToken,dwParam1,dwParam2,dwParam3,dwParam4,dwFlags,dwTagLen,lpvTags); // Parameter validation TRY { if (lpv == NULL || dwAppId == 0) return(DPNERR_INVALIDPARAM); if (dwFlags != 0) return(DPNERR_INVALIDFLAGS); } EXCEPT (EXCEPTION_EXECUTE_HANDLER) { DPFERR("Exception encountered validating parameters"); return(DPNERR_INVALIDPARAM); } lpdnLobbyObject = (LPDIRECTNETLOBBYOBJECT) GET_OBJECT_FROM_INTERFACE(lpv); // Ensure app ID is valid, and then retrieve connection (message queue) information for it if (!IsHandleValid(lpdnLobbyObject->lphsApplicationHandles,dwAppId)) { hResultCode = DPNERR_INVALIDPARAM; goto EXIT_DNL_SendToApplication; } if ((hResultCode = RetrieveHandleData(lpdnLobbyObject->lphsApplicationHandles,dwAppId, (LPVOID *)&lpdnAppHandleEntry)) != DPN_OK) { goto EXIT_DNL_SendToApplication; } dwMsgLen = sizeof(DN_MESSAGE_STRUCT); if (dwTagLen > 0) // Create buffer if there are tags { dwMsgLen += dwTagLen; if ((lpdnMessage = (LPDN_MESSAGE_STRUCT)GLOBALALLOC(dwMsgLen)) == NULL) { hResultCode = DPNERR_OUTOFMEMORY; goto EXIT_DNL_SendToApplication; } } else // Use default static buffer (avoid malloc call) { lpdnMessage = &dnMessage; } lpdnMessage->dwMessageType = dwMessageId; lpdnMessage->dwUserToken = dwUserToken; lpdnMessage->dwParam1 = dwParam1; lpdnMessage->dwParam2 = dwParam2; lpdnMessage->dwParam3 = dwParam3; lpdnMessage->dwParam4 = dwParam4; lpdnMessage->dwTagLen = dwTagLen; if (dwTagLen > 0) { memcpy(&(lpdnMessage->cTags),lpvTags,dwTagLen); } else { lpdnMessage->cTags = '\0'; } // Send message hResultCode = DnSendMsg(lpdnAppHandleEntry->lpmqSendMsgQueue,lpdnMessage,dwMsgLen,FALSE, (DWORD)NULL); // Free buffer if it was malloc'd if (dwTagLen > 0) GlobalFree(lpdnMessage); EXIT_DNL_SendToApplication: return(hResultCode); } // HRESULT DNL_ReleaseApplication // LPVOID lpv Interface pointer // DWORD dwAppId Application ID to release // // Returns // DPN_OK If the message was sent without incident // DPNERR_INVALIDPARAM If there was an invalid parameter // DPNERR_INVALIDHANDLE If the application ID is not valid // DPNERR_GENERIC If there were any problems // // Notes // This function causes the lobby client to terminate it's association with a lobbied // application (whether the lobby client launched the application or not). This allows the // application to either terminate gracefully (if still running) and then be made available // for re-connection to another (or the same) lobby client later. This should be called // whenever a lobby client is finished with a lobbied application. #undef DPF_MODNAME #define DPF_MODNAME "DNL_ReleaseApplication" STDMETHODIMP DNL_ReleaseApplication(LPVOID lpv,DWORD dwAppId) { LPDIRECTNETLOBBYOBJECT lpdnLobbyObject; HRESULT hResultCode = DPN_OK; LPDN_APP_HANDLE_ENTRY lph; DPFX(DPFPREP, 9,"Parameters: lpv [%lx], dwAppId [%lx]", lpv,dwAppId); // Parameter validation TRY { if (lpv == NULL || dwAppId == 0) return(DPNERR_INVALIDPARAM); } EXCEPT (EXCEPTION_EXECUTE_HANDLER) { DPFERR("Exception encountered validating parameters"); return(DPNERR_INVALIDPARAM); } lpdnLobbyObject = (LPDIRECTNETLOBBYOBJECT) GET_OBJECT_FROM_INTERFACE(lpv); // TODO - wait for messages to be processed if ((hResultCode = RetrieveHandleData(lpdnLobbyObject->lphsApplicationHandles,dwAppId,&lph)) == DPN_OK) { if ((hResultCode = DnCloseMsgQueue(lph->lpmqSendMsgQueue)) == DPN_OK) { if ((hResultCode = DestroyHandle(lpdnLobbyObject->lphsApplicationHandles,dwAppId)) == DPN_OK) { } } GlobalFree(lph); } //EXIT_DNL_ReleaseApplication: return(hResultCode); } #endif