www.pudn.com > Win32DirectX9.rar > netclient.cpp


//----------------------------------------------------------------------------- 
// File: NetClient.cpp 
// 
// Desc: This is a class that given a IDirectPlay8Client upon DoConnectWizard() 
//       will enumerate hosts, and allows the user to join a session.  The class uses 
//       dialog boxes and GDI for the interactive UI.  Most games will 
//       want to change the graphics to use Direct3D or another graphics 
//       layer, but this simplistic sample uses dialog boxes. Feel  
//       free to use this class as a starting point for adding extra  
//       functionality. 
// 
// 
// Copyright (c) 2000-2001 Microsoft Corporation. All rights reserved. 
//----------------------------------------------------------------------------- 
#ifndef STRICT 
#define STRICT 
#endif // !STRICT 
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include "NetClient.h" 
#include "NetClientRes.h" 
#include "DXUtil.h" 
 
#if defined(WIN32_PLATFORM_PSPC) && (_WIN32_WCE >= 300) 
#include  
#endif // PocketPC 
 
 
//----------------------------------------------------------------------------- 
// Global variables 
//----------------------------------------------------------------------------- 
CNetClientWizard* g_pNCW = NULL;           // Pointer to the net connect wizard 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: CNetClientWizard 
// Desc: Init the class 
//----------------------------------------------------------------------------- 
CNetClientWizard::CNetClientWizard( HINSTANCE hInst, TCHAR* strAppName, 
                                      GUID* pGuidApp ) 
{ 
    g_pNCW    = this; 
    m_hInst   = hInst; 
    m_guidApp = *pGuidApp; 
    m_dwPort  = 0; 
     
    if( NULL == strAppName ) 
        strAppName = TEXT("DirectPlay App"); 
 
    _tcsncpy( m_strAppName, strAppName, MAX_PATH-1 ); 
    m_strAppName[ MAX_PATH-1 ] = 0; 
    
    m_dwEnumHostExpireInterval          = 0; 
    m_hConnectCompleteEvent             = NULL; 
    m_hrConnectComplete                 = 0; 
    m_bConnecting                       = FALSE; 
    m_bEnumListChanged                  = FALSE; 
    m_bSearchingForSessions             = FALSE; 
    m_hEnumAsyncOp                      = NULL; 
    m_hConnectAsyncOp                   = NULL; 
    m_pDPClient                         = NULL; 
    m_pLobbiedApp                       = NULL; 
    m_bHaveConnectionSettingsFromLobby  = FALSE; 
    m_hLobbyClient                      = NULL; 
    m_hDlg                              = NULL; 
 
    InitializeCriticalSection( &m_csHostEnum ); 
    m_hConnectCompleteEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); 
    m_hLobbyConnectionEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); 
 
    // Setup the m_DPHostEnumHead circular linked list 
    ZeroMemory( &m_DPHostEnumHead, sizeof( DPHostEnumInfo ) ); 
    m_DPHostEnumHead.pNext = &m_DPHostEnumHead; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: ~CNetClientWizard 
// Desc: Cleanup the class 
//----------------------------------------------------------------------------- 
CNetClientWizard::~CNetClientWizard() 
{ 
    DeleteCriticalSection( &m_csHostEnum ); 
    CloseHandle( m_hConnectCompleteEvent ); 
    CloseHandle( m_hLobbyConnectionEvent ); 
} 
 
 
 
//----------------------------------------------------------------------------- 
// Name: Init 
// Desc: Initialize member variables 
//----------------------------------------------------------------------------- 
HRESULT CNetClientWizard::Init( IDirectPlay8Client* pDPClient, 
                                IDirectPlay8LobbiedApplication* pLobbiedApp ) 
{ 
    if( NULL == pDPClient || NULL == pLobbiedApp ) 
        return E_INVALIDARG; 
 
    m_pDPClient         = pDPClient; 
    m_pLobbiedApp       = pLobbiedApp; 
    m_bHaveConnectionSettingsFromLobby = FALSE; 
    m_hLobbyClient      = NULL; 
 
    return S_OK; 
} 
 
 
 
