/*++ Copyright (c) 2000 Microsoft Corporation Module Name: DirectPlayConnection Abstract: DirectPlayConnection is a wrapper around the direct play interfaces to just do what is needed for this application. It takes care of connecting to the remote machine and sending/recieving the connction paramters. Author: Marc Reyhner 7/5/2000 --*/ #include "stdafx.h" #ifdef TRC_FILE #undef TRC_FILE #endif #define TRC_FILE "rcdpc" #include "DirectPlayConnection.h" #include "exception.h" #include "resource.h" static BOOL SendLaunchSuccessful(LPDIRECTPLAYLOBBY2 pLobby); // A structure for passing data to our callback // function for enumerating the player list. typedef struct _CALLBACKDATA { DPID playerID; BOOL bSet; } CALLBACKDATA, FAR *LPCALLBACKDATA; #define DPSYS_CONNECTPARMSMSG 0x0111 typedef struct _DPMSG_CONNECTPARMS { DWORD dwType; // Message type DWORD dwDataSize; } DPMSG_CONNECTPARMS, FAR *LPDPMSG_CONNECTPARMS; // Callback function for enumerating the player list. static BOOL FAR PASCAL PlayerCallback( DPID dpId, DWORD dwPlayerType, LPCDPNAME lpName, DWORD dwFlags, LPVOID lpContext ); CDirectPlayConnection::CDirectPlayConnection( ) /*++ Routine Description: This is the constructor for CDirectPlayConnection. All that it does is create the direct play lobby. Arguments: None Return Value: None --*/ { HRESULT hr; DC_BEGIN_FN("CDirectPlayConnection::CDirectPlayConnection"); m_rLobby = NULL; m_rSettings = NULL; m_rDirectPlay = NULL; m_bConnected = FALSE; m_PlayerID = 0; hr = CoCreateInstance(CLSID_DirectPlayLobby,NULL,CLSCTX_ALL, IID_IDirectPlayLobby,(LPVOID*)&m_rLobby); if (hr != S_OK) { TRC_ERR((TB,TEXT("Error creating IDirectPlayLobby"))); goto FAILURE; } m_hEventHandle = CreateEvent(NULL,FALSE,FALSE,NULL); if (!m_hEventHandle) { TRC_ERR((TB,TEXT("Error creating event handle"))); goto FAILURE; } DC_END_FN(); return; FAILURE: if (m_rLobby) { m_rLobby->Release(); m_rLobby = NULL; } if (m_hEventHandle) { CloseHandle(m_hEventHandle); m_hEventHandle = NULL; } throw CException(IDS_DPERRORCREATE); } CDirectPlayConnection::~CDirectPlayConnection( ) /*++ Routine Description: This destroys all the direct play objects that were created. Arguments: None Return Value: None --*/ { DC_BEGIN_FN("CDirectPlayConnection::~CDirectPlayConnection"); if (m_bConnected) { DisconnectRemoteApplication(); } if (m_rLobby) { m_rLobby->Release(); } if (m_rDirectPlay) { m_rDirectPlay->Release(); } if (m_rSettings) { delete m_rSettings; } DC_END_FN(); } VOID CDirectPlayConnection::ConnectToRemoteApplication( ) /*++ Routine Description: This connects to the remote application. If we were not started by a lobby application an exception will be thrown at this point. Arguments: None Return Value: None --*/ { DWORD dwSize = 0; HRESULT hr; DC_BEGIN_FN("CDirectPlayConnection::ConnectToRemoteApplication"); hr = m_rLobby->GetConnectionSettings(0,NULL,&dwSize); if (hr != DPERR_BUFFERTOOSMALL) { TRC_ERR((TB,TEXT("Error getting connection settings size"))); goto FAILURE; } m_rSettings = (DPLCONNECTION *)new BYTE [dwSize]; if (!m_rSettings) { TRC_ERR((TB,TEXT("Out of memory"))); goto FAILURE; } hr = m_rLobby->GetConnectionSettings(0,m_rSettings,&dwSize); if (hr != DP_OK) { TRC_ERR((TB,TEXT("Error getting connection settings"))); goto FAILURE; } /* m_rSettings->lpSessionDesc->dwFlags |= DPSESSION_SECURESERVER; hr = m_rLobby->SetConnectionSettings(0,0,m_rSettings); if (hr != DP_OK) { TRC_ERR((TB,TEXT("Error setting connection settings"))); goto FAILURE; }*/ hr = m_rLobby->Connect(0,&m_rDirectPlay,NULL); if (hr != DP_OK) { TRC_ERR((TB,TEXT("Error connecting to remote application: 0x%8lx"),hr)); goto FAILURE; } hr = m_rDirectPlay->CreatePlayer(&m_PlayerID,NULL,m_hEventHandle,NULL,0,0); if (hr != DP_OK) { TRC_ERR((TB,TEXT("Error creating player"))); goto FAILURE; } m_bConnected = TRUE; DC_END_FN(); return; FAILURE: if (m_rSettings) { delete m_rSettings; m_rSettings = NULL; } if (m_rDirectPlay) { m_rDirectPlay->Release(); m_rDirectPlay = NULL; } // // We did a TRC_ERR up where the error was thrown so we won't // do another here. // if (hr == DPERR_NOTLOBBIED) { throw CException(IDS_DPNOTLOBBIED); } else { throw CException(IDS_DPERRORCONNECT); } } VOID CDirectPlayConnection::DisconnectRemoteApplication( ) { DC_BEGIN_FN("CDirectPlayConnection::DisconnectRemoteApplication"); if (!m_bConnected) { TRC_ERR((TB,TEXT("Not connected to remote application"))); throw CException(IDS_DPNOTCONNECTED); } m_rDirectPlay->Close(); m_rDirectPlay->Release(); m_rDirectPlay = NULL; delete m_rSettings; m_rSettings = NULL; m_bConnected = FALSE; DC_END_FN(); } VOID CDirectPlayConnection::SendConnectionParameters( IN BSTR parms ) /*++ Routine Description: This sends the connection parms to the client application Arguments: parms - The connection parms for Salem. Return Value: None --*/ { LPVOID sendBuffer; DWORD cbSendBuffer; LPDPMSG_CONNECTPARMS unsecMsg; DPID otherPlayerID; HRESULT hr; DC_BEGIN_FN("CDirectPlayConnection::SendConnectionParameters"); sendBuffer = NULL; if (!m_bConnected) { TRC_ERR((TB,TEXT("Not connected to remote application"))); throw CException(IDS_DPNOTCONNECTED); } otherPlayerID = xGetOtherPlayerID(); if (m_rSettings->lpSessionDesc->dwFlags & DPSESSION_SECURESERVER) { hr = m_rDirectPlay->Send(m_PlayerID,otherPlayerID, DPSEND_ENCRYPTED|DPSEND_GUARANTEED|DPSEND_SIGNED,parms, (wcslen(parms)+1)*sizeof(OLECHAR)); if (hr != DP_OK) { TRC_ERR((TB,TEXT("Error sending dp message."))); throw CException(IDS_DPERRORSEND); } } else { cbSendBuffer = sizeof(DPMSG_CONNECTPARMS) + (wcslen(parms)+1)*sizeof(OLECHAR); sendBuffer = new BYTE [ cbSendBuffer ]; if (!sendBuffer) { TRC_ERR((TB,TEXT("Out of memory."))); throw CException(IDS_DPERRORSEND); } unsecMsg = (LPDPMSG_CONNECTPARMS)sendBuffer; unsecMsg->dwType = DPSYS_CONNECTPARMSMSG; unsecMsg->dwDataSize = cbSendBuffer - sizeof(DPMSG_CONNECTPARMS); CopyMemory((LPBYTE)sendBuffer + sizeof(DPMSG_CONNECTPARMS),parms, unsecMsg->dwDataSize); hr = m_rDirectPlay->Send(m_PlayerID,otherPlayerID,DPSEND_GUARANTEED, sendBuffer,cbSendBuffer); delete sendBuffer; sendBuffer = NULL; if (hr != DP_OK) { TRC_ERR((TB,TEXT("Error sending dp message."))); throw CException(IDS_DPERRORSEND); } } DC_END_FN(); } BSTR CDirectPlayConnection::ReceiveConnectionParameters( ) /*++ Routine Description: This reads the connection parms sent by the server. This will block waiting for the server to send. Arguments: None Return Value: BSTR - The connection parameters. --*/ { BSTR parms = NULL; DPID otherPlayerID; LPDPMSG_GENERIC lpMsg; LPDPMSG_SECUREMESSAGE lpMsgSec = NULL; LPDPMSG_CONNECTPARMS lpMsgUnsec = NULL; DWORD dwSize = 0; DWORD endWaitTime, dwWaitTime; BOOL bFirstTimeInLoop; DWORD dwResult; DC_BEGIN_FN("CDirectPlayConnection::ReceiveConnectionParameters"); if (!m_bConnected) { TRC_ERR((TB,TEXT("Not connected to remote application"))); throw CException(IDS_DPNOTCONNECTED); } otherPlayerID = xGetOtherPlayerID(); endWaitTime = GetTickCount() + GETPARMSTIMEOUT; bFirstTimeInLoop = TRUE; while (endWaitTime > GetTickCount()) { dwWaitTime = endWaitTime - GetTickCount(); if (dwWaitTime <= 0) { // We hit time between the beginning of the loop // and now. break; } if (bFirstTimeInLoop) { bFirstTimeInLoop = FALSE; } else { dwResult = WaitForSingleObject(m_hEventHandle,dwWaitTime); if (dwResult != WAIT_OBJECT_0) { TRC_ERR((TB,TEXT("Timed waiting for event."))); goto FAILURE; } } while (lpMsg = xReceiveMessage(0,m_PlayerID,DPRECEIVE_TOPLAYER)) { if (lpMsg->dwType == DPSYS_SECUREMESSAGE) { lpMsgSec = (LPDPMSG_SECUREMESSAGE)lpMsg; if (!(lpMsgSec->dwFlags & (DPSEND_SIGNED|DPSEND_ENCRYPTED))) { TRC_ERR((TB,TEXT("Message not signed or encrypted."))); goto FAILURE; } parms = (BSTR)new BYTE [lpMsgSec->dwDataSize]; if (!parms) { TRC_ERR((TB,TEXT("Out of memory"))); goto FAILURE; } CopyMemory(parms,lpMsgSec->lpData,lpMsgSec->dwDataSize); break; } else if (lpMsg->dwType == DPSYS_CONNECTPARMSMSG) { lpMsgUnsec = (LPDPMSG_CONNECTPARMS)lpMsg; parms = (BSTR)new BYTE [lpMsgUnsec->dwDataSize]; if (!parms) { TRC_ERR((TB,TEXT("Out of memory"))); goto FAILURE; } CopyMemory(parms,((PCHAR)lpMsgUnsec) + sizeof(DPMSG_CONNECTPARMS), lpMsgUnsec->dwDataSize); break; } else { delete lpMsg; lpMsg = NULL; } } if (lpMsg) { break; } } if (!lpMsg) { TRC_ERR((TB,TEXT("No messages ever showed up."))); goto FAILURE; } delete lpMsg; DC_END_FN(); return parms; FAILURE: if (lpMsg) { delete lpMsg; } if (parms) { delete parms; } throw CException(IDS_DPERRORRECEIVE); } BOOL CDirectPlayConnection::IsServer( ) /*++ Routine Description: This returns whether or not this is the server. Arguments: None Return Value: BOOL - Is this the server --*/ { DC_BEGIN_FN("CDirectPlayConnection::IsServer"); if (!m_bConnected) { TRC_ERR((TB,TEXT("Not connected to remote application"))); throw CException(IDS_DPNOTCONNECTED); } DC_END_FN(); return (m_rSettings->dwFlags & DPLCONNECTION_CREATESESSION); } DPID CDirectPlayConnection::xGetOtherPlayerID( ) /*++ Routine Description: This finds out what the playerID for the remote session is. If the player has not yet been created we loop waiting for it to be created. Arguments: None Return Value: None --*/ { LPDPMSG_GENERIC lpMsg = NULL; LPDPMSG_CREATEPLAYERORGROUP lpCreateMsg; DWORD dwResult; DWORD dwWaitTime; DWORD timeOutTime; DPID otherPlayer = 0; CALLBACKDATA playerData; HRESULT hr; DC_BEGIN_FN("CDirectPlayConnection::xGetOtherPlayerID"); playerData.bSet = FALSE; hr = m_rDirectPlay->EnumPlayers(NULL,PlayerCallback,&playerData,DPENUMPLAYERS_REMOTE); if (hr == DP_OK && playerData.bSet) { DC_END_FN(); return playerData.playerID; } timeOutTime = GetTickCount() + GETREMOTEPLAYERTIMEOUT; while (timeOutTime > GetTickCount()) { dwWaitTime = timeOutTime - GetTickCount(); if (dwWaitTime <= 0) { // We hit time between the beginning of the loop // and now. break; } dwResult = WaitForSingleObject(m_hEventHandle,dwWaitTime); if (dwResult != WAIT_OBJECT_0) { TRC_ERR((TB,TEXT("Timed out waiting for event."))); throw CException(IDS_DPERRORTIMEOUT); } while (lpMsg = xReceiveMessage(DPID_SYSMSG,m_PlayerID, DPRECEIVE_TOPLAYER|DPRECEIVE_FROMPLAYER)) { if (lpMsg->dwType == DPSYS_CREATEPLAYERORGROUP) { lpCreateMsg = (LPDPMSG_CREATEPLAYERORGROUP)lpMsg; if (lpCreateMsg->dwPlayerType == DPPLAYERTYPE_PLAYER) { if (lpCreateMsg->dpId != m_PlayerID) { otherPlayer = lpCreateMsg->dpId; break; } } } delete lpMsg; } if (otherPlayer) { break; } } if (lpMsg) { delete lpMsg; } if (otherPlayer) { DC_END_FN(); return otherPlayer; } TRC_ERR((TB,TEXT("Timed out waiting to find remote player."))); throw CException(IDS_DPERRORTIMEOUT); } static BOOL FAR PASCAL PlayerCallback( IN DPID dpId, IN DWORD dwPlayerType, IN LPCDPNAME lpName, IN DWORD dwFlags, IN OUT LPVOID lpContext ) /*++ Routine Description: This is the callback for the direct play player enumeration functin. We just set what the other playerID is and then return. Arguments: None Return Value: BOOL - Should we continue the enumeration. --*/ { DC_BEGIN_FN("PlayerCallback"); LPCALLBACKDATA data = (LPCALLBACKDATA)lpContext; data->playerID = dpId; data->bSet = TRUE; DC_END_FN(); return FALSE; } LPDPMSG_GENERIC CDirectPlayConnection::xReceiveMessage( IN DPID from, IN DPID to, IN DWORD dwFlags ) /*++ Routine Description: This reads the next message for the given from and to addresses. Arguments: from - Who the message is from. to - Who the message is to. dwFlags - flags for the incoming message. Return Value: LPDPMSG_GENERIC - If there was a message. NULL - There was not a message available. --*/ { HRESULT hr; LPVOID lpMsg; DWORD dwSize = 0; DC_BEGIN_FN("CDirectPlayConnection::xReceiveMessage"); hr = m_rDirectPlay->Receive(&from,&to,dwFlags,NULL,&dwSize); if (hr != DPERR_BUFFERTOOSMALL) { if (hr == DPERR_NOMESSAGES) { DC_END_FN(); return NULL; } else { TRC_ERR((TB,TEXT("Error receiving message size."))); goto FAILURE; } } lpMsg = (LPVOID)new BYTE [dwSize]; if (!lpMsg) { TRC_ERR((TB,TEXT("Out of memory."))); goto FAILURE; } hr = m_rDirectPlay->Receive(&from,&to,dwFlags,lpMsg,&dwSize); if (hr != DP_OK) { TRC_ERR((TB,TEXT("Error receiving message."))); goto FAILURE; } DC_END_FN(); return (LPDPMSG_GENERIC)lpMsg; FAILURE: if (lpMsg) { delete lpMsg; } throw CException(IDS_DPERRORMSGRECIEVE); } static BOOL SendLaunchSuccessful( IN OUT LPDIRECTPLAYLOBBY2 pLobby ) { HRESULT hr; DPLMSG_GENERIC msg; msg.dwType = DPLSYS_DPLAYCONNECTSUCCEEDED; hr = pLobby->SendLobbyMessage( DPLMSG_STANDARD, 0, &msg, sizeof(msg) ); if ( FAILED(hr) ) { MessageBox( NULL, TEXT("Send system message failed."), TEXT("Error"), MB_OK | MB_APPLMODAL ); return false; } return true; }