702 lines
15 KiB
C++
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;
|
|
}
|