windows-nt/Source/XPSP1/NT/termsrv/remdsk/msngr/directplayconnection.cpp
2020-09-26 16:20:57 +08:00

702 lines
15 KiB
C++

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