631 lines
16 KiB
C++
631 lines
16 KiB
C++
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
DirectPlayConnection.cpp
|
|
|
|
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
|
|
SteveShi (updated) 8/23/2000
|
|
|
|
--*/
|
|
|
|
#include "stdafx.h"
|
|
|
|
#include "DirectPlayConnection.h"
|
|
#include "resource.h"
|
|
#include "msmsgs.h"
|
|
|
|
#define DP_ERR( hr ) if(SUCCEEDED(hr)) hr = E_FAIL; // Weird DP error.
|
|
|
|
#define DP_MY_DATATYPE 0x0111
|
|
|
|
typedef struct _DP_MY_DATA
|
|
{
|
|
DWORD dwType; // My message type
|
|
DWORD dwDataSize;
|
|
BYTE pData[1];
|
|
} DP_MY_DATA, *LPDP_MY_DATA;
|
|
|
|
/***********************************************
|
|
Func:
|
|
EnumPlayersCallback
|
|
Abstract:
|
|
Callback function for enumerating the player list.
|
|
For our scenario, we've only one remote player.
|
|
************************************************/
|
|
static BOOL FAR PASCAL
|
|
EnumPlayersCallback(DPID dpId, DWORD dwPlayerType, LPCDPNAME lpName, DWORD dwFlags, LPVOID lpContext)
|
|
{
|
|
*(DPID*)lpContext = dpId;
|
|
return FALSE;
|
|
}
|
|
|
|
/*************************************************
|
|
Func:
|
|
EnumAddressCallback
|
|
Abstract:
|
|
Return remote player's IP address.
|
|
**************************************************/
|
|
BOOL FAR PASCAL
|
|
EnumAddressCallback(REFGUID guidDataType,
|
|
DWORD dwDataSize,
|
|
LPCVOID lpData,
|
|
LPVOID lpContext)
|
|
{
|
|
if (guidDataType == DPAID_INet)
|
|
{
|
|
strcpy((char*)lpContext, (char*)lpData);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/**********************************************
|
|
Func:
|
|
Constructor
|
|
Abstract:
|
|
Initialize member variables
|
|
***********************************************/
|
|
CDirectPlayConnection::CDirectPlayConnection()
|
|
{
|
|
m_rLobby = NULL;
|
|
m_rSettings = NULL;
|
|
m_rDirectPlay = NULL;
|
|
m_bConnected = FALSE;
|
|
m_PlayerID = 0;
|
|
m_hEventHandle = NULL;
|
|
m_bstrLocalName = NULL;
|
|
m_idOtherPlayer = 0;
|
|
}
|
|
|
|
/**********************************************
|
|
Func:
|
|
Destructor
|
|
Abstract:
|
|
Clean up left memory
|
|
***********************************************/
|
|
CDirectPlayConnection::~CDirectPlayConnection()
|
|
{
|
|
DisconnectRemoteApplication();
|
|
|
|
if (m_bstrLocalName)
|
|
SysFreeString(m_bstrLocalName);
|
|
}
|
|
|
|
/*********************************************************
|
|
Func:
|
|
ConnectToRemoteApplication
|
|
Abstract:
|
|
Start DP connection
|
|
**********************************************************/
|
|
HRESULT CDirectPlayConnection::ConnectToRemoteApplication()
|
|
{
|
|
DWORD dwSize = 0;
|
|
HRESULT hr;
|
|
|
|
if (!m_rLobby)
|
|
{
|
|
hr = CoCreateInstance(CLSID_DirectPlayLobby,NULL,CLSCTX_ALL,
|
|
IID_IDirectPlayLobby,(LPVOID*)&m_rLobby);
|
|
|
|
if (hr != S_OK)
|
|
goto done;
|
|
}
|
|
|
|
if (!m_hEventHandle)
|
|
{
|
|
m_hEventHandle = CreateEvent(NULL,FALSE,FALSE,NULL);
|
|
if (!m_hEventHandle)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
hr = m_rLobby->GetConnectionSettings(0,NULL,&dwSize);
|
|
if (hr != DPERR_BUFFERTOOSMALL) // Unknown error.
|
|
{
|
|
DP_ERR(hr);
|
|
goto done;
|
|
}
|
|
|
|
m_rSettings = (DPLCONNECTION *)new BYTE [dwSize];
|
|
if (!m_rSettings)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto done;
|
|
}
|
|
|
|
hr = m_rLobby->GetConnectionSettings(0,m_rSettings,&dwSize);
|
|
if (hr != DP_OK)
|
|
{
|
|
DP_ERR(hr);
|
|
goto done;
|
|
}
|
|
|
|
hr = m_rLobby->Connect(0,&m_rDirectPlay,NULL);
|
|
if (hr != DP_OK)
|
|
{
|
|
DP_ERR(hr);
|
|
goto done;
|
|
}
|
|
|
|
if (SUCCEEDED(GetLocalPlayerName()))
|
|
{
|
|
DPNAME DPName;
|
|
DPName.dwSize=sizeof(DPNAME);
|
|
DPName.dwFlags = 0;
|
|
DPName.lpszShortName = m_bstrLocalName;
|
|
hr = m_rDirectPlay->CreatePlayer(&m_PlayerID,&DPName,m_hEventHandle,NULL,0,0);
|
|
}
|
|
else
|
|
hr = m_rDirectPlay->CreatePlayer(&m_PlayerID,NULL,m_hEventHandle,NULL,0,0);
|
|
|
|
if (hr != DP_OK)
|
|
goto done;
|
|
|
|
m_bConnected = TRUE;
|
|
|
|
done:
|
|
return hr;
|
|
}
|
|
|
|
/**********************************************************
|
|
Func:
|
|
DisconnectRemoteApplication
|
|
Abstract:
|
|
Disconnect DP and clean up
|
|
***********************************************************/
|
|
HRESULT CDirectPlayConnection::DisconnectRemoteApplication()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (m_rDirectPlay)
|
|
{
|
|
m_rDirectPlay->Close();
|
|
m_rDirectPlay->Release();
|
|
m_rDirectPlay = NULL;
|
|
}
|
|
|
|
if (m_rLobby)
|
|
{
|
|
m_rLobby->Release();
|
|
m_rLobby = NULL;
|
|
}
|
|
|
|
if (m_hEventHandle)
|
|
{
|
|
CloseHandle(m_hEventHandle);
|
|
m_hEventHandle = NULL;
|
|
}
|
|
|
|
if (m_rSettings)
|
|
{
|
|
delete m_rSettings;
|
|
m_rSettings = NULL;
|
|
}
|
|
|
|
m_idOtherPlayer = 0;
|
|
|
|
m_bConnected = FALSE;
|
|
return hr;
|
|
}
|
|
|
|
/*********************************************
|
|
Func:
|
|
SendConnectionParameters
|
|
Abstract:
|
|
Send data to remote player
|
|
Params:
|
|
parms: A BSTR data string to send
|
|
**********************************************/
|
|
HRESULT CDirectPlayConnection::SendConnectionParameters(BSTR parms)
|
|
{
|
|
HRESULT hr;
|
|
|
|
DWORD dwCount;
|
|
|
|
if (!m_bConnected)
|
|
{
|
|
hr = S_FALSE;
|
|
goto done;
|
|
}
|
|
|
|
if (m_idOtherPlayer == 0 && (FAILED(GetOtherPlayerID(&m_idOtherPlayer))))
|
|
{
|
|
hr = S_FALSE; // no other player
|
|
goto done;
|
|
}
|
|
|
|
// We don't support Secured session now, leave the code here for future usage.
|
|
/************************
|
|
if (m_rSettings->lpSessionDesc->dwFlags & DPSESSION_SECURESERVER)
|
|
{
|
|
hr = m_rDirectPlay->Send(m_PlayerID,
|
|
m_idOtherPlayer,
|
|
DPSEND_ENCRYPTED|DPSEND_GUARANTEED|DPSEND_SIGNED,
|
|
parms,
|
|
dwCount);
|
|
if (hr != DP_OK)
|
|
goto done;
|
|
}
|
|
else
|
|
*************************/
|
|
{
|
|
dwCount = (wcslen(parms)+1) * sizeof(OLECHAR);
|
|
LPDP_MY_DATA pConnectParms = (LPDP_MY_DATA)new BYTE[sizeof(DP_MY_DATA) + dwCount];
|
|
if (!pConnectParms)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto done;
|
|
}
|
|
pConnectParms->dwType = DP_MY_DATATYPE;
|
|
pConnectParms->dwDataSize = dwCount;
|
|
CopyMemory(pConnectParms->pData, (LPVOID)parms, dwCount);
|
|
hr = m_rDirectPlay->Send(m_PlayerID,
|
|
m_idOtherPlayer,
|
|
DPSEND_GUARANTEED,
|
|
pConnectParms,
|
|
sizeof(DP_MY_DATA) + dwCount);
|
|
|
|
delete pConnectParms;
|
|
}
|
|
|
|
done:
|
|
return hr;
|
|
}
|
|
|
|
/************************************************
|
|
Func:
|
|
ReceiveConnectionParameters
|
|
Abstract:
|
|
Receive data from remote player
|
|
Return:
|
|
BSTR data string from remote player
|
|
**************************************************/
|
|
HRESULT CDirectPlayConnection::ReceiveConnectionParameters( BSTR* pBstr)
|
|
{
|
|
DWORD dwSize = 0;
|
|
DWORD endWaitTime;
|
|
DWORD dwResult;
|
|
HRESULT hr = S_OK;
|
|
LPDP_MY_DATA pData = NULL;
|
|
|
|
if (!m_bConnected)
|
|
return S_FALSE; // not connected
|
|
|
|
if (m_idOtherPlayer == 0 && FAILED(GetOtherPlayerID(&m_idOtherPlayer)))
|
|
{
|
|
hr = S_FALSE; // No other player
|
|
goto done;
|
|
}
|
|
|
|
endWaitTime = GetTickCount() + GETPARMSTIMEOUT; // 1 minute
|
|
|
|
while (1)
|
|
{
|
|
hr = m_rDirectPlay->Receive(&m_idOtherPlayer,
|
|
&m_PlayerID,
|
|
DPRECEIVE_FROMPLAYER | DPRECEIVE_TOPLAYER,
|
|
NULL,
|
|
&dwSize);
|
|
|
|
if (hr == DPERR_BUFFERTOOSMALL && dwSize >= sizeof(DP_MY_DATA))
|
|
{
|
|
// Have received some meaningful message...
|
|
pData = (LPDP_MY_DATA) new BYTE[dwSize];
|
|
if (!pData)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto done;
|
|
}
|
|
|
|
hr = m_rDirectPlay->Receive(&m_idOtherPlayer,
|
|
&m_PlayerID,
|
|
DPRECEIVE_FROMPLAYER | DPRECEIVE_TOPLAYER,
|
|
(LPVOID)pData,
|
|
&dwSize);
|
|
if (hr != DP_OK)
|
|
goto done;
|
|
|
|
if (pData->dwType == DP_MY_DATATYPE)
|
|
{
|
|
*pBstr = SysAllocString((WCHAR*)pData->pData);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If we can't get the message in pre-defined duration, then we failed.
|
|
if (endWaitTime < GetTickCount())
|
|
{
|
|
hr = E_FAIL;
|
|
goto done;
|
|
}
|
|
|
|
// Wait for another message to come.
|
|
dwResult = WaitForSingleObject(m_hEventHandle, GETPARMSTIMEOUT); // Wait 60 seconds.
|
|
if (dwResult != WAIT_OBJECT_0)
|
|
{
|
|
hr = E_FAIL; // Timed out.
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
done:
|
|
if (pData)
|
|
delete pData;
|
|
|
|
return hr;
|
|
}
|
|
|
|
BOOL CDirectPlayConnection::IsServer()
|
|
{
|
|
if (!m_bConnected)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return (m_rSettings->dwFlags & DPLCONNECTION_CREATESESSION);
|
|
}
|
|
|
|
/***************************************
|
|
Func:
|
|
GetOtherPlayerID
|
|
Abstract:
|
|
Find the other player's ID
|
|
Params:
|
|
*pID: the return ID.
|
|
****************************************/
|
|
HRESULT CDirectPlayConnection::GetOtherPlayerID(DPID* pID)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DPID ID = 0;
|
|
|
|
hr = m_rDirectPlay->EnumPlayers(NULL, EnumPlayersCallback, &ID, /*&playerData,*/ DPENUMPLAYERS_REMOTE);
|
|
if (hr == DP_OK && ID)
|
|
{
|
|
*pID = ID;
|
|
return hr;
|
|
}
|
|
|
|
// OK. The other player is not in yet. Waiting for the System msg that tells when he's ready.
|
|
LPDPMSG_GENERIC lpMsg = NULL;
|
|
LPDPMSG_CREATEPLAYERORGROUP lpCreateMsg;
|
|
DWORD dwResult;
|
|
DWORD timeOutTime;
|
|
timeOutTime = GetTickCount() + GETREMOTEPLAYERTIMEOUT; // Wait 60 seconds.
|
|
|
|
hr = E_FAIL;
|
|
|
|
while (1)
|
|
{
|
|
// Listen to all system messages to catch the Create Player msg.
|
|
while (lpMsg = ReceiveMessage(DPID_SYSMSG,m_PlayerID,
|
|
DPRECEIVE_TOPLAYER|DPRECEIVE_FROMPLAYER))
|
|
{
|
|
if (lpMsg->dwType == DPSYS_CREATEPLAYERORGROUP)
|
|
{
|
|
lpCreateMsg = (LPDPMSG_CREATEPLAYERORGROUP)lpMsg;
|
|
if (lpCreateMsg->dwPlayerType == DPPLAYERTYPE_PLAYER &&
|
|
lpCreateMsg->dpId != m_PlayerID)
|
|
{
|
|
*pID = lpCreateMsg->dpId;
|
|
delete lpMsg;
|
|
hr = S_OK;
|
|
|
|
goto done;
|
|
}
|
|
}
|
|
delete lpMsg;
|
|
}
|
|
|
|
if (timeOutTime < GetTickCount()) // Timeout.
|
|
break;
|
|
|
|
// Wait for next message.
|
|
dwResult = WaitForSingleObject(m_hEventHandle,GETREMOTEPLAYERTIMEOUT);
|
|
if (dwResult != WAIT_OBJECT_0) // Timeout.
|
|
goto done;
|
|
}
|
|
|
|
done:
|
|
return hr;
|
|
}
|
|
|
|
/***************************************************
|
|
Func:
|
|
GetLocalPlayerName
|
|
Abstract:
|
|
Return the user name of local machine.
|
|
****************************************************/
|
|
HRESULT CDirectPlayConnection::GetLocalPlayerName()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
// Clean up local name buffer.
|
|
if (m_bstrLocalName)
|
|
{
|
|
SysFreeString(m_bstrLocalName);
|
|
m_bstrLocalName = NULL;
|
|
}
|
|
|
|
#if USE_IM_OBJECT // Don't use the IM objects.
|
|
IMsgrObject *pObj;
|
|
|
|
hr = CoCreateInstance(CLSID_MsgrObject, //Class identifier (CLSID) of the object
|
|
NULL, //Pointer to controlling IUnknown
|
|
CLSCTX_INPROC_SERVER, //Context for running executable code
|
|
IID_IMsgrObject, //Reference to the identifier of the interface
|
|
(LPVOID*)&pObj); //Address of output variable that receives
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pObj->get_LocalFriendlyName(&m_bstrLocalName);
|
|
pObj->Release();
|
|
goto done;
|
|
}
|
|
#else // Use the local username variable
|
|
WCHAR szName[256];
|
|
DWORD dw = GetEnvironmentVariable(L"USERNAME", szName, 255);
|
|
if (dw == 0)
|
|
{
|
|
hr = E_FAIL;
|
|
goto done;
|
|
}
|
|
|
|
if (dw > 255)
|
|
{
|
|
WCHAR *p = new WCHAR[dw+1];
|
|
if (!p)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto done;
|
|
}
|
|
|
|
GetEnvironmentVariable(L"USERNAME", p, dw);
|
|
m_bstrLocalName = SysAllocString(p);
|
|
delete p;
|
|
}
|
|
else
|
|
m_bstrLocalName = SysAllocString(szName);
|
|
|
|
#endif
|
|
done:
|
|
return hr;
|
|
}
|
|
|
|
/*****************************************
|
|
Func:
|
|
GetOtherPlayerName
|
|
Abstract:
|
|
Return the name on the other side of connection
|
|
Params:
|
|
pName: buffer. If other player doesn't have a name, a null string will be returned.
|
|
pdwCount: buffer size in char.
|
|
******************************************/
|
|
HRESULT CDirectPlayConnection::GetOtherPlayerName(TCHAR* pName, DWORD* pdwCount)
|
|
{
|
|
DPID ID;
|
|
HRESULT hr = E_FAIL;
|
|
LPBYTE pBuffer = NULL;
|
|
DWORD dwOriSize = *pdwCount;
|
|
|
|
*pdwCount = 0;
|
|
|
|
if (m_idOtherPlayer == 0)
|
|
hr = GetOtherPlayerID(&m_idOtherPlayer);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
DWORD dwSize = 0;
|
|
hr = m_rDirectPlay->GetPlayerName(m_idOtherPlayer, (LPVOID)pBuffer, &dwSize);
|
|
if (hr == DPERR_BUFFERTOOSMALL)
|
|
{
|
|
pBuffer = new BYTE[dwSize];
|
|
if (!pBuffer)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto done;
|
|
}
|
|
|
|
hr = m_rDirectPlay->GetPlayerName(m_idOtherPlayer, (LPVOID)pBuffer, &dwSize);
|
|
if (hr == DP_OK)
|
|
{
|
|
LPTSTR p = ((DPNAME*)pBuffer)->lpszShortName;
|
|
if (p && *p != _T('\0'))
|
|
{
|
|
dwSize = _tcslen(p);
|
|
if (dwSize > dwOriSize)
|
|
{
|
|
hr = DPERR_BUFFERTOOSMALL;
|
|
goto done;
|
|
}
|
|
|
|
_stprintf(pName, p);
|
|
*pdwCount = dwSize;
|
|
goto done;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
done:
|
|
if (pBuffer)
|
|
delete pBuffer;
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This reads the next message for the given from and to addresses.
|
|
*/
|
|
LPDPMSG_GENERIC CDirectPlayConnection::ReceiveMessage(DPID from,DPID to,DWORD dwFlags)
|
|
{
|
|
HRESULT hr;
|
|
LPVOID lpMsg = 0x0;
|
|
DWORD dwSize = 0;
|
|
|
|
hr = m_rDirectPlay->Receive(&from,&to,dwFlags,NULL,&dwSize);
|
|
if (hr != DPERR_BUFFERTOOSMALL)
|
|
goto done; //Either no message or failed
|
|
|
|
lpMsg = (LPVOID)new BYTE [dwSize];
|
|
if (!lpMsg) //"Out of memory.
|
|
goto done;
|
|
|
|
hr = m_rDirectPlay->Receive(&from,&to,dwFlags,lpMsg,&dwSize);
|
|
if (hr != DP_OK)
|
|
{
|
|
//"Error receiving message.";
|
|
goto done;
|
|
}
|
|
|
|
return (LPDPMSG_GENERIC)lpMsg;
|
|
|
|
done:
|
|
|
|
if (lpMsg) {
|
|
delete lpMsg;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* Do we need this function ???*/
|
|
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;
|
|
}
|
|
|
|
|
|
|