//----------------------------------------------------------------------------- 
// Name: DoConnectWizard 
// Desc: Show the connection wizard UI 
//----------------------------------------------------------------------------- 
HRESULT CNetClientWizard::DoConnectWizard() 
{ 
    m_hrDialog = S_OK; 
 
    // Display the multiplayer games dialog box. 
    DialogBox( m_hInst, MAKEINTRESOURCE(IDD_CLIENT_CONNECT), NULL,  
               (DLGPROC) StaticSessionsDlgProc ); 
 
    return m_hrDialog; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: StaticSessionsDlgProc() 
// Desc: Static msg handler which passes messages 
//----------------------------------------------------------------------------- 
INT_PTR CALLBACK CNetClientWizard::StaticSessionsDlgProc( HWND hDlg, UINT uMsg, 
                                                          WPARAM wParam, LPARAM lParam ) 
{ 
    if( g_pNCW ) 
        return g_pNCW->SessionsDlgProc( hDlg, uMsg, wParam, lParam ); 
 
    return FALSE; // Message not handled 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: SessionsDlgProc() 
// Desc: Handles messages for the multiplayer games dialog 
//----------------------------------------------------------------------------- 
INT_PTR CALLBACK CNetClientWizard::SessionsDlgProc( HWND hDlg, UINT msg, 
                                                    WPARAM wParam, LPARAM lParam ) 
{ 
    HRESULT hr; 
 
    switch( msg ) 
    { 
        case WM_INITDIALOG: 
            { 
#if defined(WIN32_PLATFORM_PSPC) && (_WIN32_WCE >= 300) 
				SHINITDLGINFO	shidi; 
				memset(&shidi, 0, sizeof(SHINITDLGINFO)); 
				shidi.dwMask = SHIDIM_FLAGS; 
				shidi.dwFlags = SHIDIF_SIPDOWN | SHIDIF_SIZEDLGFULLSCREEN; 
				shidi.hDlg = hDlg; 
 
				SetForegroundWindow(hDlg); 
				SHInitDialog(&shidi); 
#endif // WIN32_PLATFORM_PSPC 
 
				// Load and set the icon 
                HICON hIcon = LoadIcon( m_hInst, MAKEINTRESOURCE( IDI_MAIN ) ); 
                SendMessage( hDlg, WM_SETICON, ICON_BIG,   (LPARAM) hIcon );  // Set big icon 
                SendMessage( hDlg, WM_SETICON, ICON_SMALL, (LPARAM) hIcon );  // Set small icon 
 
                SetDlgItemText( hDlg, IDC_PLAYER_NAME_EDIT, m_strLocalPlayerName ); 
 
                // Set the window title 
                TCHAR strWindowTitle[256]; 
 
                _sntprintf( strWindowTitle, 255, TEXT("%s - Multiplayer Games"), m_strAppName ); 
                strWindowTitle[255] = 0; 
 
                SetWindowText( hDlg, strWindowTitle ); 
 
                // Set the default port 
                if( m_dwPort != 0 ) 
                { 
                    TCHAR strPort[40]; 
                    _itot( m_dwPort, strPort, 10 ); 
                    SetDlgItemText( hDlg, IDC_REMOTE_PORT, strPort ); 
                } 
 
                // Init the search portion of the dialog 
                m_bSearchingForSessions = FALSE; 
                SetDlgItemText( hDlg, IDC_SEARCH_CHECK, TEXT("Start Search") ); 
                SessionsDlgInitListbox( hDlg ); 
            } 
            break; 
 
        case WM_TIMER: 
            // Upon this timer message, then refresh the list of hosts 
            // by expiring old hosts, and displaying the list in the 
            // dialog box 
            if( wParam == TIMERID_DISPLAY_HOSTS ) 
            { 
                // Don't refresh if we are not enumerating hosts 
                if( !m_bSearchingForSessions ) 
                    break; 
 
                // Expire all of the hosts that haven't 
                // refreshed in a certain period of time 
                SessionsDlgExpireOldHostEnums(); 
 
                // Display the list of hosts in the dialog 
                if( FAILED( hr = SessionsDlgDisplayEnumList( hDlg ) ) ) 
                { 
                    DXTRACE_ERR_MSGBOX( TEXT("SessionsDlgEnumHosts"), hr ); 
                    MessageBox( hDlg, TEXT("Error enumerating DirectPlay games."), 
                                m_strAppName, MB_OK | MB_ICONERROR ); 
 
                    m_bSearchingForSessions = FALSE; 
                    KillTimer( hDlg, TIMERID_DISPLAY_HOSTS ); 
                    CheckDlgButton( hDlg, IDC_SEARCH_CHECK, BST_UNCHECKED ); 
                    SetDlgItemText( hDlg, IDC_SEARCH_CHECK, TEXT("Start Search") ); 
                    SessionsDlgInitListbox( hDlg ); 
                } 
            } 
            else if( wParam == TIMERID_CONNECT_COMPLETE ) 
            { 
                // Check to see if the MessageHandler has set an event to tell us the 
                // DPN_MSGID_CONNECT_COMPLETE has been processed.  Now m_hrConnectComplete 
                // is valid. 
                if( WAIT_OBJECT_0 == WaitForSingleObject( m_hConnectCompleteEvent, 0 ) ) 
                { 
                    m_bConnecting = FALSE; 
 
                    if( FAILED( m_hrConnectComplete ) ) 
                    { 
                        DXTRACE_ERR_MSGBOX( TEXT("DPN_MSGID_CONNECT_COMPLETE"), m_hrConnectComplete ); 
                        MessageBox( hDlg, TEXT("Unable to join game."), 
                                    m_strAppName, MB_OK | MB_ICONERROR ); 
                    } 
                    else 
                    { 
                        // DirectPlay connect successful, so end dialog 
                        m_hrDialog = NCW_S_FORWARD; 
                        EndDialog( hDlg, 0 ); 
                    } 
                } 
            } 
 
            break; 
 
        case WM_COMMAND: 
            switch( LOWORD(wParam) ) 
            { 
                case IDC_SEARCH_CHECK: 
                    m_bSearchingForSessions = !m_bSearchingForSessions; 
 
                    if( m_bSearchingForSessions ) 
                    { 
                        SetDlgItemText( hDlg, IDC_SEARCH_CHECK, TEXT("Searching...") ); 
 
                        // Start the timer to display the host list every so often 
                        SetTimer( hDlg, TIMERID_DISPLAY_HOSTS, DISPLAY_REFRESH_RATE, NULL ); 
 
                        // Start the async enumeration 
                        if( FAILED( hr = SessionsDlgEnumHosts( hDlg ) ) ) 
                        { 
                            if( hr == DPNERR_ADDRESSING ) 
                            { 
                                // This will be returned if the ip address is invalid 
                                // for example something like "asdf"  
                                MessageBox( hDlg, TEXT("IP address not valid. Stopping search"), 
                                            m_strAppName, MB_OK ); 
                            } 
                            else 
                            { 
                                DXTRACE_ERR_MSGBOX( TEXT("SessionsDlgEnumHosts"), hr ); 
                                MessageBox( hDlg, TEXT("Error enumerating DirectPlay games."), 
                                            m_strAppName, MB_OK | MB_ICONERROR ); 
                            } 
 
                            m_bSearchingForSessions = FALSE; 
                            KillTimer( hDlg, TIMERID_DISPLAY_HOSTS ); 
                            CheckDlgButton( hDlg, IDC_SEARCH_CHECK, BST_UNCHECKED ); 
                            SetDlgItemText( hDlg, IDC_SEARCH_CHECK, TEXT("Start Search") ); 
                            SessionsDlgInitListbox( hDlg ); 
                        } 
                    } 
                    else 
                    { 
                        SetDlgItemText( hDlg, IDC_SEARCH_CHECK, TEXT("Start Search") ); 
 
                        // Stop the timer, and stop the async enumeration 
                        KillTimer( hDlg, TIMERID_DISPLAY_HOSTS ); 
 
                        // Until the CancelAsyncOperation returns, it is possible 
                        // to still receive host enumerations 
                        if( m_hEnumAsyncOp ) 
                            m_pDPClient->CancelAsyncOperation( m_hEnumAsyncOp, 0 ); 
 
                        // Reset the search portion of the dialog 
                        SessionsDlgInitListbox( hDlg ); 
                    } 
                    break; 
 
                case IDC_GAMES_LIST: 
                    if( HIWORD(wParam) != LBN_DBLCLK ) 
                        break; 
                    // Fall through 
 
                case IDC_JOIN: 
                    if( FAILED( hr = SessionsDlgJoinGame( hDlg ) ) ) 
                    { 
                        DXTRACE_ERR_MSGBOX( TEXT("SessionsDlgJoinGame"), hr ); 
                        MessageBox( hDlg, TEXT("Unable to join game."), 
                                    TEXT("DirectPlay Sample"), 
                                    MB_OK | MB_ICONERROR ); 
                    } 
                    break; 
 
                case IDCANCEL: // The close button was press 
                    m_hrDialog = NCW_S_QUIT; 
                    EndDialog( hDlg, 0 ); 
                    break; 
 
                default: 
                    return FALSE; // Message not handled 
            } 
            break; 
 
        case WM_DESTROY: 
        { 
            KillTimer( hDlg, 1 ); 
 
            // Cancel the enum hosts search 
            // if the enumeration is going on 
            if( m_bSearchingForSessions && m_hEnumAsyncOp ) 
            { 
                m_pDPClient->CancelAsyncOperation( m_hEnumAsyncOp, 0 ); 
                m_bSearchingForSessions = FALSE; 
            } 
            break; 
        } 
 
        default: 
            return FALSE; // Message not handled 
    } 
 
    // Message was handled 
    return TRUE; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: SessionsDlgInitListbox() 
// Desc: Initializes the listbox 
//----------------------------------------------------------------------------- 
VOID CNetClientWizard::SessionsDlgInitListbox( HWND hDlg ) 
{ 
    HWND hWndListBox = GetDlgItem( hDlg, IDC_GAMES_LIST ); 
 
    // Clear the contents from the list box, and 
    // display "Looking for games" text in listbox 
    SendMessage( hWndListBox, LB_RESETCONTENT, 0, 0 ); 
    if( m_bSearchingForSessions ) 
    { 
        SendMessage( hWndListBox, LB_ADDSTRING, 0, 
                     (LPARAM) TEXT("Looking for games...") ); 
    } 
    else 
    { 
        SendMessage( hWndListBox, LB_ADDSTRING, 0, 
                     (LPARAM) TEXT("Click Start Search to see a list of games.")); 
    } 
 
    SendMessage( hWndListBox, LB_SETITEMDATA,  0, NULL ); 
    SendMessage( hWndListBox, LB_SETCURSEL,    0, 0 ); 
 
    // Disable the join button until sessions are found 
    EnableWindow( GetDlgItem( hDlg, IDC_JOIN ), FALSE ); 
 
    // 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( SUCCEEDED( m_pDPClient->GetSPCaps( &CLSID_DP8SP_TCPIP, &dpspCaps, 0 ) ) ) 
    { 
        // Set the host expire time to around 3 times 
        // length of the dwDefaultEnumRetryInterval 
        m_dwEnumHostExpireInterval = dpspCaps.dwDefaultEnumRetryInterval * 3; 
    } 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: SessionsDlgEnumHosts() 
// Desc: Enumerates the DirectPlay sessions, and displays them in the listbox 
//----------------------------------------------------------------------------- 
HRESULT CNetClientWizard::SessionsDlgEnumHosts( HWND hDlg ) 
{ 
    HRESULT hr; 
 
    m_bEnumListChanged = TRUE; 
 
    DPN_APPLICATION_DESC   dpnAppDesc; 
    IDirectPlay8Address*   pDP8AddressHost  = NULL; 
    IDirectPlay8Address*   pDP8AddressLocal = NULL; 
    WCHAR*                 wszHostName      = NULL; 
 
    // Create the local device address object 
    if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay8Address, NULL,  
                                       CLSCTX_ALL, IID_IDirectPlay8Address, 
                                       (LPVOID*) &pDP8AddressLocal ) ) ) 
    { 
        DXTRACE_ERR_MSGBOX( TEXT("CoCreateInstance"), hr ); 
        goto LCleanup; 
    } 
 
    // Set IP service provider 
    if( FAILED( hr = pDP8AddressLocal->SetSP( &CLSID_DP8SP_TCPIP ) ) ) 
    { 
        DXTRACE_ERR_MSGBOX( TEXT("SetSP"), hr ); 
        goto LCleanup; 
    } 
 
 
    // Create the remote host address object 
    if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay8Address, NULL,  
                                       CLSCTX_ALL, IID_IDirectPlay8Address, 
                                       (LPVOID*) &pDP8AddressHost ) ) ) 
    { 
        DXTRACE_ERR_MSGBOX( TEXT("CoCreateInstance"), hr ); 
        goto LCleanup; 
    } 
 
    // Set IP service provider 
    if( FAILED( hr = pDP8AddressHost->SetSP( &CLSID_DP8SP_TCPIP ) ) ) 
    { 
        DXTRACE_ERR_MSGBOX( TEXT("SetSP"), hr ); 
        goto LCleanup; 
    } 
 
    // Set the remote host name (if provided) 
    TCHAR strIPAddress[MAX_PATH]; 
    GetDlgItemText( hDlg, IDC_IP_ADDRESS, strIPAddress, MAX_PATH ); 
 
    if( strIPAddress != NULL && strIPAddress[0] != 0 ) 
    { 
        wszHostName = new WCHAR[_tcslen(strIPAddress)+1]; 
        if( NULL == wszHostName ) 
        { 
            hr = E_OUTOFMEMORY; 
            DXTRACE_ERR_MSGBOX( TEXT("SessionsDlgEnumHosts"), hr ); 
            goto LCleanup; 
        } 
 
        DXUtil_ConvertGenericStringToWideCch( wszHostName, strIPAddress, (int)_tcslen(strIPAddress)+1 ); 
 
        hr = pDP8AddressHost->AddComponent( DPNA_KEY_HOSTNAME, wszHostName,  
                                            (DWORD) (wcslen(wszHostName)+1)*sizeof(WCHAR),  
                                            DPNA_DATATYPE_STRING ); 
        if( FAILED(hr) ) 
        { 
            DXTRACE_ERR_MSGBOX( TEXT("AddComponent"), hr ); 
            goto LCleanup; 
        } 
    } 
 
    TCHAR strPort[40]; 
    GetDlgItemText( hDlg, IDC_REMOTE_PORT, strPort, 40 ); 
    strPort[39] = 0; 
    m_dwPort = _ttoi( strPort ); 
 
    // If a port was specified in the IP string, then add it. 
    // Games will typically hard code the port so the user need not know it 
    if( m_dwPort != 0 ) 
    { 
        hr = pDP8AddressHost->AddComponent( DPNA_KEY_PORT,  
                                            &m_dwPort, sizeof(m_dwPort), 
                                            DPNA_DATATYPE_DWORD ); 
        if( FAILED(hr) ) 
        { 
            DXTRACE_ERR_MSGBOX( TEXT("AddComponent"), hr ); 
            goto LCleanup; 
        } 
    } 
  
 
    ZeroMemory( &dpnAppDesc, sizeof( DPN_APPLICATION_DESC ) ); 
    dpnAppDesc.dwSize = sizeof( DPN_APPLICATION_DESC ); 
    dpnAppDesc.guidApplication = m_guidApp; 
 
    // Enumerate all StressMazeApp hosts running on IP service providers 
    hr = m_pDPClient->EnumHosts( &dpnAppDesc, pDP8AddressHost,  
                                 pDP8AddressLocal, NULL,  
                                 0, INFINITE, 0, INFINITE, NULL,  
                                 &m_hEnumAsyncOp, 0 ); 
    if( FAILED(hr) ) 
    { 
        if( hr != DPNERR_INVALIDDEVICEADDRESS &&  
            hr != DPNERR_ADDRESSING ) // This will be returned if the ip address is is invalid.  
            DXTRACE_ERR_MSGBOX( TEXT("EnumHosts"), hr ); 
        goto LCleanup; 
    } 
 
