1724 lines
61 KiB
C++
1724 lines
61 KiB
C++
/****************************************************************************
|
|
*
|
|
* File: testnet.cpp
|
|
* Project: DxDiag (DirectX Diagnostic Tool)
|
|
* Author: Jason Sandlin (jasonsa@microsoft.com)
|
|
* Purpose: Test DPlay8 functionality on this machine
|
|
*
|
|
* (C) Copyright 2000-2001 Microsoft Corp. All rights reserved.
|
|
*
|
|
****************************************************************************/
|
|
#define INITGUID
|
|
#include <Windows.h>
|
|
#include <multimon.h>
|
|
#include <dplay8.h>
|
|
#include <tchar.h>
|
|
#include <wchar.h>
|
|
#include <dplobby.h>
|
|
#include <mmsystem.h>
|
|
#include "reginfo.h"
|
|
#include "sysinfo.h"
|
|
#include "dispinfo.h"
|
|
#include "netinfo.h"
|
|
#include "testnet.h"
|
|
#include "resource.h"
|
|
|
|
#ifndef ReleasePpo
|
|
#define ReleasePpo(ppo) \
|
|
if (*(ppo) != NULL) \
|
|
{ \
|
|
(*(ppo))->Release(); \
|
|
*(ppo) = NULL; \
|
|
} \
|
|
else (VOID)0
|
|
#endif
|
|
|
|
#define TIMER_WAIT_CONNECT_COMPLETE 0
|
|
#define TIMER_UPDATE_SESSION_LIST 1
|
|
|
|
enum TESTID
|
|
{
|
|
TESTID_COINITIALIZE = 1,
|
|
TESTID_CREATEDPLAY,
|
|
TESTID_ADDRESSING,
|
|
TESTID_ENUMSESSIONS,
|
|
TESTID_ENUMPLAYERS,
|
|
TESTID_SENDCHATMESSAGE,
|
|
TESTID_RECEIVE,
|
|
TESTID_SETPEERINFO,
|
|
TESTID_CREATESESSION,
|
|
TESTID_JOINSESSION,
|
|
};
|
|
|
|
struct DPHostEnumInfo
|
|
{
|
|
DPN_APPLICATION_DESC* pAppDesc;
|
|
IDirectPlay8Address* pHostAddr;
|
|
IDirectPlay8Address* pDeviceAddr;
|
|
TCHAR szSession[MAX_PATH];
|
|
DWORD dwLastPollTime;
|
|
BOOL bValid;
|
|
DPHostEnumInfo* pNext;
|
|
};
|
|
|
|
#define MAX_CHAT_STRING_LENGTH 200
|
|
#define MAX_PLAYER_NAME MAX_PATH
|
|
#define MAX_CHAT_STRING (MAX_PLAYER_NAME + MAX_CHAT_STRING_LENGTH + 32)
|
|
|
|
struct APP_PLAYER_INFO
|
|
{
|
|
LONG lRefCount; // Ref count so we can cleanup when all threads
|
|
// are done w/ this object
|
|
DPNID dpnidPlayer; // DPNID of player
|
|
WCHAR strPlayerName[MAX_PLAYER_NAME]; // Player name
|
|
};
|
|
|
|
#define GAME_MSGID_CHAT 1
|
|
|
|
// Change compiler pack alignment to be BYTE aligned, and pop the current value
|
|
#pragma pack( push, 1 )
|
|
|
|
UNALIGNED struct GAMEMSG_GENERIC
|
|
{
|
|
WORD nType;
|
|
};
|
|
|
|
UNALIGNED struct GAMEMSG_CHAT : public GAMEMSG_GENERIC
|
|
{
|
|
WCHAR strChatString[MAX_CHAT_STRING_LENGTH];
|
|
};
|
|
|
|
// Pop the old pack alignment
|
|
#pragma pack( pop )
|
|
|
|
struct APP_QUEUE_CHAT_MSG
|
|
{
|
|
WCHAR strChatBuffer[MAX_CHAT_STRING];
|
|
};
|
|
|
|
struct APP_PLAYER_MSG
|
|
{
|
|
WCHAR strPlayerName[MAX_PATH]; // Player name
|
|
};
|
|
|
|
#define WM_APP_CHAT (WM_APP + 1)
|
|
#define WM_APP_LEAVE (WM_APP + 2)
|
|
#define WM_APP_JOIN (WM_APP + 3)
|
|
#define WM_APP_CONNECTING (WM_APP + 4)
|
|
#define WM_APP_CONNECTED (WM_APP + 5)
|
|
|
|
BOOL BTranslateError(HRESULT hr, TCHAR* psz, BOOL bEnglish = FALSE); // from main.cpp (yuck)
|
|
|
|
static INT_PTR CALLBACK SetupDialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
static BOOL FAR PASCAL EnumConnectionsCallback(LPCGUID lpguidSP, VOID* pvConnection,
|
|
DWORD dwConnectionSize, LPCDPNAME pName, DWORD dwFlags, VOID* pvContext);
|
|
static INT_PTR CALLBACK SessionsDialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
static VOID SessionsDlgInitListbox( HWND hDlg );
|
|
static VOID SessionsDlgNoteEnumResponse( PDPNMSG_ENUM_HOSTS_RESPONSE pEnumHostsResponseMsg );
|
|
static VOID SessionsDlgUpdateSessionList(HWND hDlg);
|
|
static VOID SessionsDlgEnumListCleanup();
|
|
|
|
static HRESULT InitDirectPlay( BOOL* pbCoInitializeDone );
|
|
static HRESULT InitDirectPlayAddresses();
|
|
static HRESULT InitSession();
|
|
static VOID LoadStringWide( int nID, WCHAR* szWide );
|
|
|
|
static BOOL FAR PASCAL EnumSessionsCallback(LPCDPSESSIONDESC2 pdpsd,
|
|
DWORD* pdwTimeout, DWORD dwFlags, VOID* pvContext);
|
|
static INT_PTR CALLBACK ChatDialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
static VOID ShowTextString(HWND hDlg, WCHAR* sz );
|
|
static HRESULT SendChatMessage( TCHAR* szMessage );
|
|
static HRESULT WINAPI DirectPlayMessageHandler( PVOID pvUserContext, DWORD dwMessageId, PVOID pMsgBuffer );
|
|
static BOOL ConvertStringToGUID(const TCHAR* strBuffer, GUID* lpguid);
|
|
static VOID ConvertGenericStringToWide( WCHAR* wstrDestination, const TCHAR* tstrSource, int cchDestChar = -1 );
|
|
static VOID ConvertWideStringToGeneric( TCHAR* tstrDestination, const WCHAR* wstrSource, int cchDestChar );
|
|
static VOID ConvertWideStringToAnsi( CHAR* strDestination, const WCHAR* wstrSource, int cchDestChar );
|
|
|
|
static const GUID s_guidDPTest = // {61EF80DA-691B-4247-9ADD-1C7BED2BC13E}
|
|
{ 0x61ef80da, 0x691b, 0x4247, { 0x9a, 0xdd, 0x1c, 0x7b, 0xed, 0x2b, 0xc1, 0x3e } };
|
|
|
|
static NetInfo* s_pNetInfo = NULL;
|
|
static IDirectPlay8Peer* s_pDP = NULL;
|
|
static TCHAR s_szPlayerName[100];
|
|
static TCHAR s_szSessionName[100];
|
|
static DWORD s_dwPort = 0;
|
|
static NetSP* s_pNetSP = NULL;
|
|
static BOOL s_bCreateSession = FALSE;
|
|
static DPHostEnumInfo* s_pSelectedSession = NULL;
|
|
static DPHostEnumInfo s_DPHostEnumHead;
|
|
static IDirectPlay8Address* s_pDeviceAddress = NULL;
|
|
static IDirectPlay8Address* s_pHostAddress = NULL;
|
|
static DPNHANDLE s_hEnumAsyncOp = NULL;
|
|
static DWORD s_dwEnumHostExpireInterval = 0;
|
|
static BOOL s_bEnumListChanged = FALSE;
|
|
static BOOL s_bConnecting = FALSE;
|
|
static DPHostEnumInfo* s_pDPHostEnumSelected = NULL;
|
|
static CRITICAL_SECTION s_csHostEnum;
|
|
static DPNID s_dpnidLocalPlayer = 0;
|
|
static LONG s_lNumberOfActivePlayers = 0;
|
|
static HWND s_hDlg = NULL;
|
|
static HWND s_hwndSessionDlg = NULL;
|
|
static DPNHANDLE s_hConnectAsyncOp = NULL;
|
|
static HRESULT s_hrConnectComplete = S_OK;
|
|
static HANDLE s_hConnectCompleteEvent = NULL;
|
|
|
|
|
|
static CRITICAL_SECTION s_csPlayerContext;
|
|
#define PLAYER_LOCK() EnterCriticalSection( &s_csPlayerContext );
|
|
#define PLAYER_ADDREF( pPlayerInfo ) if( pPlayerInfo ) pPlayerInfo->lRefCount++;
|
|
#define PLAYER_RELEASE( pPlayerInfo ) if( pPlayerInfo ) { pPlayerInfo->lRefCount--; if( pPlayerInfo->lRefCount <= 0 ) delete pPlayerInfo; } pPlayerInfo = NULL;
|
|
#define PLAYER_UNLOCK() LeaveCriticalSection( &s_csPlayerContext );
|
|
|
|
|
|
/****************************************************************************
|
|
*
|
|
* TestNetwork
|
|
*
|
|
****************************************************************************/
|
|
VOID TestNetwork(HWND hwndMain, NetInfo* pNetInfo)
|
|
{
|
|
BOOL bCoInitializeDone = FALSE;
|
|
TCHAR sz[300];
|
|
HINSTANCE hinst = (HINSTANCE)GetWindowLongPtr(hwndMain, GWLP_HINSTANCE);
|
|
|
|
s_pNetInfo = pNetInfo;
|
|
|
|
// Remove info from any previous test:
|
|
ZeroMemory(&s_pNetInfo->m_testResult, sizeof(TestResult));
|
|
s_pNetInfo->m_testResult.m_bStarted = TRUE;
|
|
|
|
// Setup the s_DPHostEnumHead circular linked list
|
|
ZeroMemory( &s_DPHostEnumHead, sizeof( DPHostEnumInfo ) );
|
|
s_DPHostEnumHead.pNext = &s_DPHostEnumHead;
|
|
|
|
InitializeCriticalSection( &s_csHostEnum );
|
|
InitializeCriticalSection( &s_csPlayerContext );
|
|
s_hConnectCompleteEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
|
|
|
|
// Setup s_pDP, and mark installed SP's
|
|
if( FAILED( InitDirectPlay( &bCoInitializeDone ) ) )
|
|
goto LEnd;
|
|
|
|
// Show setup dialog. This will tell us:
|
|
// - service provider
|
|
// - player name
|
|
// - either create or join
|
|
// - game name (if creating)
|
|
// - port (if SP=TCP/IP)
|
|
DialogBox(hinst, MAKEINTRESOURCE(IDD_TESTNETSETUP), hwndMain, SetupDialogProc);
|
|
|
|
if (s_pNetSP == NULL)
|
|
{
|
|
// Something weird happened...no service provider chosen
|
|
goto LEnd;
|
|
}
|
|
|
|
// At this point s_szPlayerName, s_szSessionName, s_pNetSP, s_dwPort,
|
|
// and s_bCreateSession have been initialized
|
|
|
|
// Setup s_dwEnumHostExpireInterval, s_pDeviceAddress, and s_pHostAddress
|
|
if( FAILED( InitDirectPlayAddresses() ) )
|
|
goto LEnd;
|
|
|
|
// Now s_dwEnumHostExpireInterval, s_pDeviceAddress, and s_pHostAddress
|
|
// have been initialized
|
|
|
|
// Session list window (if joining session)
|
|
if( !s_bCreateSession )
|
|
{
|
|
// Open a dialog to choose which host to connect to
|
|
DialogBox(hinst, MAKEINTRESOURCE(IDD_TESTNETSESSIONS), hwndMain, SessionsDialogProc);
|
|
// Now s_pDPHostEnumSelected will be NULL or valid
|
|
|
|
if( FAILED(s_pNetInfo->m_testResult.m_hr) || s_pDPHostEnumSelected == NULL )
|
|
goto LEnd;
|
|
|
|
// Now s_pDPHostEnumSelected is valid
|
|
}
|
|
|
|
// Launch chat window and host or join session
|
|
DialogBox(hinst, MAKEINTRESOURCE(IDD_TESTNETCHAT), hwndMain, ChatDialogProc);
|
|
|
|
LEnd:
|
|
s_pNetSP = NULL;
|
|
ReleasePpo( &s_pDeviceAddress );
|
|
ReleasePpo( &s_pHostAddress );
|
|
if( s_hEnumAsyncOp )
|
|
s_pDP->CancelAsyncOperation( s_hEnumAsyncOp, 0 );
|
|
ReleasePpo(&s_pDP);
|
|
if (bCoInitializeDone)
|
|
CoUninitialize(); // Release COM
|
|
DeleteCriticalSection( &s_csHostEnum );
|
|
DeleteCriticalSection( &s_csPlayerContext );
|
|
CloseHandle( s_hConnectCompleteEvent );
|
|
|
|
if (s_pNetInfo->m_testResult.m_bCancelled)
|
|
{
|
|
LoadString(NULL, IDS_TESTSCANCELLED, sz, 300);
|
|
lstrcpy(s_pNetInfo->m_testResult.m_szDescription, sz);
|
|
|
|
LoadString(NULL, IDS_TESTSCANCELLED_ENGLISH, sz, 300);
|
|
lstrcpy(s_pNetInfo->m_testResult.m_szDescriptionEnglish, sz);
|
|
}
|
|
else if (s_pNetInfo->m_testResult.m_iStepThatFailed == 0)
|
|
{
|
|
LoadString(NULL, IDS_TESTSSUCCESSFUL, sz, 300);
|
|
lstrcpy(s_pNetInfo->m_testResult.m_szDescription, sz);
|
|
|
|
LoadString(NULL, IDS_TESTSSUCCESSFUL_ENGLISH, sz, 300);
|
|
lstrcpy(s_pNetInfo->m_testResult.m_szDescriptionEnglish, sz);
|
|
}
|
|
else
|
|
{
|
|
TCHAR szDesc[300];
|
|
TCHAR szError[300];
|
|
if (0 == LoadString(NULL, IDS_FIRSTDPLAYTESTERROR +
|
|
s_pNetInfo->m_testResult.m_iStepThatFailed - 1, szDesc, 200))
|
|
{
|
|
LoadString(NULL, IDS_UNKNOWNERROR, sz, 300);
|
|
lstrcpy(szDesc, sz);
|
|
}
|
|
LoadString(NULL, IDS_FAILUREFMT, sz, 300);
|
|
BTranslateError(s_pNetInfo->m_testResult.m_hr, szError);
|
|
wsprintf(s_pNetInfo->m_testResult.m_szDescription, sz,
|
|
s_pNetInfo->m_testResult.m_iStepThatFailed,
|
|
szDesc, s_pNetInfo->m_testResult.m_hr, szError);
|
|
|
|
// Nonlocalized version:
|
|
if (0 == LoadString(NULL, IDS_FIRSTDPLAYTESTERROR_ENGLISH +
|
|
s_pNetInfo->m_testResult.m_iStepThatFailed - 1, szDesc, 200))
|
|
{
|
|
LoadString(NULL, IDS_UNKNOWNERROR_ENGLISH, sz, 300);
|
|
lstrcpy(szDesc, sz);
|
|
}
|
|
LoadString(NULL, IDS_FAILUREFMT_ENGLISH, sz, 300);
|
|
BTranslateError(s_pNetInfo->m_testResult.m_hr, szError, TRUE);
|
|
wsprintf(s_pNetInfo->m_testResult.m_szDescriptionEnglish, sz,
|
|
s_pNetInfo->m_testResult.m_iStepThatFailed,
|
|
szDesc, s_pNetInfo->m_testResult.m_hr, szError);
|
|
}
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
*
|
|
* InitDirectPlay
|
|
*
|
|
****************************************************************************/
|
|
HRESULT InitDirectPlay( BOOL* pbCoInitializeDone )
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwItems = 0;
|
|
DWORD dwSize = 0;
|
|
DPN_SERVICE_PROVIDER_INFO* pdnSPInfoEnum = NULL;
|
|
DPN_SERVICE_PROVIDER_INFO* pdnSPInfo = NULL;
|
|
DWORD i;
|
|
|
|
// Initialize COM
|
|
if (FAILED(hr = CoInitialize(NULL)))
|
|
{
|
|
s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_COINITIALIZE;
|
|
s_pNetInfo->m_testResult.m_hr = hr;
|
|
return hr;
|
|
}
|
|
*pbCoInitializeDone = TRUE;
|
|
|
|
// Create DirectPlay object
|
|
if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay8Peer, NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IDirectPlay8Peer,
|
|
(LPVOID*) &s_pDP ) ) )
|
|
{
|
|
s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_CREATEDPLAY;
|
|
s_pNetInfo->m_testResult.m_hr = hr;
|
|
return hr;
|
|
}
|
|
|
|
// Init IDirectPlay8Peer
|
|
if( FAILED( hr = s_pDP->Initialize( NULL, DirectPlayMessageHandler, 0 ) ) )
|
|
{
|
|
s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_CREATEDPLAY;
|
|
s_pNetInfo->m_testResult.m_hr = hr;
|
|
return hr;
|
|
}
|
|
|
|
// Enumerate all DirectPlay service providers
|
|
// to figure out which are installed
|
|
hr = s_pDP->EnumServiceProviders( NULL, NULL, pdnSPInfo, &dwSize,
|
|
&dwItems, 0 );
|
|
if( hr != DPNERR_BUFFERTOOSMALL && FAILED(hr) )
|
|
{
|
|
s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_ADDRESSING;
|
|
s_pNetInfo->m_testResult.m_hr = hr;
|
|
return hr;
|
|
}
|
|
pdnSPInfo = (DPN_SERVICE_PROVIDER_INFO*) new BYTE[dwSize];
|
|
if( FAILED( hr = s_pDP->EnumServiceProviders( NULL, NULL, pdnSPInfo,
|
|
&dwSize, &dwItems, 0 ) ) )
|
|
{
|
|
s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_ADDRESSING;
|
|
s_pNetInfo->m_testResult.m_hr = hr;
|
|
if( pdnSPInfo )
|
|
delete[] pdnSPInfo;
|
|
return hr;
|
|
}
|
|
|
|
// Mark installed SP's as such
|
|
pdnSPInfoEnum = pdnSPInfo;
|
|
for ( i = 0; i < dwItems; i++ )
|
|
{
|
|
NetSP* pNetSP;
|
|
for (pNetSP = s_pNetInfo->m_pNetSPFirst; pNetSP != NULL;
|
|
pNetSP = pNetSP->m_pNetSPNext)
|
|
{
|
|
if( pNetSP->m_guid == pdnSPInfoEnum->guid )
|
|
{
|
|
pNetSP->m_bInstalled = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
pdnSPInfoEnum++;
|
|
}
|
|
|
|
if( pdnSPInfo )
|
|
delete[] pdnSPInfo;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
*
|
|
* SetupDialogProc
|
|
*
|
|
****************************************************************************/
|
|
INT_PTR CALLBACK SetupDialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
NetSP* pNetSP;
|
|
TCHAR sz[MAX_PATH];
|
|
HWND hwndList = GetDlgItem(hDlg, IDC_SPLIST);
|
|
LONG iItem;
|
|
LONG iSelect = LB_ERR;
|
|
|
|
for (pNetSP = s_pNetInfo->m_pNetSPFirst; pNetSP != NULL;
|
|
pNetSP = pNetSP->m_pNetSPNext)
|
|
{
|
|
if( pNetSP->m_dwDXVer == 8 && pNetSP->m_bInstalled )
|
|
{
|
|
iItem = (LONG)SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)(LPCTSTR)pNetSP->m_szName);
|
|
if ((LRESULT)iItem != LB_ERR)
|
|
{
|
|
SendMessage(hwndList, LB_SETITEMDATA, iItem, (LPARAM)pNetSP);
|
|
|
|
// Try to select TCP/IP by default
|
|
if( lstrcmpi(pNetSP->m_szGuid, TEXT("{EBFE7BA0-628D-11D2-AE0F-006097B01411}")) == 0)
|
|
iSelect = iItem;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Try to select the default preferred provider
|
|
if( iSelect != LB_ERR )
|
|
SendMessage( hwndList, LB_SETCURSEL, iSelect, 0 );
|
|
else
|
|
SendMessage( hwndList, LB_SETCURSEL, 0, 0 );
|
|
|
|
SendMessage(hDlg, WM_COMMAND, IDC_SPLIST, 0);
|
|
LoadString(NULL, IDS_DEFAULTUSERNAME, sz, MAX_PATH);
|
|
SetWindowText(GetDlgItem(hDlg, IDC_PLAYERNAME), sz);
|
|
LoadString(NULL, IDS_DEFAULTSESSIONNAME, sz, MAX_PATH);
|
|
SetWindowText(GetDlgItem(hDlg, IDC_SESSIONNAME), sz);
|
|
CheckRadioButton(hDlg, IDC_CREATESESSION, IDC_JOINSESSION, IDC_CREATESESSION);
|
|
return TRUE;
|
|
}
|
|
|
|
case WM_COMMAND:
|
|
{
|
|
switch(LOWORD(wParam))
|
|
{
|
|
case IDC_CREATESESSION:
|
|
{
|
|
EnableWindow(GetDlgItem(hDlg, IDC_SESSIONNAME), TRUE);
|
|
break;
|
|
}
|
|
|
|
case IDC_JOINSESSION:
|
|
{
|
|
EnableWindow(GetDlgItem(hDlg, IDC_SESSIONNAME), FALSE);
|
|
break;
|
|
}
|
|
|
|
case IDC_SPLIST:
|
|
{
|
|
HWND hwndList;
|
|
hwndList = GetDlgItem(hDlg, IDC_SPLIST);
|
|
LONG iItem;
|
|
iItem = (LONG)SendMessage(hwndList, LB_GETCURSEL, 0, 0);
|
|
NetSP* pNetSP = (NetSP*)SendMessage(hwndList, LB_GETITEMDATA, iItem, 0);
|
|
|
|
// Only enable the port if the selected SP == TCP/IP
|
|
if( pNetSP && lstrcmp( pNetSP->m_szGuid, TEXT("{EBFE7BA0-628D-11D2-AE0F-006097B01411}") ) == 0 )
|
|
{
|
|
EnableWindow( GetDlgItem(hDlg, IDC_PORT), TRUE );
|
|
EnableWindow( GetDlgItem(hDlg, IDC_PORT_TEXT), TRUE );
|
|
}
|
|
else
|
|
{
|
|
EnableWindow( GetDlgItem(hDlg, IDC_PORT), FALSE );
|
|
EnableWindow( GetDlgItem(hDlg, IDC_PORT_TEXT), FALSE );
|
|
}
|
|
break;
|
|
}
|
|
|
|
case IDOK:
|
|
{
|
|
// Set create/join option
|
|
if (IsDlgButtonChecked(hDlg, IDC_CREATESESSION))
|
|
s_bCreateSession = TRUE;
|
|
else
|
|
s_bCreateSession = FALSE;
|
|
|
|
// Get player name
|
|
GetWindowText(GetDlgItem(hDlg, IDC_PLAYERNAME), s_szPlayerName, 100);
|
|
if (lstrlen(s_szPlayerName) == 0)
|
|
{
|
|
TCHAR szMsg[MAX_PATH];
|
|
TCHAR szTitle[MAX_PATH];
|
|
LoadString(NULL, IDS_NEEDUSERNAME, szMsg, MAX_PATH);
|
|
LoadString(NULL, IDS_APPFULLNAME, szTitle, MAX_PATH);
|
|
MessageBox(hDlg, szMsg, szTitle, MB_OK);
|
|
break;
|
|
}
|
|
|
|
// Get port
|
|
TCHAR szPort[MAX_PATH];
|
|
GetDlgItemText( hDlg, IDC_PORT, szPort, MAX_PATH);
|
|
s_dwPort = _ttoi( szPort );
|
|
|
|
// Get session name
|
|
GetWindowText(GetDlgItem(hDlg, IDC_SESSIONNAME), s_szSessionName, 100);
|
|
if (s_bCreateSession && lstrlen(s_szSessionName) == 0)
|
|
{
|
|
TCHAR szMsg[MAX_PATH];
|
|
TCHAR szTitle[MAX_PATH];
|
|
LoadString(NULL, IDS_NEEDSESSIONNAME, szMsg, MAX_PATH);
|
|
LoadString(NULL, IDS_APPFULLNAME, szTitle, MAX_PATH);
|
|
MessageBox(hDlg, szMsg, szTitle, MB_OK);
|
|
break;
|
|
}
|
|
|
|
// Get sp
|
|
HWND hwndList;
|
|
hwndList = GetDlgItem(hDlg, IDC_SPLIST);
|
|
LONG iItem;
|
|
iItem = (LONG)SendMessage(hwndList, LB_GETCURSEL, 0, 0);
|
|
if ((LPARAM)iItem == LB_ERR)
|
|
{
|
|
s_pNetInfo->m_testResult.m_bCancelled = TRUE;
|
|
EndDialog(hDlg, 0);
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
s_pNetSP = (NetSP*)SendMessage(hwndList, LB_GETITEMDATA, iItem, 0);
|
|
}
|
|
EndDialog(hDlg, 1);
|
|
break;
|
|
}
|
|
|
|
case IDCANCEL:
|
|
{
|
|
EndDialog(hDlg, 0);
|
|
s_pNetInfo->m_testResult.m_bCancelled = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
*
|
|
* InitDirectPlayAddresses
|
|
*
|
|
****************************************************************************/
|
|
HRESULT InitDirectPlayAddresses()
|
|
{
|
|
HRESULT hr;
|
|
|
|
// Query for the enum host timeout for this SP
|
|
DPN_SP_CAPS dpspCaps;
|
|
ZeroMemory( &dpspCaps, sizeof(DPN_SP_CAPS) );
|
|
dpspCaps.dwSize = sizeof(DPN_SP_CAPS);
|
|
if( FAILED( hr = s_pDP->GetSPCaps( &s_pNetSP->m_guid, &dpspCaps, 0 ) ) )
|
|
{
|
|
s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_ADDRESSING;
|
|
s_pNetInfo->m_testResult.m_hr = hr;
|
|
return hr;
|
|
}
|
|
|
|
// Set the host expire time to around 3 times
|
|
// length of the dwDefaultEnumRetryInterval
|
|
s_dwEnumHostExpireInterval = dpspCaps.dwDefaultEnumRetryInterval * 3;
|
|
|
|
// Create a device address
|
|
ReleasePpo( &s_pDeviceAddress );
|
|
hr = CoCreateInstance( CLSID_DirectPlay8Address, NULL,CLSCTX_INPROC_SERVER,
|
|
IID_IDirectPlay8Address, (LPVOID*) &s_pDeviceAddress );
|
|
if( FAILED(hr) )
|
|
{
|
|
s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_ADDRESSING;
|
|
s_pNetInfo->m_testResult.m_hr = hr;
|
|
return hr;
|
|
}
|
|
|
|
if( FAILED( hr = s_pDeviceAddress->SetSP( &s_pNetSP->m_guid ) ) )
|
|
{
|
|
s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_ADDRESSING;
|
|
s_pNetInfo->m_testResult.m_hr = hr;
|
|
return hr;
|
|
}
|
|
|
|
// Create a host address
|
|
ReleasePpo( &s_pHostAddress );
|
|
hr = CoCreateInstance( CLSID_DirectPlay8Address, NULL,CLSCTX_INPROC_SERVER,
|
|
IID_IDirectPlay8Address, (LPVOID*) &s_pHostAddress );
|
|
if( FAILED(hr) )
|
|
{
|
|
s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_ADDRESSING;
|
|
s_pNetInfo->m_testResult.m_hr = hr;
|
|
return hr;
|
|
}
|
|
|
|
// Set the SP
|
|
if( FAILED( hr = s_pHostAddress->SetSP( &s_pNetSP->m_guid ) ) )
|
|
{
|
|
s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_ADDRESSING;
|
|
s_pNetInfo->m_testResult.m_hr = hr;
|
|
return hr;
|
|
}
|
|
|
|
// If TCP/IP then set the port if its non-zero
|
|
if( s_pNetSP->m_guid == CLSID_DP8SP_TCPIP )
|
|
{
|
|
if( s_bCreateSession )
|
|
{
|
|
if( s_dwPort > 0 )
|
|
{
|
|
// Add the port to pDeviceAddress
|
|
if( FAILED( hr = s_pDeviceAddress->AddComponent( DPNA_KEY_PORT,
|
|
&s_dwPort, sizeof(s_dwPort),
|
|
DPNA_DATATYPE_DWORD ) ) )
|
|
{
|
|
s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_ADDRESSING;
|
|
s_pNetInfo->m_testResult.m_hr = hr;
|
|
return hr;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( s_dwPort > 0 )
|
|
{
|
|
// Add the port to pHostAddress
|
|
if( FAILED( hr = s_pHostAddress->AddComponent( DPNA_KEY_PORT,
|
|
&s_dwPort, sizeof(s_dwPort),
|
|
DPNA_DATATYPE_DWORD ) ) )
|
|
{
|
|
s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_ADDRESSING;
|
|
s_pNetInfo->m_testResult.m_hr = hr;
|
|
return hr;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
*
|
|
* SessionsDialogProc
|
|
*
|
|
****************************************************************************/
|
|
INT_PTR CALLBACK SessionsDialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
HRESULT hr;
|
|
|
|
s_hwndSessionDlg = hDlg;
|
|
s_bEnumListChanged = TRUE;
|
|
|
|
// Enumerate hosts
|
|
DPN_APPLICATION_DESC dnAppDesc;
|
|
ZeroMemory( &dnAppDesc, sizeof(DPN_APPLICATION_DESC) );
|
|
dnAppDesc.dwSize = sizeof(DPN_APPLICATION_DESC);
|
|
dnAppDesc.guidApplication = s_guidDPTest;
|
|
|
|
// Enumerate all the active DirectPlay games on the selected connection
|
|
hr = s_pDP->EnumHosts( &dnAppDesc, // application description
|
|
s_pHostAddress, // host address
|
|
s_pDeviceAddress, // device address
|
|
NULL, // pointer to user data
|
|
0, // user data size
|
|
INFINITE, // retry count (forever)
|
|
0, // retry interval (0=default)
|
|
INFINITE, // time out (forever)
|
|
NULL, // user context
|
|
&s_hEnumAsyncOp, // async handle
|
|
DPNENUMHOSTS_OKTOQUERYFORADDRESSING // flags
|
|
);
|
|
if( FAILED(hr) )
|
|
{
|
|
s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_ENUMSESSIONS;
|
|
s_pNetInfo->m_testResult.m_hr = hr;
|
|
EndDialog(hDlg, 0);
|
|
return TRUE;
|
|
}
|
|
|
|
SessionsDlgInitListbox(hDlg);
|
|
SetTimer(hDlg, TIMER_UPDATE_SESSION_LIST, 250, NULL);
|
|
return TRUE;
|
|
}
|
|
|
|
case WM_TIMER:
|
|
{
|
|
if( wParam == TIMER_UPDATE_SESSION_LIST )
|
|
SessionsDlgUpdateSessionList(hDlg);
|
|
return TRUE;
|
|
}
|
|
|
|
case WM_COMMAND:
|
|
{
|
|
switch(LOWORD(wParam))
|
|
{
|
|
case IDOK:
|
|
{
|
|
HWND hwndList = GetDlgItem(hDlg, IDC_SESSIONLIST);
|
|
|
|
LONG iSelCur = (LONG)SendMessage(hwndList, LB_GETCURSEL, 0, 0);
|
|
if( iSelCur != LB_ERR )
|
|
{
|
|
// This will prevent s_pDPHostEnumSelected from being
|
|
// deleting due to SessionsDlgUpdateSessionList()
|
|
EnterCriticalSection( &s_csHostEnum );
|
|
s_pDPHostEnumSelected = (DPHostEnumInfo*)SendMessage( hwndList, LB_GETITEMDATA,
|
|
iSelCur, 0 );
|
|
|
|
if ( (LRESULT)s_pDPHostEnumSelected != LB_ERR &&
|
|
s_pDPHostEnumSelected != NULL )
|
|
{
|
|
// We keep the CS until we are done with s_pDPHostEnumSelected,
|
|
// otherwise it might change out from under us.
|
|
EndDialog(hDlg, 1);
|
|
break;
|
|
}
|
|
|
|
s_pDPHostEnumSelected = NULL;
|
|
LeaveCriticalSection( &s_csHostEnum );
|
|
}
|
|
|
|
s_pNetInfo->m_testResult.m_bCancelled = TRUE;
|
|
EndDialog(hDlg, 0);
|
|
break;
|
|
}
|
|
|
|
case IDCANCEL:
|
|
{
|
|
s_pDPHostEnumSelected = NULL;
|
|
s_pNetInfo->m_testResult.m_bCancelled = TRUE;
|
|
EndDialog(hDlg, 0);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
case WM_DESTROY:
|
|
{
|
|
KillTimer( hDlg, TIMER_UPDATE_SESSION_LIST );
|
|
s_hwndSessionDlg = NULL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
*
|
|
* SessionsDlgInitListbox
|
|
*
|
|
****************************************************************************/
|
|
VOID SessionsDlgInitListbox( HWND hDlg )
|
|
{
|
|
HWND hWndListBox = GetDlgItem( hDlg, IDC_SESSIONLIST );
|
|
|
|
LONG numChars;
|
|
TCHAR szFmt[200];
|
|
|
|
LoadString(NULL, IDS_LOOKINGFORSESSIONS, szFmt, 200);
|
|
numChars = (LONG)SendMessage(GetDlgItem(hDlg, IDC_CHATOUTPUT), WM_GETTEXTLENGTH, 0, 0);
|
|
SendMessage(GetDlgItem(hDlg, IDC_CHATOUTPUT), EM_SETSEL, numChars, numChars);
|
|
SendMessage(GetDlgItem(hDlg, IDC_CHATOUTPUT), EM_REPLACESEL,
|
|
FALSE, (LPARAM)szFmt);
|
|
|
|
// Clear the contents from the list box, and
|
|
// display "Looking for sessions" text in listbox
|
|
SendMessage( hWndListBox, LB_RESETCONTENT, 0, 0 );
|
|
SendMessage( hWndListBox, LB_SETITEMDATA, 0, NULL );
|
|
SendMessage( hWndListBox, LB_SETCURSEL, 0, 0 );
|
|
|
|
// Disable the join button until sessions are found
|
|
EnableWindow( GetDlgItem( hDlg, IDOK ), FALSE );
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
*
|
|
* SessionsDlgNoteEnumResponse
|
|
*
|
|
****************************************************************************/
|
|
VOID SessionsDlgNoteEnumResponse( PDPNMSG_ENUM_HOSTS_RESPONSE pEnumHostsResponseMsg )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL bFound;
|
|
|
|
// This function is called from the DirectPlay message handler so it could be
|
|
// called simultaneously from multiple threads, so enter a critical section
|
|
// to assure that it we don't get race conditions.
|
|
EnterCriticalSection( &s_csHostEnum );
|
|
|
|
DPHostEnumInfo* pDPHostEnum = s_DPHostEnumHead.pNext;
|
|
DPHostEnumInfo* pDPHostEnumNext = NULL;
|
|
const DPN_APPLICATION_DESC* pResponseMsgAppDesc =
|
|
pEnumHostsResponseMsg->pApplicationDescription;
|
|
|
|
// Look for a matching session instance GUID.
|
|
bFound = FALSE;
|
|
while ( pDPHostEnum != &s_DPHostEnumHead )
|
|
{
|
|
if( pResponseMsgAppDesc->guidInstance == pDPHostEnum->pAppDesc->guidInstance )
|
|
{
|
|
bFound = TRUE;
|
|
break;
|
|
}
|
|
|
|
pDPHostEnumNext = pDPHostEnum;
|
|
pDPHostEnum = pDPHostEnum->pNext;
|
|
}
|
|
|
|
if( !bFound )
|
|
{
|
|
s_bEnumListChanged = TRUE;
|
|
|
|
// If there's no match, then look for invalid session and use it
|
|
pDPHostEnum = s_DPHostEnumHead.pNext;
|
|
while ( pDPHostEnum != &s_DPHostEnumHead )
|
|
{
|
|
if( !pDPHostEnum->bValid )
|
|
break;
|
|
|
|
pDPHostEnum = pDPHostEnum->pNext;
|
|
}
|
|
|
|
// If no invalid sessions are found then make a new one
|
|
if( pDPHostEnum == &s_DPHostEnumHead )
|
|
{
|
|
// Found a new session, so create a new node
|
|
pDPHostEnum = new DPHostEnumInfo;
|
|
if( NULL == pDPHostEnum )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto LCleanup;
|
|
}
|
|
|
|
ZeroMemory( pDPHostEnum, sizeof(DPHostEnumInfo) );
|
|
|
|
// Add pDPHostEnum to the circular linked list, m_DPHostEnumHead
|
|
pDPHostEnum->pNext = s_DPHostEnumHead.pNext;
|
|
s_DPHostEnumHead.pNext = pDPHostEnum;
|
|
}
|
|
}
|
|
|
|
// Update the pDPHostEnum with new information
|
|
TCHAR strName[MAX_PATH];
|
|
if( pResponseMsgAppDesc->pwszSessionName )
|
|
ConvertWideStringToGeneric( strName, pResponseMsgAppDesc->pwszSessionName, MAX_PATH );
|
|
else
|
|
lstrcpy( strName, TEXT("???") );
|
|
|
|
// Cleanup any old enum
|
|
if( pDPHostEnum->pAppDesc )
|
|
{
|
|
delete[] pDPHostEnum->pAppDesc->pwszSessionName;
|
|
delete[] pDPHostEnum->pAppDesc;
|
|
}
|
|
ReleasePpo( &pDPHostEnum->pHostAddr );
|
|
ReleasePpo( &pDPHostEnum->pDeviceAddr );
|
|
pDPHostEnum->bValid = FALSE;
|
|
|
|
//
|
|
// Duplicate pEnumHostsResponseMsg->pAddressSender in pDPHostEnum->pHostAddr.
|
|
// Duplicate pEnumHostsResponseMsg->pAddressDevice in pDPHostEnum->pDeviceAddr.
|
|
//
|
|
if( FAILED( hr = pEnumHostsResponseMsg->pAddressSender->Duplicate( &pDPHostEnum->pHostAddr ) ) )
|
|
{
|
|
goto LCleanup;
|
|
}
|
|
|
|
if( FAILED( hr = pEnumHostsResponseMsg->pAddressDevice->Duplicate( &pDPHostEnum->pDeviceAddr ) ) )
|
|
{
|
|
goto LCleanup;
|
|
}
|
|
|
|
// Deep copy the DPN_APPLICATION_DESC from
|
|
pDPHostEnum->pAppDesc = new DPN_APPLICATION_DESC;
|
|
ZeroMemory( pDPHostEnum->pAppDesc, sizeof(DPN_APPLICATION_DESC) );
|
|
memcpy( pDPHostEnum->pAppDesc, pResponseMsgAppDesc, sizeof(DPN_APPLICATION_DESC) );
|
|
if( pResponseMsgAppDesc->pwszSessionName )
|
|
{
|
|
pDPHostEnum->pAppDesc->pwszSessionName = new WCHAR[ wcslen(pResponseMsgAppDesc->pwszSessionName)+1 ];
|
|
wcscpy( pDPHostEnum->pAppDesc->pwszSessionName,
|
|
pResponseMsgAppDesc->pwszSessionName );
|
|
}
|
|
|
|
// Update the time this was done, so that we can expire this host
|
|
// if it doesn't refresh w/in a certain amount of time
|
|
pDPHostEnum->dwLastPollTime = timeGetTime();
|
|
|
|
// if this node was previously invalidated, or the session name is now
|
|
// different the session list in the dialog needs to be updated
|
|
if( ( pDPHostEnum->bValid == FALSE ) ||
|
|
( _tcscmp( pDPHostEnum->szSession, strName ) != 0 ) )
|
|
{
|
|
s_bEnumListChanged = TRUE;
|
|
}
|
|
_tcscpy( pDPHostEnum->szSession, strName );
|
|
|
|
// This host is now valid
|
|
pDPHostEnum->bValid = TRUE;
|
|
|
|
LCleanup:
|
|
LeaveCriticalSection( &s_csHostEnum );
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
*
|
|
* SessionsDlgUpdateSessionList
|
|
*
|
|
****************************************************************************/
|
|
VOID SessionsDlgUpdateSessionList( HWND hDlg )
|
|
{
|
|
HWND hWndListBox = GetDlgItem(hDlg, IDC_SESSIONLIST);
|
|
DPHostEnumInfo* pDPHostEnum = NULL;
|
|
DPHostEnumInfo* pDPHostEnumSelected = NULL;
|
|
GUID guidSelectedInstance;
|
|
BOOL bFindSelectedGUID;
|
|
BOOL bFoundSelectedGUID;
|
|
int nItemSelected;
|
|
|
|
DWORD dwCurrentTime = timeGetTime();
|
|
|
|
// This is called from the dialog UI thread, NoteEnumResponse()
|
|
// is called from the DirectPlay message handler threads so
|
|
// they may also be inside it at this time, so we need to go into the
|
|
// critical section first
|
|
EnterCriticalSection( &s_csHostEnum );
|
|
|
|
// Expire old host enums
|
|
pDPHostEnum = s_DPHostEnumHead.pNext;
|
|
while ( pDPHostEnum != &s_DPHostEnumHead )
|
|
{
|
|
// Check the poll time to expire stale entries. Also check to see if
|
|
// the entry is already invalid. If so, don't note that the enum list
|
|
// changed because that causes the list in the dialog to constantly redraw.
|
|
if( ( pDPHostEnum->bValid != FALSE ) &&
|
|
( pDPHostEnum->dwLastPollTime < dwCurrentTime - s_dwEnumHostExpireInterval ) )
|
|
{
|
|
// This node has expired, so invalidate it.
|
|
pDPHostEnum->bValid = FALSE;
|
|
s_bEnumListChanged = TRUE;
|
|
}
|
|
|
|
pDPHostEnum = pDPHostEnum->pNext;
|
|
}
|
|
|
|
// Only update the display list if it has changed since last time
|
|
if( !s_bEnumListChanged )
|
|
{
|
|
LeaveCriticalSection( &s_csHostEnum );
|
|
return;
|
|
}
|
|
|
|
s_bEnumListChanged = FALSE;
|
|
|
|
bFindSelectedGUID = FALSE;
|
|
bFoundSelectedGUID = FALSE;
|
|
|
|
// Try to keep the same session selected unless it goes away or
|
|
// there is no real session currently selected
|
|
nItemSelected = (int)SendMessage( hWndListBox, LB_GETCURSEL, 0, 0 );
|
|
if( nItemSelected != LB_ERR )
|
|
{
|
|
pDPHostEnumSelected = (DPHostEnumInfo*) SendMessage( hWndListBox, LB_GETITEMDATA,
|
|
nItemSelected, 0 );
|
|
if( pDPHostEnumSelected != NULL && pDPHostEnumSelected->bValid )
|
|
{
|
|
guidSelectedInstance = pDPHostEnumSelected->pAppDesc->guidInstance;
|
|
bFindSelectedGUID = TRUE;
|
|
}
|
|
}
|
|
|
|
// Tell listbox not to redraw itself since the contents are going to change
|
|
SendMessage( hWndListBox, WM_SETREDRAW, FALSE, 0 );
|
|
|
|
// Test to see if any sessions exist in the linked list
|
|
pDPHostEnum = s_DPHostEnumHead.pNext;
|
|
while ( pDPHostEnum != &s_DPHostEnumHead )
|
|
{
|
|
if( pDPHostEnum->bValid )
|
|
break;
|
|
pDPHostEnum = pDPHostEnum->pNext;
|
|
}
|
|
|
|
// If there are any sessions in list,
|
|
// then add them to the listbox
|
|
if( pDPHostEnum != &s_DPHostEnumHead )
|
|
{
|
|
// Clear the contents from the list box and enable the join button
|
|
SendMessage( hWndListBox, LB_RESETCONTENT, 0, 0 );
|
|
|
|
EnableWindow( GetDlgItem( hDlg, IDOK ), TRUE );
|
|
|
|
pDPHostEnum = s_DPHostEnumHead.pNext;
|
|
while ( pDPHostEnum != &s_DPHostEnumHead )
|
|
{
|
|
// Add host to list box if it is valid
|
|
if( pDPHostEnum->bValid )
|
|
{
|
|
int nIndex = (int)SendMessage( hWndListBox, LB_ADDSTRING, 0,
|
|
(LPARAM)pDPHostEnum->szSession );
|
|
SendMessage( hWndListBox, LB_SETITEMDATA, nIndex, (LPARAM)pDPHostEnum );
|
|
|
|
if( bFindSelectedGUID )
|
|
{
|
|
// Look for the session the was selected before
|
|
if( pDPHostEnum->pAppDesc->guidInstance == guidSelectedInstance )
|
|
{
|
|
SendMessage( hWndListBox, LB_SETCURSEL, nIndex, 0 );
|
|
bFoundSelectedGUID = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
pDPHostEnum = pDPHostEnum->pNext;
|
|
}
|
|
|
|
if( !bFindSelectedGUID || !bFoundSelectedGUID )
|
|
SendMessage( hWndListBox, LB_SETCURSEL, 0, 0 );
|
|
}
|
|
else
|
|
{
|
|
// There are no active session, so just reset the listbox
|
|
SessionsDlgInitListbox( hDlg );
|
|
}
|
|
|
|
// Tell listbox to redraw itself now since the contents have changed
|
|
SendMessage( hWndListBox, WM_SETREDRAW, TRUE, 0 );
|
|
InvalidateRect( hWndListBox, NULL, FALSE );
|
|
|
|
LeaveCriticalSection( &s_csHostEnum );
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
*
|
|
* SessionsDlgEnumListCleanup
|
|
*
|
|
****************************************************************************/
|
|
VOID SessionsDlgEnumListCleanup()
|
|
{
|
|
DPHostEnumInfo* pDPHostEnum = s_DPHostEnumHead.pNext;
|
|
DPHostEnumInfo* pDPHostEnumDelete;
|
|
|
|
while ( pDPHostEnum != &s_DPHostEnumHead )
|
|
{
|
|
pDPHostEnumDelete = pDPHostEnum;
|
|
pDPHostEnum = pDPHostEnum->pNext;
|
|
|
|
if( pDPHostEnumDelete->pAppDesc )
|
|
{
|
|
delete[] pDPHostEnumDelete->pAppDesc->pwszSessionName;
|
|
delete[] pDPHostEnumDelete->pAppDesc;
|
|
}
|
|
|
|
// Changed from array delete to Release
|
|
ReleasePpo( &pDPHostEnumDelete->pHostAddr );
|
|
ReleasePpo( &pDPHostEnumDelete->pDeviceAddr );
|
|
delete pDPHostEnumDelete;
|
|
}
|
|
|
|
// Re-link the s_DPHostEnumHead circular linked list
|
|
s_DPHostEnumHead.pNext = &s_DPHostEnumHead;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
*
|
|
* ChatDialogProc
|
|
*
|
|
****************************************************************************/
|
|
INT_PTR CALLBACK ChatDialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
s_hDlg = hDlg;
|
|
|
|
// Join or host the session
|
|
if( FAILED( InitSession() ) )
|
|
{
|
|
EndDialog(hDlg, 0);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
case WM_TIMER:
|
|
{
|
|
if( wParam == TIMER_WAIT_CONNECT_COMPLETE )
|
|
{
|
|
// Check for connect complete
|
|
if( WAIT_OBJECT_0 == WaitForSingleObject( s_hConnectCompleteEvent, 0 ) )
|
|
{
|
|
s_bConnecting = FALSE;
|
|
|
|
if( FAILED( s_hrConnectComplete ) )
|
|
{
|
|
s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_JOINSESSION;
|
|
s_pNetInfo->m_testResult.m_hr = s_hrConnectComplete;
|
|
EndDialog(hDlg, 0);
|
|
}
|
|
else
|
|
{
|
|
// DirectPlay connect successful
|
|
PostMessage( s_hDlg, WM_APP_CONNECTED, 0, 0 );
|
|
EnableWindow( GetDlgItem( s_hDlg, IDC_SEND), TRUE );
|
|
}
|
|
|
|
KillTimer( s_hDlg, TIMER_WAIT_CONNECT_COMPLETE );
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case WM_APP_CONNECTING:
|
|
{
|
|
WCHAR sz[MAX_PATH];
|
|
LoadStringWide(IDS_CONNECTING, sz);
|
|
ShowTextString( hDlg, sz );
|
|
break;
|
|
}
|
|
|
|
case WM_APP_CONNECTED:
|
|
{
|
|
WCHAR sz[MAX_PATH];
|
|
LoadStringWide(IDS_CONNECTED, sz);
|
|
ShowTextString( hDlg, sz );
|
|
break;
|
|
}
|
|
|
|
case WM_APP_JOIN:
|
|
{
|
|
APP_PLAYER_MSG* pPlayerMsg = (APP_PLAYER_MSG*) lParam;
|
|
|
|
WCHAR szFmt[MAX_PATH];
|
|
WCHAR szSuperMessage[MAX_PATH];
|
|
|
|
LoadStringWide(IDS_JOINMSGFMT, szFmt);
|
|
swprintf(szSuperMessage, szFmt, pPlayerMsg->strPlayerName);
|
|
ShowTextString( hDlg, szSuperMessage );
|
|
|
|
delete pPlayerMsg;
|
|
break;
|
|
}
|
|
|
|
case WM_APP_CHAT:
|
|
{
|
|
APP_QUEUE_CHAT_MSG* pQueuedChat = (APP_QUEUE_CHAT_MSG*) lParam;
|
|
|
|
ShowTextString( hDlg, pQueuedChat->strChatBuffer );
|
|
|
|
delete pQueuedChat;
|
|
break;
|
|
}
|
|
|
|
case WM_APP_LEAVE:
|
|
{
|
|
APP_PLAYER_MSG* pPlayerMsg = (APP_PLAYER_MSG*) lParam;
|
|
|
|
WCHAR szSuperMessage[MAX_PATH];
|
|
WCHAR szFmt[MAX_PATH];
|
|
LoadStringWide(IDS_LEAVEMSGFMT, szFmt);
|
|
swprintf(szSuperMessage, szFmt, pPlayerMsg->strPlayerName );
|
|
ShowTextString( hDlg, szSuperMessage );
|
|
|
|
delete pPlayerMsg;
|
|
break;
|
|
}
|
|
|
|
case WM_COMMAND:
|
|
{
|
|
switch(LOWORD(wParam))
|
|
{
|
|
case IDC_SEND:
|
|
{
|
|
HRESULT hr;
|
|
TCHAR szMessage[MAX_PATH];
|
|
GetWindowText(GetDlgItem(hDlg, IDC_CHATINPUT), szMessage, MAX_PATH);
|
|
SendMessage(GetDlgItem(hDlg, IDC_CHATINPUT), EM_SETSEL, 0, -1);
|
|
SendMessage(GetDlgItem(hDlg, IDC_CHATINPUT), EM_REPLACESEL, FALSE, (LPARAM)"");
|
|
|
|
hr = SendChatMessage( szMessage );
|
|
if (FAILED(hr))
|
|
{
|
|
s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_SENDCHATMESSAGE;
|
|
s_pNetInfo->m_testResult.m_hr = hr;
|
|
EndDialog(hDlg, 1);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IDOK:
|
|
EndDialog(hDlg, 1);
|
|
break;
|
|
|
|
case IDCANCEL:
|
|
EndDialog(hDlg, 0);
|
|
break;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
case WM_DESTROY:
|
|
{
|
|
s_hDlg = NULL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
*
|
|
* InitSession
|
|
*
|
|
****************************************************************************/
|
|
HRESULT InitSession()
|
|
{
|
|
HRESULT hr;
|
|
|
|
if( s_bCreateSession )
|
|
{
|
|
// Set peer info name
|
|
WCHAR wszPeerName[MAX_PLAYER_NAME];
|
|
ConvertGenericStringToWide( wszPeerName, s_szPlayerName, MAX_PLAYER_NAME );
|
|
|
|
DPN_PLAYER_INFO dpPlayerInfo;
|
|
ZeroMemory( &dpPlayerInfo, sizeof(DPN_PLAYER_INFO) );
|
|
dpPlayerInfo.dwSize = sizeof(DPN_PLAYER_INFO);
|
|
dpPlayerInfo.dwInfoFlags = DPNINFO_NAME;
|
|
dpPlayerInfo.pwszName = wszPeerName;
|
|
|
|
// Set the peer info, and use the DPNOP_SYNC since by default this
|
|
// is an async call. If it is not DPNOP_SYNC, then the peer info may not
|
|
// be set by the time we call Host() below.
|
|
if( FAILED( hr = s_pDP->SetPeerInfo( &dpPlayerInfo, NULL, NULL, DPNOP_SYNC ) ) )
|
|
{
|
|
s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_SETPEERINFO;
|
|
s_pNetInfo->m_testResult.m_hr = hr;
|
|
return hr;
|
|
}
|
|
|
|
WCHAR wszSessionName[MAX_PATH];
|
|
ConvertGenericStringToWide( wszSessionName, s_szSessionName );
|
|
|
|
// Setup the application desc
|
|
DPN_APPLICATION_DESC dnAppDesc;
|
|
ZeroMemory( &dnAppDesc, sizeof(DPN_APPLICATION_DESC) );
|
|
dnAppDesc.dwSize = sizeof(DPN_APPLICATION_DESC);
|
|
dnAppDesc.guidApplication = s_guidDPTest;
|
|
dnAppDesc.pwszSessionName = wszSessionName;
|
|
dnAppDesc.dwFlags = DPNSESSION_MIGRATE_HOST;
|
|
|
|
// Host a game on m_pDeviceAddress as described by dnAppDesc
|
|
// DPNHOST_OKTOQUERYFORADDRESSING allows DirectPlay to prompt the user
|
|
// using a dialog box for any device address information that is missing
|
|
if( FAILED( hr = s_pDP->Host( &dnAppDesc, // the application desc
|
|
&s_pDeviceAddress, // array of addresses of the local devices used to connect to the host
|
|
1, // number in array
|
|
NULL, NULL, // DPN_SECURITY_DESC, DPN_SECURITY_CREDENTIALS
|
|
NULL, // player context
|
|
DPNHOST_OKTOQUERYFORADDRESSING ) ) ) // flags
|
|
{
|
|
if (hr == DPNERR_USERCANCEL || hr == DPNERR_INVALIDDEVICEADDRESS)
|
|
{
|
|
s_pNetInfo->m_testResult.m_bCancelled = TRUE;
|
|
return hr;
|
|
}
|
|
s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_CREATESESSION;
|
|
s_pNetInfo->m_testResult.m_hr = hr;
|
|
return hr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Set the peer info
|
|
WCHAR wszPeerName[MAX_PLAYER_NAME];
|
|
ConvertGenericStringToWide( wszPeerName, s_szPlayerName, MAX_PLAYER_NAME );
|
|
|
|
DPN_PLAYER_INFO dpPlayerInfo;
|
|
ZeroMemory( &dpPlayerInfo, sizeof(DPN_PLAYER_INFO) );
|
|
dpPlayerInfo.dwSize = sizeof(DPN_PLAYER_INFO);
|
|
dpPlayerInfo.dwInfoFlags = DPNINFO_NAME;
|
|
dpPlayerInfo.pwszName = wszPeerName;
|
|
|
|
// Set the peer info, and use the DPNOP_SYNC since by default this
|
|
// is an async call. If it is not DPNOP_SYNC, then the peer info may not
|
|
// be set by the time we call Connect() below.
|
|
if( FAILED( hr = s_pDP->SetPeerInfo( &dpPlayerInfo, NULL, NULL, DPNOP_SYNC ) ) )
|
|
{
|
|
s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_SETPEERINFO;
|
|
s_pNetInfo->m_testResult.m_hr = hr;
|
|
LeaveCriticalSection( &s_csHostEnum );
|
|
return hr;
|
|
}
|
|
|
|
ResetEvent( s_hConnectCompleteEvent );
|
|
s_bConnecting = TRUE;
|
|
|
|
// Connect to an existing session. DPNCONNECT_OKTOQUERYFORADDRESSING allows
|
|
// DirectPlay to prompt the user using a dialog box for any device address
|
|
// or host address information that is missing
|
|
// We also pass in copies of the app desc and host addr, since pDPHostEnumSelected
|
|
// might be deleted from another thread that calls SessionsDlgExpireOldHostEnums().
|
|
// This process could also be done using reference counting instead.
|
|
hr = s_pDP->Connect( s_pDPHostEnumSelected->pAppDesc, // the application desc
|
|
s_pDPHostEnumSelected->pHostAddr, // address of the host of the session
|
|
s_pDPHostEnumSelected->pDeviceAddr, // address of the local device the enum responses were received on
|
|
NULL, NULL, // DPN_SECURITY_DESC, DPN_SECURITY_CREDENTIALS
|
|
NULL, 0, // user data, user data size
|
|
NULL, // player context,
|
|
NULL, &s_hConnectAsyncOp, // async context, async handle,
|
|
DPNCONNECT_OKTOQUERYFORADDRESSING ); // flags
|
|
|
|
LeaveCriticalSection( &s_csHostEnum );
|
|
|
|
if( hr != E_PENDING && FAILED(hr) )
|
|
{
|
|
if (hr == DPNERR_USERCANCEL)
|
|
{
|
|
s_pNetInfo->m_testResult.m_bCancelled = TRUE;
|
|
return hr;
|
|
}
|
|
s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_JOINSESSION;
|
|
s_pNetInfo->m_testResult.m_hr = hr;
|
|
return hr;
|
|
}
|
|
|
|
// Set a timer to wait for m_hConnectCompleteEvent to be signaled.
|
|
// This will tell us when DPN_MSGID_CONNECT_COMPLETE has been processed
|
|
// which lets us know if the connect was successful or not.
|
|
PostMessage( s_hDlg, WM_APP_CONNECTING, 0, 0 );
|
|
SetTimer( s_hDlg, TIMER_WAIT_CONNECT_COMPLETE, 100, NULL );
|
|
EnableWindow( GetDlgItem( s_hDlg, IDC_SEND), FALSE );
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
*
|
|
* LoadStringWide
|
|
*
|
|
****************************************************************************/
|
|
VOID LoadStringWide( int nID, WCHAR* szWide )
|
|
{
|
|
TCHAR sz[MAX_PATH];
|
|
LoadString(NULL, nID, sz, MAX_PATH);
|
|
ConvertGenericStringToWide( szWide, sz, MAX_PATH );
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
*
|
|
* ShowTextString
|
|
*
|
|
****************************************************************************/
|
|
VOID ShowTextString( HWND hDlg, WCHAR* sz )
|
|
{
|
|
TCHAR szT[MAX_CHAT_STRING];
|
|
ConvertWideStringToGeneric( szT, sz, MAX_CHAT_STRING );
|
|
|
|
LONG numChars = (LONG)SendMessage(GetDlgItem(hDlg, IDC_CHATOUTPUT), WM_GETTEXTLENGTH, 0, 0);
|
|
SendMessage(GetDlgItem(hDlg, IDC_CHATOUTPUT), EM_SETSEL, numChars, numChars);
|
|
SendMessage(GetDlgItem(hDlg, IDC_CHATOUTPUT), EM_REPLACESEL, FALSE, (LPARAM)szT);
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
*
|
|
* SendChatMessage
|
|
*
|
|
****************************************************************************/
|
|
HRESULT SendChatMessage( TCHAR* szMessage )
|
|
{
|
|
// Send a message to all of the players
|
|
GAMEMSG_CHAT msgChat;
|
|
msgChat.nType = GAME_MSGID_CHAT;
|
|
ConvertGenericStringToWide( msgChat.strChatString, szMessage, MAX_CHAT_STRING_LENGTH-1 );
|
|
msgChat.strChatString[MAX_CHAT_STRING_LENGTH-1] = 0;
|
|
|
|
DPN_BUFFER_DESC bufferDesc;
|
|
bufferDesc.dwBufferSize = sizeof(GAMEMSG_CHAT);
|
|
bufferDesc.pBufferData = (BYTE*) &msgChat;
|
|
|
|
DPNHANDLE hAsync;
|
|
s_pDP->SendTo( DPNID_ALL_PLAYERS_GROUP, &bufferDesc, 1,
|
|
0, NULL, &hAsync, 0 );
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
*
|
|
* DirectPlayMessageHandler
|
|
*
|
|
****************************************************************************/
|
|
HRESULT WINAPI DirectPlayMessageHandler( PVOID pvUserContext,
|
|
DWORD dwMessageId,
|
|
PVOID pMsgBuffer )
|
|
{
|
|
switch( dwMessageId )
|
|
{
|
|
case DPN_MSGID_CONNECT_COMPLETE:
|
|
{
|
|
PDPNMSG_CONNECT_COMPLETE pConnectCompleteMsg;
|
|
pConnectCompleteMsg = (PDPNMSG_CONNECT_COMPLETE)pMsgBuffer;
|
|
|
|
// Set m_hrConnectComplete, then set an event letting
|
|
// everyone know that the DPN_MSGID_CONNECT_COMPLETE msg
|
|
// has been handled
|
|
s_hrConnectComplete = pConnectCompleteMsg->hResultCode;
|
|
SetEvent( s_hConnectCompleteEvent );
|
|
break;
|
|
}
|
|
|
|
case DPN_MSGID_ENUM_HOSTS_RESPONSE:
|
|
{
|
|
PDPNMSG_ENUM_HOSTS_RESPONSE pEnumHostsResponseMsg;
|
|
pEnumHostsResponseMsg = (PDPNMSG_ENUM_HOSTS_RESPONSE)pMsgBuffer;
|
|
|
|
// Take note of the host response
|
|
SessionsDlgNoteEnumResponse( pEnumHostsResponseMsg );
|
|
break;
|
|
}
|
|
|
|
case DPN_MSGID_ASYNC_OP_COMPLETE:
|
|
{
|
|
PDPNMSG_ASYNC_OP_COMPLETE pAsyncOpCompleteMsg;
|
|
pAsyncOpCompleteMsg = (PDPNMSG_ASYNC_OP_COMPLETE)pMsgBuffer;
|
|
|
|
if( pAsyncOpCompleteMsg->hAsyncOp == s_hEnumAsyncOp )
|
|
{
|
|
SessionsDlgEnumListCleanup();
|
|
s_hEnumAsyncOp = NULL;
|
|
|
|
// Ignore errors if we are connecting already or something else failed
|
|
if( !s_bConnecting && s_pNetInfo->m_testResult.m_iStepThatFailed == 0 )
|
|
{
|
|
if( FAILED(pAsyncOpCompleteMsg->hResultCode) )
|
|
{
|
|
if( pAsyncOpCompleteMsg->hResultCode == DPNERR_USERCANCEL )
|
|
{
|
|
s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_ENUMSESSIONS;
|
|
s_pNetInfo->m_testResult.m_hr = pAsyncOpCompleteMsg->hResultCode;
|
|
s_pNetInfo->m_testResult.m_bCancelled = TRUE;
|
|
}
|
|
else if( pAsyncOpCompleteMsg->hResultCode == DPNERR_ADDRESSING )
|
|
{
|
|
TCHAR szTitle[MAX_PATH];
|
|
TCHAR szMessage[MAX_PATH];
|
|
LoadString(NULL, IDS_APPFULLNAME, szTitle, MAX_PATH);
|
|
LoadString(NULL, IDS_SESSIONLISTERROR, szMessage, MAX_PATH);
|
|
MessageBox(s_hwndSessionDlg, szMessage, szTitle, MB_OK);
|
|
|
|
s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_ENUMSESSIONS;
|
|
s_pNetInfo->m_testResult.m_hr = pAsyncOpCompleteMsg->hResultCode;
|
|
s_pNetInfo->m_testResult.m_bCancelled = TRUE;
|
|
}
|
|
else
|
|
{
|
|
s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_ENUMSESSIONS;
|
|
s_pNetInfo->m_testResult.m_hr = pAsyncOpCompleteMsg->hResultCode;
|
|
}
|
|
|
|
EndDialog(s_hwndSessionDlg, 1);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case DPN_MSGID_TERMINATE_SESSION:
|
|
{
|
|
PDPNMSG_TERMINATE_SESSION pTerminateSessionMsg;
|
|
pTerminateSessionMsg = (PDPNMSG_TERMINATE_SESSION)pMsgBuffer;
|
|
|
|
EndDialog(s_hDlg,0);
|
|
break;
|
|
}
|
|
|
|
case DPN_MSGID_CREATE_PLAYER:
|
|
{
|
|
HRESULT hr;
|
|
PDPNMSG_CREATE_PLAYER pCreatePlayerMsg;
|
|
pCreatePlayerMsg = (PDPNMSG_CREATE_PLAYER)pMsgBuffer;
|
|
|
|
// Get the peer info and extract its name
|
|
DWORD dwSize = 0;
|
|
DPN_PLAYER_INFO* pdpPlayerInfo = NULL;
|
|
// Create a new and fill in a APP_PLAYER_INFO
|
|
APP_PLAYER_INFO* pPlayerInfo = new APP_PLAYER_INFO;
|
|
ZeroMemory( pPlayerInfo, sizeof(APP_PLAYER_INFO) );
|
|
pPlayerInfo->dpnidPlayer = pCreatePlayerMsg->dpnidPlayer;
|
|
wcscpy( pPlayerInfo->strPlayerName, L"???" );
|
|
pPlayerInfo->lRefCount = 1;
|
|
|
|
hr = s_pDP->GetPeerInfo( pCreatePlayerMsg->dpnidPlayer, pdpPlayerInfo, &dwSize, 0 );
|
|
if( SUCCEEDED(hr) || hr == DPNERR_BUFFERTOOSMALL )
|
|
{
|
|
pdpPlayerInfo = (DPN_PLAYER_INFO*) new BYTE[ dwSize ];
|
|
ZeroMemory( pdpPlayerInfo, dwSize );
|
|
pdpPlayerInfo->dwSize = sizeof(DPN_PLAYER_INFO);
|
|
|
|
hr = s_pDP->GetPeerInfo( pCreatePlayerMsg->dpnidPlayer, pdpPlayerInfo, &dwSize, 0 );
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
// This stores a extra TCHAR copy of the player name for
|
|
// easier access. This will be redundent copy since DPlay
|
|
// also keeps a copy of the player name in GetPeerInfo()
|
|
wcsncpy( pPlayerInfo->strPlayerName,
|
|
pdpPlayerInfo->pwszName, MAX_PLAYER_NAME );
|
|
pPlayerInfo->strPlayerName[MAX_PLAYER_NAME-1] = 0;
|
|
}
|
|
}
|
|
|
|
if( pdpPlayerInfo->dwPlayerFlags & DPNPLAYER_LOCAL )
|
|
s_dpnidLocalPlayer = pCreatePlayerMsg->dpnidPlayer;
|
|
|
|
delete[] pdpPlayerInfo;
|
|
pdpPlayerInfo = NULL;
|
|
|
|
if( s_hDlg )
|
|
{
|
|
// Record the buffer handle so the buffer can be returned later
|
|
APP_PLAYER_MSG* pPlayerMsg = new APP_PLAYER_MSG;
|
|
wcscpy( pPlayerMsg->strPlayerName, pPlayerInfo->strPlayerName );
|
|
|
|
// Pass the APP_PLAYER_MSG to the main dialog thread, so it can
|
|
// process it. It will also cleanup the struct
|
|
PostMessage( s_hDlg, WM_APP_JOIN, pPlayerInfo->dpnidPlayer, (LPARAM) pPlayerMsg );
|
|
}
|
|
|
|
// Tell DirectPlay to store this pPlayerInfo
|
|
// pointer in the pvPlayerContext.
|
|
pCreatePlayerMsg->pvPlayerContext = pPlayerInfo;
|
|
|
|
// Update the number of active players, and
|
|
// post a message to the dialog thread to update the
|
|
// UI. This keeps the DirectPlay message handler
|
|
// from blocking
|
|
InterlockedIncrement( &s_lNumberOfActivePlayers );
|
|
|
|
break;
|
|
}
|
|
|
|
case DPN_MSGID_DESTROY_PLAYER:
|
|
{
|
|
PDPNMSG_DESTROY_PLAYER pDestroyPlayerMsg;
|
|
pDestroyPlayerMsg = (PDPNMSG_DESTROY_PLAYER)pMsgBuffer;
|
|
APP_PLAYER_INFO* pPlayerInfo = (APP_PLAYER_INFO*) pDestroyPlayerMsg->pvPlayerContext;
|
|
|
|
if( s_hDlg )
|
|
{
|
|
// Record the buffer handle so the buffer can be returned later
|
|
APP_PLAYER_MSG* pPlayerMsg = new APP_PLAYER_MSG;
|
|
wcscpy( pPlayerMsg->strPlayerName, pPlayerInfo->strPlayerName );
|
|
|
|
// Pass the APP_PLAYER_MSG to the main dialog thread, so it can
|
|
// process it. It will also cleanup the struct
|
|
PostMessage( s_hDlg, WM_APP_LEAVE, pPlayerInfo->dpnidPlayer, (LPARAM) pPlayerMsg );
|
|
}
|
|
|
|
PLAYER_LOCK(); // enter player context CS
|
|
PLAYER_RELEASE( pPlayerInfo ); // Release player and cleanup if needed
|
|
PLAYER_UNLOCK(); // leave player context CS
|
|
|
|
// Update the number of active players, and
|
|
// post a message to the dialog thread to update the
|
|
// UI. This keeps the DirectPlay message handler
|
|
// from blocking
|
|
InterlockedDecrement( &s_lNumberOfActivePlayers );
|
|
break;
|
|
}
|
|
|
|
case DPN_MSGID_RECEIVE:
|
|
{
|
|
PDPNMSG_RECEIVE pReceiveMsg;
|
|
pReceiveMsg = (PDPNMSG_RECEIVE)pMsgBuffer;
|
|
APP_PLAYER_INFO* pPlayerInfo = (APP_PLAYER_INFO*) pReceiveMsg->pvPlayerContext;
|
|
|
|
GAMEMSG_GENERIC* pMsg = (GAMEMSG_GENERIC*) pReceiveMsg->pReceiveData;
|
|
|
|
if( pReceiveMsg->dwReceiveDataSize == sizeof(GAMEMSG_CHAT) &&
|
|
pMsg->nType == GAME_MSGID_CHAT )
|
|
{
|
|
// This message is sent when a player has send a chat message to us, so
|
|
// post a message to the dialog thread to update the UI.
|
|
// This keeps the DirectPlay threads from blocking, and also
|
|
// serializes the recieves since DirectPlayMessageHandler can
|
|
// be called simultaneously from a pool of DirectPlay threads.
|
|
GAMEMSG_CHAT* pChatMessage = (GAMEMSG_CHAT*) pMsg;
|
|
|
|
// Record the buffer handle so the buffer can be returned later
|
|
APP_QUEUE_CHAT_MSG* pQueuedChat = new APP_QUEUE_CHAT_MSG;
|
|
_snwprintf( pQueuedChat->strChatBuffer, MAX_CHAT_STRING, L"<%s> %s\r\n",
|
|
pPlayerInfo->strPlayerName,
|
|
pChatMessage->strChatString );
|
|
pQueuedChat->strChatBuffer[MAX_CHAT_STRING-1]=0;
|
|
|
|
// Pass the APP_QUEUE_CHAT_MSG to the main dialog thread, so it can
|
|
// process it. It will also cleanup the struct
|
|
PostMessage( s_hDlg, WM_APP_CHAT, pPlayerInfo->dpnidPlayer, (LPARAM) pQueuedChat );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
*
|
|
* ConvertAnsiStringToWide
|
|
*
|
|
****************************************************************************/
|
|
VOID ConvertAnsiStringToWide( WCHAR* wstrDestination, const CHAR* strSource,
|
|
int cchDestChar )
|
|
{
|
|
if( wstrDestination==NULL || strSource==NULL )
|
|
return;
|
|
|
|
if( cchDestChar == -1 )
|
|
cchDestChar = strlen(strSource)+1;
|
|
|
|
MultiByteToWideChar( CP_ACP, 0, strSource, -1,
|
|
wstrDestination, cchDestChar-1 );
|
|
|
|
wstrDestination[cchDestChar-1] = 0;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
*
|
|
* ConvertGenericStringToWide
|
|
*
|
|
****************************************************************************/
|
|
VOID ConvertGenericStringToWide( WCHAR* wstrDestination, const TCHAR* tstrSource, int cchDestChar )
|
|
{
|
|
if( wstrDestination==NULL || tstrSource==NULL )
|
|
return;
|
|
|
|
#ifdef _UNICODE
|
|
if( cchDestChar == -1 )
|
|
wcscpy( wstrDestination, tstrSource );
|
|
else
|
|
{
|
|
wcsncpy( wstrDestination, tstrSource, cchDestChar );
|
|
wstrDestination[cchDestChar-1] = 0;
|
|
}
|
|
#else
|
|
ConvertAnsiStringToWide( wstrDestination, tstrSource, cchDestChar );
|
|
#endif
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
*
|
|
* ConvertWideStringToGeneric
|
|
*
|
|
****************************************************************************/
|
|
VOID ConvertWideStringToGeneric( TCHAR* tstrDestination, const WCHAR* wstrSource, int cchDestChar )
|
|
{
|
|
if( tstrDestination==NULL || wstrSource==NULL )
|
|
return;
|
|
|
|
#ifdef _UNICODE
|
|
if( cchDestChar == -1 )
|
|
wcscpy( tstrDestination, wstrSource );
|
|
else
|
|
{
|
|
wcsncpy( tstrDestination, wstrSource, cchDestChar );
|
|
tstrDestination[cchDestChar-1] = 0;
|
|
}
|
|
|
|
#else
|
|
ConvertWideStringToAnsi( tstrDestination, wstrSource, cchDestChar );
|
|
#endif
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
*
|
|
* ConvertWideStringToAnsi
|
|
*
|
|
****************************************************************************/
|
|
VOID ConvertWideStringToAnsi( CHAR* strDestination, const WCHAR* wstrSource, int cchDestChar )
|
|
{
|
|
if( strDestination==NULL || wstrSource==NULL )
|
|
return;
|
|
|
|
if( cchDestChar == -1 )
|
|
cchDestChar = wcslen(wstrSource)+1;
|
|
|
|
WideCharToMultiByte( CP_ACP, 0, wstrSource, -1, strDestination,
|
|
cchDestChar-1, NULL, NULL );
|
|
|
|
strDestination[cchDestChar-1] = 0;
|
|
}
|
|
|
|
|