LCleanup: 
    SAFE_RELEASE( pDP8AddressHost); 
    SAFE_RELEASE( pDP8AddressLocal ); 
    SAFE_DELETE_ARRAY( wszHostName ); 
 
    if( hr == DPNERR_PENDING ) 
        hr = DPN_OK; 
 
    return hr; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: SessionsDlgNoteEnumResponse() 
// Desc: Stores them in the linked list, m_DPHostEnumHead.  This is 
//       called from the DirectPlay message handler so it could be 
//       called simultaneously from multiple threads. 
//----------------------------------------------------------------------------- 
HRESULT CNetClientWizard::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.  Locking the entire 
    // function is crude, and could be more optimal but is effective for this 
    // simple sample 
    EnterCriticalSection( &m_csHostEnum ); 
 
    DPHostEnumInfo* pDPHostEnum          = m_DPHostEnumHead.pNext; 
    DPHostEnumInfo* pDPHostEnumNext      = NULL; 
    const DPN_APPLICATION_DESC* pResponseMsgAppDesc = 
                            pEnumHostsResponseMsg->pApplicationDescription; 
 
    // Look for a matching session instance GUID. 
    bFound = FALSE; 
    while ( pDPHostEnum != &m_DPHostEnumHead ) 
    { 
        if( pResponseMsgAppDesc->guidInstance == pDPHostEnum->pAppDesc->guidInstance ) 
        { 
            bFound = TRUE; 
            break; 
        } 
 
        pDPHostEnumNext = pDPHostEnum; 
        pDPHostEnum = pDPHostEnum->pNext; 
    } 
 
    if( !bFound ) 
    { 
        m_bEnumListChanged = TRUE; 
 
        // If there's no match, then look for invalid session and use it 
        pDPHostEnum = m_DPHostEnumHead.pNext; 
        while ( pDPHostEnum != &m_DPHostEnumHead ) 
        { 
            if( !pDPHostEnum->bValid ) 
                break; 
 
            pDPHostEnum = pDPHostEnum->pNext; 
        } 
 
        // If no invalid sessions are found then make a new one 
        if( pDPHostEnum == &m_DPHostEnumHead ) 
        { 
            // Found a new session, so create a new node 
            pDPHostEnum = new DPHostEnumInfo; 
            if( NULL == pDPHostEnum ) 
            { 
                hr = E_OUTOFMEMORY; 
                DXTRACE_ERR_MSGBOX( TEXT("SessionsDlgNoteEnumResponse"), hr ); 
                goto LCleanup; 
            } 
 
            ZeroMemory( pDPHostEnum, sizeof(DPHostEnumInfo) ); 
 
            // Add pDPHostEnum to the circular linked list, m_DPHostEnumHead 
            pDPHostEnum->pNext = m_DPHostEnumHead.pNext; 
            m_DPHostEnumHead.pNext = pDPHostEnum; 
        } 
    } 
 
    // Update the pDPHostEnum with new information 
    TCHAR strName[MAX_PATH]; 
    if( pResponseMsgAppDesc->pwszSessionName ) 
    { 
        DXUtil_ConvertWideStringToGenericCch( strName, pResponseMsgAppDesc->pwszSessionName, MAX_PATH ); 
    } 
 
    // Cleanup any old enum 
    if( pDPHostEnum->pAppDesc ) 
    { 
        SAFE_DELETE_ARRAY( pDPHostEnum->pAppDesc->pwszSessionName ); 
        SAFE_DELETE( pDPHostEnum->pAppDesc ); 
    } 
    SAFE_RELEASE( pDPHostEnum->pHostAddr ); 
    SAFE_RELEASE( pDPHostEnum->pDeviceAddr ); 
 
    // 
    // Duplicate pEnumHostsResponseMsg->pAddressSender in pDPHostEnum->pHostAddr. 
    // Duplicate pEnumHostsResponseMsg->pAddressDevice in pDPHostEnum->pDeviceAddr. 
    // 
    if( FAILED( hr = pEnumHostsResponseMsg->pAddressSender->Duplicate( &pDPHostEnum->pHostAddr ) ) ) 
    { 
        DXTRACE_ERR_MSGBOX( TEXT("Duplicate"), hr ); 
        goto LCleanup; 
    } 
 
    if( FAILED( hr = pEnumHostsResponseMsg->pAddressDevice->Duplicate( &pDPHostEnum->pDeviceAddr ) ) ) 
    { 
        DXTRACE_ERR_MSGBOX( TEXT("Duplicate"), hr ); 
        goto LCleanup; 
    } 
 
    // Deep copy the DPN_APPLICATION_DESC from 
    pDPHostEnum->pAppDesc = new DPN_APPLICATION_DESC; 
    if( NULL == pDPHostEnum->pAppDesc ) 
    { 
        hr = E_OUTOFMEMORY; 
        DXTRACE_ERR_MSGBOX( TEXT("SessionsDlgNoteEnumResponse"), hr ); 
        goto LCleanup; 
    } 
    ZeroMemory( pDPHostEnum->pAppDesc, sizeof(DPN_APPLICATION_DESC) ); 
    memcpy( pDPHostEnum->pAppDesc, pResponseMsgAppDesc, sizeof(DPN_APPLICATION_DESC) ); 
    if( pResponseMsgAppDesc->pwszSessionName ) 
    { 
        WCHAR* wstr = new WCHAR[ wcslen(pResponseMsgAppDesc->pwszSessionName)+1 ]; 
        if( NULL == wstr ) 
        { 
            hr = E_OUTOFMEMORY; 
            DXTRACE_ERR_MSGBOX( TEXT("SessionsDlgNoteEnumResponse"), hr ); 
            goto LCleanup; 
        } 
         
        wcscpy( wstr, pResponseMsgAppDesc->pwszSessionName ); 
        pDPHostEnum->pAppDesc->pwszSessionName = wstr; 
    } 
 
    // 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 = GETTIMESTAMP(); 
 
    // Check to see if the current number of players changed 
    TCHAR szSessionTemp[MAX_PATH]; 
    if( pResponseMsgAppDesc->dwMaxPlayers > 0 ) 
    { 
        _sntprintf( szSessionTemp, MAX_PATH-1, TEXT("%s (%d/%d) (%dms)"), strName, 
                    pResponseMsgAppDesc->dwCurrentPlayers - 1,  // ignore the host player 
                    pResponseMsgAppDesc->dwMaxPlayers - 1,      // ignore the host player 
                    pEnumHostsResponseMsg->dwRoundTripLatencyMS ); 
 
        // Null terminate 
        szSessionTemp[ MAX_PATH-1 ] = 0; 
    } 
    else 
    { 
        _sntprintf( szSessionTemp, MAX_PATH-1, TEXT("%s (%d) (%dms)"), strName, 
                    pResponseMsgAppDesc->dwCurrentPlayers - 1,  // ignore the host player 
                    pEnumHostsResponseMsg->dwRoundTripLatencyMS ); 
 
        // Null terminate 
        szSessionTemp[ MAX_PATH-1 ] = 0; 
    } 
 
    // 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, szSessionTemp ) != 0 ) ) 
    { 
        m_bEnumListChanged = TRUE; 
    } 
    _tcscpy( pDPHostEnum->szSession, szSessionTemp ); 
 
    // This host is now valid 
    pDPHostEnum->bValid = TRUE; 
 
LCleanup: 
    LeaveCriticalSection( &m_csHostEnum ); 
 
    return hr; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: SessionsDlgExpireOldHostEnums 
// Desc: Check all nodes to see if any have expired yet. 
//----------------------------------------------------------------------------- 
VOID CNetClientWizard::SessionsDlgExpireOldHostEnums() 
{ 
    DWORD dwCurrentTime = GETTIMESTAMP(); 
 
    // This is called from the dialog UI thread, SessionsDlgNoteEnumResponse 
    // 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( &m_csHostEnum ); 
 
    DPHostEnumInfo* pDPHostEnum = m_DPHostEnumHead.pNext; 
    while ( pDPHostEnum != &m_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 - m_dwEnumHostExpireInterval ) ) 
        { 
            // This node has expired, so invalidate it. 
            pDPHostEnum->bValid = FALSE; 
            m_bEnumListChanged  = TRUE; 
        } 
 
        pDPHostEnum = pDPHostEnum->pNext; 
    } 
 
    LeaveCriticalSection( &m_csHostEnum ); 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: SessionsDlgDisplayEnumList 
// Desc: Display the list of hosts in the dialog box 
//----------------------------------------------------------------------------- 
HRESULT CNetClientWizard::SessionsDlgDisplayEnumList( HWND hDlg ) 
{ 
    HWND           hWndListBox   = GetDlgItem( hDlg, IDC_GAMES_LIST ); 
    DPHostEnumInfo* pDPHostEnumSelected = NULL; 
    GUID           guidSelectedInstance; 
    BOOL           bFindSelectedGUID; 
    BOOL           bFoundSelectedGUID; 
    int            nItemSelected; 
 
    // This is called from the dialog UI thread, SessionsDlgNoteEnumResponse 
    // 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( &m_csHostEnum ); 
 
    // Only update the display list if it has changed since last time 
    if( !m_bEnumListChanged ) 
    { 
        LeaveCriticalSection( &m_csHostEnum ); 
        return S_OK; 
    } 
 
    m_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 
    DPHostEnumInfo* pDPHostEnum = m_DPHostEnumHead.pNext; 
    while ( pDPHostEnum != &m_DPHostEnumHead ) 
    { 
        if( pDPHostEnum->bValid ) 
            break; 
        pDPHostEnum = pDPHostEnum->pNext; 
    } 
 
    // If there are any sessions in list, 
    // then add them to the listbox 
    if( pDPHostEnum != &m_DPHostEnumHead ) 
    { 
        // Clear the contents from the list box and enable the join button 
        SendMessage( hWndListBox, LB_RESETCONTENT, 0, 0 ); 
 
        // Enable the join button only if not already connecting to a game 
        if( !m_bConnecting )         
            EnableWindow( GetDlgItem( hDlg, IDC_JOIN ), TRUE ); 
 
        pDPHostEnum = m_DPHostEnumHead.pNext; 
        while ( pDPHostEnum != &m_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( &m_csHostEnum ); 
 
    return S_OK; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: SessionsDlgJoinGame() 
// Desc: Joins the selected DirectPlay session 
//----------------------------------------------------------------------------- 
HRESULT CNetClientWizard::SessionsDlgJoinGame( HWND hDlg ) 
{ 
    HRESULT         hr; 
    HWND            hWndListBox = GetDlgItem( hDlg, IDC_GAMES_LIST ); 
    DPHostEnumInfo* pDPHostEnumSelected = NULL; 
    int             nItemSelected; 
 
    // Add status text in list box 
    nItemSelected = (int)SendMessage( hWndListBox, LB_GETCURSEL, 0, 0 ); 
 
    EnterCriticalSection( &m_csHostEnum ); 
 
    pDPHostEnumSelected = (DPHostEnumInfo*) SendMessage( hWndListBox, LB_GETITEMDATA, 
                                                         nItemSelected, 0 ); 
 
    if( NULL == pDPHostEnumSelected ) 
    { 
        MessageBox( hDlg, TEXT("There are no games to join."), 
                    TEXT("DirectPlay Sample"), MB_OK ); 
        hr = S_OK; 
        goto LCleanReturn; 
    } 
 
    m_bConnecting = TRUE; 
 
    // Set the peer info 
    WCHAR wszPeerName[MAX_PATH]; 
    GetDlgItemText( hDlg, IDC_PLAYER_NAME_EDIT, m_strLocalPlayerName, MAX_PATH ); 
    DXUtil_ConvertGenericStringToWideCch( wszPeerName, m_strLocalPlayerName, MAX_PATH ); 
 
    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 = m_pDPClient->SetClientInfo( &dpPlayerInfo, NULL, NULL, DPNOP_SYNC ) ) ) 
    { 
        DXTRACE_ERR_MSGBOX( TEXT("SetClientInfo"), hr ); 
        goto LCleanReturn; 
    } 
 
    ResetEvent( m_hConnectCompleteEvent ); 
 
    // 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 = m_pDPClient->Connect( pDPHostEnumSelected->pAppDesc,       // the application desc 
                               pDPHostEnumSelected->pHostAddr,      // address of the host of the session 
                               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, &m_hConnectAsyncOp,            // async context, async handle, 
                               DPNCONNECT_OKTOQUERYFORADDRESSING ); // flags 
    if( FAILED(hr) && hr != E_PENDING ) 
    { 
        DXTRACE_ERR_MSGBOX( TEXT("Connect"), hr ); 
        goto LCleanReturn; 
    } 
 
     
    // 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. 
    SetTimer( hDlg, TIMERID_CONNECT_COMPLETE, 100, NULL ); 
 
    // Disable the join button until connect succeeds or fails 
    EnableWindow( GetDlgItem( hDlg, IDC_JOIN ), FALSE ); 
 
    hr = S_OK; 
 
LCleanReturn: 
    LeaveCriticalSection( &m_csHostEnum ); 
 
    return hr; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: SessionsDlgEnumListCleanup() 
// Desc: Deletes the linked list, g_DPHostEnumInfoHead 
//----------------------------------------------------------------------------- 
VOID CNetClientWizard::SessionsDlgEnumListCleanup() 
{ 
    DPHostEnumInfo* pDPHostEnum = m_DPHostEnumHead.pNext; 
    DPHostEnumInfo* pDPHostEnumDelete; 
 
    while ( pDPHostEnum != &m_DPHostEnumHead ) 
    { 
        pDPHostEnumDelete = pDPHostEnum; 
        pDPHostEnum = pDPHostEnum->pNext; 
 
        if( pDPHostEnumDelete->pAppDesc ) 
        { 
            SAFE_DELETE_ARRAY( pDPHostEnumDelete->pAppDesc->pwszSessionName ); 
            SAFE_DELETE( pDPHostEnumDelete->pAppDesc ); 
        } 
 
        // Changed from array delete to Release 
        SAFE_RELEASE( pDPHostEnumDelete->pHostAddr ); 
        SAFE_RELEASE( pDPHostEnumDelete->pDeviceAddr ); 
        SAFE_DELETE( pDPHostEnumDelete ); 
    } 
 
    // Re-link the g_DPHostEnumInfoHead circular linked list 
    m_DPHostEnumHead.pNext = &m_DPHostEnumHead; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: MessageHandler 
// Desc: Handler for DirectPlay messages.  This function is called by 
//       the DirectPlay message handler pool of threads, so be careful of thread 
//       synchronization problems with shared memory 
//----------------------------------------------------------------------------- 
HRESULT WINAPI CNetClientWizard::MessageHandler( PVOID pvUserContext, 
                                                  DWORD dwMessageId, 
                                                  PVOID pMsgBuffer ) 
{ 
    // Try not to stay in this message handler for too long, otherwise 
    // there will be a backlog of data.  The best solution is to 
    // queue data as it comes in, and then handle it on other threads. 
 
    // This function is called by the DirectPlay message handler pool of 
    // threads, so be careful of thread synchronization problems with shared memory 
 
    switch(dwMessageId) 
    { 
        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 == m_hEnumAsyncOp ) 
            { 
                SessionsDlgEnumListCleanup(); 
 
                // The user canceled the DirectPlay connection dialog, 
                // so stop the search 
                if( m_bSearchingForSessions ) 
                { 
                    CheckDlgButton( m_hDlg, IDC_SEARCH_CHECK, BST_UNCHECKED ); 
                    SendMessage( m_hDlg, WM_COMMAND, IDC_SEARCH_CHECK, 0 ); 
                } 
 
                m_hEnumAsyncOp = NULL; 
                m_bSearchingForSessions = FALSE; 
            } 
            break; 
        } 
 
        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 
            m_hrConnectComplete = pConnectCompleteMsg->hResultCode; 
            SetEvent( m_hConnectCompleteEvent ); 
            break; 
        } 
    } 
 
    return S_OK; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: ConnectUsingLobbySettings 
// Desc: Call this after the DPL_MSGID_CONNECT has been processed to carry out 
//       the connection settings received by the lobby client.  DPL_MSGID_CONNECT 
//       will have already been processed if we were lobby launched, or after 
//       WaitForConnection returns without timing out. 
//----------------------------------------------------------------------------- 
HRESULT CNetClientWizard::ConnectUsingLobbySettings() 
{ 
    HRESULT hr; 
    DPNHANDLE hAsync; 
 
    if( m_hLobbyClient == NULL ) 
        return E_INVALIDARG; 
 
    DPL_CONNECTION_SETTINGS* pSettings = NULL; 
    DWORD dwSettingsSize = 0; 
 
    // Get the connection settings from the lobby. 
    hr = m_pLobbiedApp->GetConnectionSettings( m_hLobbyClient, pSettings, &dwSettingsSize, 0 ); 
    if( hr != DPNERR_BUFFERTOOSMALL ) 
    { 
        DXTRACE_ERR_MSGBOX( TEXT("GetConnectionSettings"), hr ); 
        goto LCleanReturn; 
    } 
 
    pSettings = (DPL_CONNECTION_SETTINGS*) new BYTE[dwSettingsSize]; 
    if( NULL == pSettings ) 
    { 
        hr = E_OUTOFMEMORY; 
        DXTRACE_ERR_MSGBOX( TEXT("ConnectUsingLobbySettings"), hr ); 
        goto LCleanReturn; 
    } 
 
    if( FAILED( hr = m_pLobbiedApp->GetConnectionSettings( m_hLobbyClient, pSettings, &dwSettingsSize, 0 ) ) ) 
    { 
        DXTRACE_ERR_MSGBOX( TEXT("GetConnectionSettings"), hr ); 
        goto LCleanReturn; 
    } 
 
    // Set the peer info 
    WCHAR wszPeerName[MAX_PATH]; 
    DXUtil_ConvertGenericStringToWideCch( wszPeerName, m_strLocalPlayerName, MAX_PATH ); 
    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 = m_pDPClient->SetClientInfo( &dpPlayerInfo, NULL, NULL, DPNOP_SYNC ) ) ) 
    { 
        DXTRACE_ERR_MSGBOX( TEXT("SetClientInfo"), hr ); 
        goto LCleanReturn; 
    } 
 
    // Connect to an existing session. There should only be on device address in 
    // the connection settings structure when connecting to a session, so just 
    // pass in the first one. 
    // The enumeration is automatically cancelled after Connect is called  
    hr = m_pDPClient->Connect( &pSettings->dpnAppDesc,              // the application desc 
                               pSettings->pdp8HostAddress,          // address of the host of the session 
                               pSettings->ppdp8DeviceAddresses[0],  // address of the local device used to connect to the host 
                               NULL, NULL,                          // DPN_SECURITY_DESC, DPN_SECURITY_CREDENTIALS 
                               NULL, 0,                             // user data, user data size 
                               NULL, &hAsync,                       // async context, async handle, 
                               0 );                                 // flags 
    if( hr != E_PENDING && FAILED(hr) ) 
    { 
        DXTRACE_ERR_MSGBOX( TEXT("Connect"), hr ); 
        goto LCleanReturn; 
    } 
 
    hr = S_OK; // Accept E_PENDING. 
 
    // Wait until the MessageHandler sets an event to tell us the 
    // DPN_MSGID_CONNECT_COMPLETE has been processed.  Then m_hrConnectComplete 
    // will be valid. 
    WaitForSingleObject( m_hConnectCompleteEvent, INFINITE ); 
 
    if( FAILED( m_hrConnectComplete ) ) 
    { 
        DXTRACE_ERR_MSGBOX( TEXT("DPN_MSGID_CONNECT_COMPLETE"), m_hrConnectComplete ); 
        MessageBox( m_hDlg, TEXT("Unable to join game."), 
                    TEXT("DirectPlay Sample"), 
                    MB_OK | MB_ICONERROR ); 
        hr = m_hrConnectComplete; 
    } 
 
    // Cleanup the addresses and memory obtained from GetConnectionSettings 
LCleanReturn: 
    if( pSettings ) 
    { 
        SAFE_RELEASE( pSettings->pdp8HostAddress ); 
 
        for( DWORD dwIndex=0; dwIndex < pSettings->cNumDeviceAddresses; dwIndex++ ) 
            SAFE_RELEASE( pSettings->ppdp8DeviceAddresses[dwIndex] ); 
         
        SAFE_DELETE_ARRAY( pSettings ); 
    } 
 
    return hr; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: LobbyMessageHandler 
// Desc: Handler for DirectPlay messages.  This function is called by 
//       the DirectPlay lobby message handler pool of threads, so be careful of thread 
//       synchronization problems with shared memory 
//----------------------------------------------------------------------------- 
HRESULT WINAPI CNetClientWizard::LobbyMessageHandler( PVOID pvUserContext, 
                                                       DWORD dwMessageId, 
                                                       PVOID pMsgBuffer ) 
{ 
    HRESULT hr = S_OK; 
 
    switch(dwMessageId) 
    { 
        case DPL_MSGID_CONNECT: 
        { 
            // This message will be processed when a lobby connection has been 
            // established. If you were lobby launched then 
            // IDirectPlay8LobbiedApplication::Initialize() 
            // waits until this message has been processed before returning, so 
            // take care not to deadlock by making calls that need to be handled by 
            // the thread who called Initialize().  The same is true for WaitForConnection() 
 
            PDPL_MESSAGE_CONNECT pConnectMsg; 
            pConnectMsg = (PDPL_MESSAGE_CONNECT)pMsgBuffer; 
            PDPL_CONNECTION_SETTINGS pSettings = pConnectMsg->pdplConnectionSettings; 
 
            m_hLobbyClient = pConnectMsg->hConnectId; 
 
            if( FAILED( hr = m_pDPClient->RegisterLobby( m_hLobbyClient, m_pLobbiedApp, 
                                                   DPNLOBBY_REGISTER ) ) ) 
                return DXTRACE_ERR_MSGBOX( TEXT("RegisterLobby"), hr ); 
 
            if( pSettings == NULL ) 
            { 
                // There aren't connection settings from the lobby 
                m_bHaveConnectionSettingsFromLobby = FALSE; 
            } 
            else 
            { 
                // Record the player name if found 
                if( pSettings->pwszPlayerName != NULL ) 
                { 
                    TCHAR strPlayerName[MAX_PATH]; 
                    DXUtil_ConvertWideStringToGenericCch( strPlayerName, pSettings->pwszPlayerName, MAX_PATH ); 
                    _tcscpy( m_strLocalPlayerName, strPlayerName ); 
                } 
                else 
                { 
                    _tcscpy( m_strLocalPlayerName, TEXT("Unknown player name") ); 
                } 
 
                m_bHaveConnectionSettingsFromLobby = TRUE; 
            } 
 
            // Tell everyone we have a lobby connection now 
            SetEvent( m_hLobbyConnectionEvent ); 
            break; 
        } 
    } 
 
    return S_OK; 
} 
 
 
 
//----------------------------------------------------------------------------- 
// Name: StaticLobbyWaitDlgProc() 
// Desc: Static msg handler which passes messages 
//----------------------------------------------------------------------------- 
INT_PTR CALLBACK CNetClientWizard::StaticLobbyWaitDlgProc( HWND hDlg, UINT uMsg, 
                                                                WPARAM wParam, LPARAM lParam ) 
{ 
    if( g_pNCW ) 
        return g_pNCW->LobbyWaitDlgProc( hDlg, uMsg, wParam, lParam ); 
 
    return FALSE; // Message not handled 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: LobbyWaitDlgProc() 
// Desc: Handles messages for the lobby wait status dialog 
//----------------------------------------------------------------------------- 
INT_PTR CALLBACK CNetClientWizard::LobbyWaitDlgProc( HWND hDlg, UINT msg, 
                                                      WPARAM wParam, LPARAM lParam ) 
{ 
    switch( msg ) 
    { 
        case WM_INITDIALOG: 
#if defined(WIN32_PLATFORM_PSPC) && (_WIN32_WCE >= 300) 
			SHINITDLGINFO	shidi; 
			memset(&shidi, 0, sizeof(SHINITDLGINFO)); 
			shidi.dwMask = SHIDIM_FLAGS; 
			shidi.dwFlags = SHIDIF_SIPDOWN | SHIDIF_SIZEDLGFULLSCREEN; 
			shidi.hDlg = hDlg; 
 
    	    SetForegroundWindow(hDlg); 
    	    SHInitDialog(&shidi); 
#endif // WIN32_PLATFORM_PSPC 
 
			// 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. 
            SetTimer( hDlg, TIMERID_CONNECT_COMPLETE, 100, NULL ); 
 
            SetDlgItemText( hDlg, IDC_WAIT_TEXT, TEXT("Waiting for lobby connection...") ); 
            return TRUE; 
 
        case WM_COMMAND: 
            switch( LOWORD(wParam) ) 
            { 
                case IDCANCEL: 
                    EndDialog( hDlg, IDCANCEL ); 
                    return TRUE; 
            } 
            break; 
 
        case WM_TIMER: 
        { 
            if( wParam == TIMERID_CONNECT_COMPLETE ) 
            { 
                // Wait for a lobby connection.  If this call 
                // returns WAIT_OBJECT_0 then the DPL_MSGID_CONNECT will 
                // have already been processed. 
                DWORD dwResult = WaitForSingleObject( m_hLobbyConnectionEvent, 100 ); 
                if( dwResult != WAIT_TIMEOUT ) 
                    EndDialog( hDlg, IDOK ); 
            } 
            break; 
        } 
    } 
 
    return FALSE; // Didn't handle message 
}