netclient.cpp
上传用户:sycq158
上传日期:2008-10-22
资源大小:15361k
文件大小:48k
源码类别:

游戏

开发平台:

Visual C++

  1. //-----------------------------------------------------------------------------
  2. // File: NetClient.cpp
  3. //
  4. // Desc: This is a class that given a IDirectPlay8Client upon DoConnectWizard()
  5. //       will enumerate hosts, and allows the user to join a session.  The class uses
  6. //       dialog boxes and GDI for the interactive UI.  Most games will
  7. //       want to change the graphics to use Direct3D or another graphics
  8. //       layer, but this simplistic sample uses dialog boxes. Feel 
  9. //       free to use this class as a starting point for adding extra 
  10. //       functionality.
  11. //
  12. // Copyright (c) Microsoft Corporation. All rights reserved.
  13. //-----------------------------------------------------------------------------
  14. #ifndef STRICT
  15. #define STRICT
  16. #endif // !STRICT
  17. #include <windows.h>
  18. #include <basetsd.h>
  19. #include <stdio.h>
  20. #include <mmsystem.h>
  21. #include <dxerr9.h>
  22. #include <dplay8.h>
  23. #include <dpaddr.h>
  24. #include <dplobby8.h>
  25. #include "NetClient.h"
  26. #include "NetClientRes.h"
  27. #include "DXUtil.h"
  28. #if defined(WIN32_PLATFORM_PSPC) && (_WIN32_WCE >= 300)
  29. #include <aygshell.h>
  30. #endif // PocketPC
  31. //-----------------------------------------------------------------------------
  32. // Global variables
  33. //-----------------------------------------------------------------------------
  34. CNetClientWizard* g_pNCW = NULL;           // Pointer to the net connect wizard
  35. //-----------------------------------------------------------------------------
  36. // Name: CNetClientWizard
  37. // Desc: Init the class
  38. //-----------------------------------------------------------------------------
  39. CNetClientWizard::CNetClientWizard( HINSTANCE hInst, LPCTSTR strAppName,
  40.                                       GUID* pGuidApp )
  41. {
  42.     g_pNCW    = this;
  43.     m_hInst   = hInst;
  44.     m_guidApp = *pGuidApp;
  45.     m_dwPort  = 0;
  46.     
  47.     if( NULL == strAppName )
  48.         strAppName = TEXT("DirectPlay App");
  49.     _tcsncpy( m_strAppName, strAppName, MAX_PATH-1 );
  50.     m_strAppName[ MAX_PATH-1 ] = 0;
  51.    
  52.     m_dwEnumHostExpireInterval          = 0;
  53.     m_hConnectCompleteEvent             = NULL;
  54.     m_hrConnectComplete                 = 0;
  55.     m_bConnecting                       = FALSE;
  56.     m_bEnumListChanged                  = FALSE;
  57.     m_bSearchingForSessions             = FALSE;
  58.     m_hEnumAsyncOp                      = NULL;
  59.     m_hConnectAsyncOp                   = NULL;
  60.     m_pDPClient                         = NULL;
  61.     m_pLobbiedApp                       = NULL;
  62.     m_bHaveConnectionSettingsFromLobby  = FALSE;
  63.     m_hLobbyClient                      = NULL;
  64.     m_hDlg                              = NULL;
  65.     InitializeCriticalSection( &m_csHostEnum );
  66.     m_hConnectCompleteEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  67.     m_hLobbyConnectionEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  68.     // Setup the m_DPHostEnumHead circular linked list
  69.     ZeroMemory( &m_DPHostEnumHead, sizeof( DPHostEnumInfo ) );
  70.     m_DPHostEnumHead.pNext = &m_DPHostEnumHead;
  71. }
  72. //-----------------------------------------------------------------------------
  73. // Name: ~CNetClientWizard
  74. // Desc: Cleanup the class
  75. //-----------------------------------------------------------------------------
  76. CNetClientWizard::~CNetClientWizard()
  77. {
  78.     DeleteCriticalSection( &m_csHostEnum );
  79.     CloseHandle( m_hConnectCompleteEvent );
  80.     CloseHandle( m_hLobbyConnectionEvent );
  81. }
  82. //-----------------------------------------------------------------------------
  83. // Name: Init
  84. // Desc: Initialize member variables
  85. //-----------------------------------------------------------------------------
  86. HRESULT CNetClientWizard::Init( IDirectPlay8Client* pDPClient,
  87.                                 IDirectPlay8LobbiedApplication* pLobbiedApp )
  88. {
  89.     if( NULL == pDPClient || NULL == pLobbiedApp )
  90.         return E_INVALIDARG;
  91.     m_pDPClient         = pDPClient;
  92.     m_pLobbiedApp       = pLobbiedApp;
  93.     m_bHaveConnectionSettingsFromLobby = FALSE;
  94.     m_hLobbyClient      = NULL;
  95.     return S_OK;
  96. }
  97. //-----------------------------------------------------------------------------
  98. // Name: DoConnectWizard
  99. // Desc: Show the connection wizard UI
  100. //-----------------------------------------------------------------------------
  101. HRESULT CNetClientWizard::DoConnectWizard()
  102. {
  103.     m_hrDialog = S_OK;
  104.     // Display the multiplayer games dialog box.
  105.     DialogBox( m_hInst, MAKEINTRESOURCE(IDD_CLIENT_CONNECT), NULL, 
  106.                (DLGPROC) StaticSessionsDlgProc );
  107.     return m_hrDialog;
  108. }
  109. //-----------------------------------------------------------------------------
  110. // Name: StaticSessionsDlgProc()
  111. // Desc: Static msg handler which passes messages
  112. //-----------------------------------------------------------------------------
  113. INT_PTR CALLBACK CNetClientWizard::StaticSessionsDlgProc( HWND hDlg, UINT uMsg,
  114.                                                           WPARAM wParam, LPARAM lParam )
  115. {
  116.     if( g_pNCW )
  117.         return g_pNCW->SessionsDlgProc( hDlg, uMsg, wParam, lParam );
  118.     return FALSE; // Message not handled
  119. }
  120. //-----------------------------------------------------------------------------
  121. // Name: SessionsDlgProc()
  122. // Desc: Handles messages for the multiplayer games dialog
  123. //-----------------------------------------------------------------------------
  124. INT_PTR CALLBACK CNetClientWizard::SessionsDlgProc( HWND hDlg, UINT msg,
  125.                                                     WPARAM wParam, LPARAM lParam )
  126. {
  127.     HRESULT hr;
  128.     switch( msg )
  129.     {
  130.         case WM_INITDIALOG:
  131.             {
  132. #if defined(WIN32_PLATFORM_PSPC) && (_WIN32_WCE >= 300)
  133. SHINITDLGINFO shidi;
  134. memset(&shidi, 0, sizeof(SHINITDLGINFO));
  135. shidi.dwMask = SHIDIM_FLAGS;
  136. shidi.dwFlags = SHIDIF_SIPDOWN | SHIDIF_SIZEDLGFULLSCREEN;
  137. shidi.hDlg = hDlg;
  138. SetForegroundWindow(hDlg);
  139. SHInitDialog(&shidi);
  140. #endif // WIN32_PLATFORM_PSPC
  141. // Load and set the icon
  142.                 HICON hIcon = LoadIcon( m_hInst, MAKEINTRESOURCE( IDI_MAIN ) );
  143.                 SendMessage( hDlg, WM_SETICON, ICON_BIG,   (LPARAM) hIcon );  // Set big icon
  144.                 SendMessage( hDlg, WM_SETICON, ICON_SMALL, (LPARAM) hIcon );  // Set small icon
  145.                 SetDlgItemText( hDlg, IDC_PLAYER_NAME_EDIT, m_strLocalPlayerName );
  146.                 // Limit the player name size
  147.                 SendDlgItemMessage( hDlg, IDC_PLAYER_NAME_EDIT, EM_LIMITTEXT, MAX_PLAYER_NAME-1, 0 );
  148.                 // Set the window title
  149.                 TCHAR strWindowTitle[256];
  150.                 _sntprintf( strWindowTitle, 255, TEXT("%s - Multiplayer Games"), m_strAppName );
  151.                 strWindowTitle[255] = 0;
  152.                 SetWindowText( hDlg, strWindowTitle );
  153.                 // Set the default port
  154.                 if( m_dwPort != 0 )
  155.                 {
  156.                     TCHAR strPort[40];
  157.                     _itot( m_dwPort, strPort, 10 );
  158.                     SetDlgItemText( hDlg, IDC_REMOTE_PORT, strPort );
  159.                 }
  160.                 // Init the search portion of the dialog
  161.                 m_bSearchingForSessions = FALSE;
  162.                 SetDlgItemText( hDlg, IDC_SEARCH_CHECK, TEXT("Start Search") );
  163.                 SessionsDlgInitListbox( hDlg );
  164.             }
  165.             break;
  166.         case WM_TIMER:
  167.             // Upon this timer message, then refresh the list of hosts
  168.             // by expiring old hosts, and displaying the list in the
  169.             // dialog box
  170.             if( wParam == TIMERID_DISPLAY_HOSTS )
  171.             {
  172.                 // Don't refresh if we are not enumerating hosts
  173.                 if( !m_bSearchingForSessions )
  174.                     break;
  175.                 // Expire all of the hosts that haven't
  176.                 // refreshed in a certain period of time
  177.                 SessionsDlgExpireOldHostEnums();
  178.                 // Display the list of hosts in the dialog
  179.                 if( FAILED( hr = SessionsDlgDisplayEnumList( hDlg ) ) )
  180.                 {
  181.                     DXTRACE_ERR_MSGBOX( TEXT("SessionsDlgEnumHosts"), hr );
  182.                     MessageBox( hDlg, TEXT("Error enumerating DirectPlay games."),
  183.                                 m_strAppName, MB_OK | MB_ICONERROR );
  184.                     m_bSearchingForSessions = FALSE;
  185.                     KillTimer( hDlg, TIMERID_DISPLAY_HOSTS );
  186.                     CheckDlgButton( hDlg, IDC_SEARCH_CHECK, BST_UNCHECKED );
  187.                     SetDlgItemText( hDlg, IDC_SEARCH_CHECK, TEXT("Start Search") );
  188.                     SessionsDlgInitListbox( hDlg );
  189.                 }
  190.             }
  191.             else if( wParam == TIMERID_CONNECT_COMPLETE )
  192.             {
  193.                 // Check to see if the MessageHandler has set an event to tell us the
  194.                 // DPN_MSGID_CONNECT_COMPLETE has been processed.  Now m_hrConnectComplete
  195.                 // is valid.
  196.                 if( WAIT_OBJECT_0 == WaitForSingleObject( m_hConnectCompleteEvent, 0 ) )
  197.                 {
  198.                     m_bConnecting = FALSE;
  199.                     if( FAILED( m_hrConnectComplete ) )
  200.                     {
  201.                         DXTRACE_ERR_MSGBOX( TEXT("DPN_MSGID_CONNECT_COMPLETE"), m_hrConnectComplete );
  202.                         MessageBox( hDlg, TEXT("Unable to join game."),
  203.                                     m_strAppName, MB_OK | MB_ICONERROR );
  204.                     }
  205.                     else
  206.                     {
  207.                         // DirectPlay connect successful, so end dialog
  208.                         m_hrDialog = NCW_S_FORWARD;
  209.                         EndDialog( hDlg, 0 );
  210.                     }
  211.                 }
  212.             }
  213.             break;
  214.         case WM_COMMAND:
  215.             switch( LOWORD(wParam) )
  216.             {
  217.                 case IDC_SEARCH_CHECK:
  218.                     m_bSearchingForSessions = !m_bSearchingForSessions;
  219.                     if( m_bSearchingForSessions )
  220.                     {
  221.                         SetDlgItemText( hDlg, IDC_SEARCH_CHECK, TEXT("Searching...") );
  222.                         // Start the timer to display the host list every so often
  223.                         SetTimer( hDlg, TIMERID_DISPLAY_HOSTS, DISPLAY_REFRESH_RATE, NULL );
  224.                         // Start the async enumeration
  225.                         if( FAILED( hr = SessionsDlgEnumHosts( hDlg ) ) )
  226.                         {
  227.                             if( hr == DPNERR_ADDRESSING )
  228.                             {
  229.                                 // This will be returned if the ip address is invalid
  230.                                 // for example something like "asdf" 
  231.                                 MessageBox( hDlg, TEXT("IP address not valid. Stopping search"),
  232.                                             m_strAppName, MB_OK );
  233.                             }
  234.                             else
  235.                             {
  236.                                 DXTRACE_ERR_MSGBOX( TEXT("SessionsDlgEnumHosts"), hr );
  237.                                 MessageBox( hDlg, TEXT("Error enumerating DirectPlay games."),
  238.                                             m_strAppName, MB_OK | MB_ICONERROR );
  239.                             }
  240.                             m_bSearchingForSessions = FALSE;
  241.                             KillTimer( hDlg, TIMERID_DISPLAY_HOSTS );
  242.                             CheckDlgButton( hDlg, IDC_SEARCH_CHECK, BST_UNCHECKED );
  243.                             SetDlgItemText( hDlg, IDC_SEARCH_CHECK, TEXT("Start Search") );
  244.                             SessionsDlgInitListbox( hDlg );
  245.                         }
  246.                     }
  247.                     else
  248.                     {
  249.                         SetDlgItemText( hDlg, IDC_SEARCH_CHECK, TEXT("Start Search") );
  250.                         // Stop the timer, and stop the async enumeration
  251.                         KillTimer( hDlg, TIMERID_DISPLAY_HOSTS );
  252.                         // Until the CancelAsyncOperation returns, it is possible
  253.                         // to still receive host enumerations
  254.                         if( m_hEnumAsyncOp )
  255.                             m_pDPClient->CancelAsyncOperation( m_hEnumAsyncOp, 0 );
  256.                         // Reset the search portion of the dialog
  257.                         SessionsDlgInitListbox( hDlg );
  258.                     }
  259.                     break;
  260.                 case IDC_GAMES_LIST:
  261.                     if( HIWORD(wParam) != LBN_DBLCLK )
  262.                         break;
  263.                     // Fall through
  264.                 case IDC_JOIN:
  265.                     if( FAILED( hr = SessionsDlgJoinGame( hDlg ) ) )
  266.                     {
  267.                         DXTRACE_ERR_MSGBOX( TEXT("SessionsDlgJoinGame"), hr );
  268.                         MessageBox( hDlg, TEXT("Unable to join game."),
  269.                                     TEXT("DirectPlay Sample"),
  270.                                     MB_OK | MB_ICONERROR );
  271.                     }
  272.                     break;
  273.                 case IDCANCEL: // The close button was press
  274.                     m_hrDialog = NCW_S_QUIT;
  275.                     EndDialog( hDlg, 0 );
  276.                     break;
  277.                 default:
  278.                     return FALSE; // Message not handled
  279.             }
  280.             break;
  281.         case WM_DESTROY:
  282.         {
  283.             KillTimer( hDlg, 1 );
  284.             // Cancel the enum hosts search
  285.             // if the enumeration is going on
  286.             if( m_bSearchingForSessions && m_hEnumAsyncOp )
  287.             {
  288.                 m_pDPClient->CancelAsyncOperation( m_hEnumAsyncOp, 0 );
  289.                 m_bSearchingForSessions = FALSE;
  290.             }
  291.             break;
  292.         }
  293.         default:
  294.             return FALSE; // Message not handled
  295.     }
  296.     // Message was handled
  297.     return TRUE;
  298. }
  299. //-----------------------------------------------------------------------------
  300. // Name: SessionsDlgInitListbox()
  301. // Desc: Initializes the listbox
  302. //-----------------------------------------------------------------------------
  303. VOID CNetClientWizard::SessionsDlgInitListbox( HWND hDlg )
  304. {
  305.     HWND hWndListBox = GetDlgItem( hDlg, IDC_GAMES_LIST );
  306.     // Clear the contents from the list box, and
  307.     // display "Looking for games" text in listbox
  308.     SendMessage( hWndListBox, LB_RESETCONTENT, 0, 0 );
  309.     if( m_bSearchingForSessions )
  310.     {
  311.         SendMessage( hWndListBox, LB_ADDSTRING, 0,
  312.                      (LPARAM) TEXT("Looking for games...") );
  313.     }
  314.     else
  315.     {
  316.         SendMessage( hWndListBox, LB_ADDSTRING, 0,
  317.                      (LPARAM) TEXT("Click Start Search to see a list of games."));
  318.     }
  319.     SendMessage( hWndListBox, LB_SETITEMDATA,  0, NULL );
  320.     SendMessage( hWndListBox, LB_SETCURSEL,    0, 0 );
  321.     // Disable the join button until sessions are found
  322.     EnableWindow( GetDlgItem( hDlg, IDC_JOIN ), FALSE );
  323.     // Query for the enum host timeout for this SP
  324.     DPN_SP_CAPS dpspCaps;
  325.     ZeroMemory( &dpspCaps, sizeof(DPN_SP_CAPS) );
  326.     dpspCaps.dwSize = sizeof(DPN_SP_CAPS);
  327.     
  328.     if( SUCCEEDED( m_pDPClient->GetSPCaps( &CLSID_DP8SP_TCPIP, &dpspCaps, 0 ) ) )
  329.     {
  330.         // Set the host expire time to around 3 times
  331.         // length of the dwDefaultEnumRetryInterval
  332.         m_dwEnumHostExpireInterval = dpspCaps.dwDefaultEnumRetryInterval * 3;
  333.     }
  334. }
  335. //-----------------------------------------------------------------------------
  336. // Name: SessionsDlgEnumHosts()
  337. // Desc: Enumerates the DirectPlay sessions, and displays them in the listbox
  338. //-----------------------------------------------------------------------------
  339. HRESULT CNetClientWizard::SessionsDlgEnumHosts( HWND hDlg )
  340. {
  341.     HRESULT hr;
  342.     m_bEnumListChanged = TRUE;
  343.     DPN_APPLICATION_DESC   dpnAppDesc;
  344.     IDirectPlay8Address*   pDP8AddressHost  = NULL;
  345.     IDirectPlay8Address*   pDP8AddressLocal = NULL;
  346.     WCHAR*                 wszHostName      = NULL;
  347.     // Create the local device address object
  348.     if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay8Address, NULL, 
  349.                                        CLSCTX_ALL, IID_IDirectPlay8Address,
  350.                                        (LPVOID*) &pDP8AddressLocal ) ) )
  351.     {
  352.         DXTRACE_ERR_MSGBOX( TEXT("CoCreateInstance"), hr );
  353.         goto LCleanup;
  354.     }
  355.     // Set IP service provider
  356.     if( FAILED( hr = pDP8AddressLocal->SetSP( &CLSID_DP8SP_TCPIP ) ) )
  357.     {
  358.         DXTRACE_ERR_MSGBOX( TEXT("SetSP"), hr );
  359.         goto LCleanup;
  360.     }
  361.     // Create the remote host address object
  362.     if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay8Address, NULL, 
  363.                                        CLSCTX_ALL, IID_IDirectPlay8Address,
  364.                                        (LPVOID*) &pDP8AddressHost ) ) )
  365.     {
  366.         DXTRACE_ERR_MSGBOX( TEXT("CoCreateInstance"), hr );
  367.         goto LCleanup;
  368.     }
  369.     // Set IP service provider
  370.     if( FAILED( hr = pDP8AddressHost->SetSP( &CLSID_DP8SP_TCPIP ) ) )
  371.     {
  372.         DXTRACE_ERR_MSGBOX( TEXT("SetSP"), hr );
  373.         goto LCleanup;
  374.     }
  375.     // Set the remote host name (if provided)
  376.     TCHAR strIPAddress[MAX_PATH];
  377.     GetDlgItemText( hDlg, IDC_IP_ADDRESS, strIPAddress, MAX_PATH );
  378.     if( strIPAddress != NULL && strIPAddress[0] != 0 )
  379.     {
  380.         wszHostName = new WCHAR[_tcslen(strIPAddress)+1];
  381.         if( NULL == wszHostName )
  382.         {
  383.             hr = E_OUTOFMEMORY;
  384.             DXTRACE_ERR_MSGBOX( TEXT("SessionsDlgEnumHosts"), hr );
  385.             goto LCleanup;
  386.         }
  387.         DXUtil_ConvertGenericStringToWideCch( wszHostName, strIPAddress, (int)_tcslen(strIPAddress)+1 );
  388.         hr = pDP8AddressHost->AddComponent( DPNA_KEY_HOSTNAME, wszHostName, 
  389.                                             (DWORD) (wcslen(wszHostName)+1)*sizeof(WCHAR), 
  390.                                             DPNA_DATATYPE_STRING );
  391.         if( FAILED(hr) )
  392.         {
  393.             DXTRACE_ERR_MSGBOX( TEXT("AddComponent"), hr );
  394.             goto LCleanup;
  395.         }
  396.     }
  397.     TCHAR strPort[40];
  398.     GetDlgItemText( hDlg, IDC_REMOTE_PORT, strPort, 40 );
  399.     strPort[39] = 0;
  400.     m_dwPort = _ttoi( strPort );
  401.     // If a port was specified in the IP string, then add it.
  402.     // Games will typically hard code the port so the user need not know it
  403.     if( m_dwPort != 0 )
  404.     {
  405.         hr = pDP8AddressHost->AddComponent( DPNA_KEY_PORT, 
  406.                                             &m_dwPort, sizeof(m_dwPort),
  407.                                             DPNA_DATATYPE_DWORD );
  408.         if( FAILED(hr) )
  409.         {
  410.             DXTRACE_ERR_MSGBOX( TEXT("AddComponent"), hr );
  411.             goto LCleanup;
  412.         }
  413.     }
  414.  
  415.     ZeroMemory( &dpnAppDesc, sizeof( DPN_APPLICATION_DESC ) );
  416.     dpnAppDesc.dwSize = sizeof( DPN_APPLICATION_DESC );
  417.     dpnAppDesc.guidApplication = m_guidApp;
  418.     // Enumerate all StressMazeApp hosts running on IP service providers
  419.     hr = m_pDPClient->EnumHosts( &dpnAppDesc, pDP8AddressHost, 
  420.                                  pDP8AddressLocal, NULL, 
  421.                                  0, INFINITE, 0, INFINITE, NULL, 
  422.                                  &m_hEnumAsyncOp, 0 );
  423.     if( FAILED(hr) )
  424.     {
  425.         if( hr != DPNERR_INVALIDDEVICEADDRESS && 
  426.             hr != DPNERR_ADDRESSING ) // This will be returned if the ip address is is invalid. 
  427.             DXTRACE_ERR_MSGBOX( TEXT("EnumHosts"), hr );
  428.         goto LCleanup;
  429.     }
  430. LCleanup:
  431.     SAFE_RELEASE( pDP8AddressHost);
  432.     SAFE_RELEASE( pDP8AddressLocal );
  433.     SAFE_DELETE_ARRAY( wszHostName );
  434.     if( hr == DPNERR_PENDING )
  435.         hr = DPN_OK;
  436.     return hr;
  437. }
  438. //-----------------------------------------------------------------------------
  439. // Name: SessionsDlgNoteEnumResponse()
  440. // Desc: Stores them in the linked list, m_DPHostEnumHead.  This is
  441. //       called from the DirectPlay message handler so it could be
  442. //       called simultaneously from multiple threads.
  443. //-----------------------------------------------------------------------------
  444. HRESULT CNetClientWizard::SessionsDlgNoteEnumResponse( PDPNMSG_ENUM_HOSTS_RESPONSE pEnumHostsResponseMsg )
  445. {
  446.     HRESULT hr = S_OK;
  447.     BOOL    bFound;
  448.     // This function is called from the DirectPlay message handler so it could be
  449.     // called simultaneously from multiple threads, so enter a critical section
  450.     // to assure that it we don't get race conditions.  Locking the entire
  451.     // function is crude, and could be more optimal but is effective for this
  452.     // simple sample
  453.     EnterCriticalSection( &m_csHostEnum );
  454.     DPHostEnumInfo* pDPHostEnum          = m_DPHostEnumHead.pNext;
  455.     DPHostEnumInfo* pDPHostEnumNext      = NULL;
  456.     const DPN_APPLICATION_DESC* pResponseMsgAppDesc =
  457.                             pEnumHostsResponseMsg->pApplicationDescription;
  458.     // Look for a matching session instance GUID.
  459.     bFound = FALSE;
  460.     while ( pDPHostEnum != &m_DPHostEnumHead )
  461.     {
  462.         if( pResponseMsgAppDesc->guidInstance == pDPHostEnum->pAppDesc->guidInstance )
  463.         {
  464.             bFound = TRUE;
  465.             break;
  466.         }
  467.         pDPHostEnumNext = pDPHostEnum;
  468.         pDPHostEnum = pDPHostEnum->pNext;
  469.     }
  470.     if( !bFound )
  471.     {
  472.         m_bEnumListChanged = TRUE;
  473.         // If there's no match, then look for invalid session and use it
  474.         pDPHostEnum = m_DPHostEnumHead.pNext;
  475.         while ( pDPHostEnum != &m_DPHostEnumHead )
  476.         {
  477.             if( !pDPHostEnum->bValid )
  478.                 break;
  479.             pDPHostEnum = pDPHostEnum->pNext;
  480.         }
  481.         // If no invalid sessions are found then make a new one
  482.         if( pDPHostEnum == &m_DPHostEnumHead )
  483.         {
  484.             // Found a new session, so create a new node
  485.             pDPHostEnum = new DPHostEnumInfo;
  486.             if( NULL == pDPHostEnum )
  487.             {
  488.                 hr = E_OUTOFMEMORY;
  489.                 DXTRACE_ERR_MSGBOX( TEXT("SessionsDlgNoteEnumResponse"), hr );
  490.                 goto LCleanup;
  491.             }
  492.             ZeroMemory( pDPHostEnum, sizeof(DPHostEnumInfo) );
  493.             // Add pDPHostEnum to the circular linked list, m_DPHostEnumHead
  494.             pDPHostEnum->pNext = m_DPHostEnumHead.pNext;
  495.             m_DPHostEnumHead.pNext = pDPHostEnum;
  496.         }
  497.     }
  498.     // Update the pDPHostEnum with new information
  499.     TCHAR strName[MAX_PATH];
  500.     if( pResponseMsgAppDesc->pwszSessionName )
  501.     {
  502.         DXUtil_ConvertWideStringToGenericCch( strName, pResponseMsgAppDesc->pwszSessionName, MAX_PATH );
  503.     }
  504.     // Cleanup any old enum
  505.     if( pDPHostEnum->pAppDesc )
  506.     {
  507.         SAFE_DELETE_ARRAY( pDPHostEnum->pAppDesc->pwszSessionName );
  508.         SAFE_DELETE( pDPHostEnum->pAppDesc );
  509.     }
  510.     SAFE_RELEASE( pDPHostEnum->pHostAddr );
  511.     SAFE_RELEASE( pDPHostEnum->pDeviceAddr );
  512.     //
  513.     // Duplicate pEnumHostsResponseMsg->pAddressSender in pDPHostEnum->pHostAddr.
  514.     // Duplicate pEnumHostsResponseMsg->pAddressDevice in pDPHostEnum->pDeviceAddr.
  515.     //
  516.     if( FAILED( hr = pEnumHostsResponseMsg->pAddressSender->Duplicate( &pDPHostEnum->pHostAddr ) ) )
  517.     {
  518.         DXTRACE_ERR_MSGBOX( TEXT("Duplicate"), hr );
  519.         goto LCleanup;
  520.     }
  521.     if( FAILED( hr = pEnumHostsResponseMsg->pAddressDevice->Duplicate( &pDPHostEnum->pDeviceAddr ) ) )
  522.     {
  523.         DXTRACE_ERR_MSGBOX( TEXT("Duplicate"), hr );
  524.         goto LCleanup;
  525.     }
  526.     // Deep copy the DPN_APPLICATION_DESC from
  527.     pDPHostEnum->pAppDesc = new DPN_APPLICATION_DESC;
  528.     if( NULL == pDPHostEnum->pAppDesc )
  529.     {
  530.         hr = E_OUTOFMEMORY;
  531.         DXTRACE_ERR_MSGBOX( TEXT("SessionsDlgNoteEnumResponse"), hr );
  532.         goto LCleanup;
  533.     }
  534.     ZeroMemory( pDPHostEnum->pAppDesc, sizeof(DPN_APPLICATION_DESC) );
  535.     memcpy( pDPHostEnum->pAppDesc, pResponseMsgAppDesc, sizeof(DPN_APPLICATION_DESC) );
  536.     if( pResponseMsgAppDesc->pwszSessionName )
  537.     {
  538.         WCHAR* wstr = new WCHAR[ wcslen(pResponseMsgAppDesc->pwszSessionName)+1 ];
  539.         if( NULL == wstr )
  540.         {
  541.             hr = E_OUTOFMEMORY;
  542.             DXTRACE_ERR_MSGBOX( TEXT("SessionsDlgNoteEnumResponse"), hr );
  543.             goto LCleanup;
  544.         }
  545.         
  546.         wcscpy( wstr, pResponseMsgAppDesc->pwszSessionName );
  547.         pDPHostEnum->pAppDesc->pwszSessionName = wstr;
  548.     }
  549.     // Update the time this was done, so that we can expire this host
  550.     // if it doesn't refresh w/in a certain amount of time
  551.     pDPHostEnum->dwLastPollTime = GETTIMESTAMP();
  552.     // Check to see if the current number of players changed
  553.     TCHAR szSessionTemp[MAX_PATH];
  554.     if( pResponseMsgAppDesc->dwMaxPlayers > 0 )
  555.     {
  556.         _sntprintf( szSessionTemp, MAX_PATH-1, TEXT("%s (%d/%d) (%dms)"), strName,
  557.                     pResponseMsgAppDesc->dwCurrentPlayers - 1,  // ignore the host player
  558.                     pResponseMsgAppDesc->dwMaxPlayers - 1,      // ignore the host player
  559.                     pEnumHostsResponseMsg->dwRoundTripLatencyMS );
  560.         // Null terminate
  561.         szSessionTemp[ MAX_PATH-1 ] = 0;
  562.     }
  563.     else
  564.     {
  565.         _sntprintf( szSessionTemp, MAX_PATH-1, TEXT("%s (%d) (%dms)"), strName,
  566.                     pResponseMsgAppDesc->dwCurrentPlayers - 1,  // ignore the host player
  567.                     pEnumHostsResponseMsg->dwRoundTripLatencyMS );
  568.         // Null terminate
  569.         szSessionTemp[ MAX_PATH-1 ] = 0;
  570.     }
  571.     // if this node was previously invalidated, or the session name is now
  572.     // different the session list in the dialog needs to be updated
  573.     if( ( pDPHostEnum->bValid == FALSE ) ||
  574.         ( _tcscmp( pDPHostEnum->szSession, szSessionTemp ) != 0 ) )
  575.     {
  576.         m_bEnumListChanged = TRUE;
  577.     }
  578.     _tcscpy( pDPHostEnum->szSession, szSessionTemp );
  579.     // This host is now valid
  580.     pDPHostEnum->bValid = TRUE;
  581. LCleanup:
  582.     LeaveCriticalSection( &m_csHostEnum );
  583.     return hr;
  584. }
  585. //-----------------------------------------------------------------------------
  586. // Name: SessionsDlgExpireOldHostEnums
  587. // Desc: Check all nodes to see if any have expired yet.
  588. //-----------------------------------------------------------------------------
  589. VOID CNetClientWizard::SessionsDlgExpireOldHostEnums()
  590. {
  591.     DWORD dwCurrentTime = GETTIMESTAMP();
  592.     // This is called from the dialog UI thread, SessionsDlgNoteEnumResponse
  593.     // is called from the DirectPlay message handler threads so
  594.     // they may also be inside it at this time, so we need to go into the
  595.     // critical section first
  596.     EnterCriticalSection( &m_csHostEnum );
  597.     DPHostEnumInfo* pDPHostEnum = m_DPHostEnumHead.pNext;
  598.     while ( pDPHostEnum != &m_DPHostEnumHead )
  599.     {
  600.         // Check the poll time to expire stale entries.  Also check to see if
  601.         // the entry is already invalid.  If so, don't note that the enum list
  602.         // changed because that causes the list in the dialog to constantly redraw.
  603.         if( ( pDPHostEnum->bValid != FALSE ) &&
  604.             ( pDPHostEnum->dwLastPollTime < dwCurrentTime - m_dwEnumHostExpireInterval ) )
  605.         {
  606.             // This node has expired, so invalidate it.
  607.             pDPHostEnum->bValid = FALSE;
  608.             m_bEnumListChanged  = TRUE;
  609.         }
  610.         pDPHostEnum = pDPHostEnum->pNext;
  611.     }
  612.     LeaveCriticalSection( &m_csHostEnum );
  613. }
  614. //-----------------------------------------------------------------------------
  615. // Name: SessionsDlgDisplayEnumList
  616. // Desc: Display the list of hosts in the dialog box
  617. //-----------------------------------------------------------------------------
  618. HRESULT CNetClientWizard::SessionsDlgDisplayEnumList( HWND hDlg )
  619. {
  620.     HWND           hWndListBox   = GetDlgItem( hDlg, IDC_GAMES_LIST );
  621.     DPHostEnumInfo* pDPHostEnumSelected = NULL;
  622.     GUID           guidSelectedInstance;
  623.     BOOL           bFindSelectedGUID;
  624.     BOOL           bFoundSelectedGUID;
  625.     int            nItemSelected;
  626.     // This is called from the dialog UI thread, SessionsDlgNoteEnumResponse
  627.     // is called from the DirectPlay message handler threads so
  628.     // they may also be inside it at this time, so we need to go into the
  629.     // critical section first
  630.     EnterCriticalSection( &m_csHostEnum );
  631.     // Only update the display list if it has changed since last time
  632.     if( !m_bEnumListChanged )
  633.     {
  634.         LeaveCriticalSection( &m_csHostEnum );
  635.         return S_OK;
  636.     }
  637.     m_bEnumListChanged = FALSE;
  638.     bFindSelectedGUID  = FALSE;
  639.     bFoundSelectedGUID = FALSE;
  640.     // Try to keep the same session selected unless it goes away or
  641.     // there is no real session currently selected
  642.     nItemSelected = (int)SendMessage( hWndListBox, LB_GETCURSEL, 0, 0 );
  643.     if( nItemSelected != LB_ERR )
  644.     {
  645.         pDPHostEnumSelected = (DPHostEnumInfo*) SendMessage( hWndListBox, LB_GETITEMDATA,
  646.                                                              nItemSelected, 0 );
  647.         if( pDPHostEnumSelected != NULL && pDPHostEnumSelected->bValid )
  648.         {
  649.             guidSelectedInstance = pDPHostEnumSelected->pAppDesc->guidInstance;
  650.             bFindSelectedGUID = TRUE;
  651.         }
  652.     }
  653.     // Tell listbox not to redraw itself since the contents are going to change
  654.     SendMessage( hWndListBox, WM_SETREDRAW, FALSE, 0 );
  655.     // Test to see if any sessions exist in the linked list
  656.     DPHostEnumInfo* pDPHostEnum = m_DPHostEnumHead.pNext;
  657.     while ( pDPHostEnum != &m_DPHostEnumHead )
  658.     {
  659.         if( pDPHostEnum->bValid )
  660.             break;
  661.         pDPHostEnum = pDPHostEnum->pNext;
  662.     }
  663.     // If there are any sessions in list,
  664.     // then add them to the listbox
  665.     if( pDPHostEnum != &m_DPHostEnumHead )
  666.     {
  667.         // Clear the contents from the list box and enable the join button
  668.         SendMessage( hWndListBox, LB_RESETCONTENT, 0, 0 );
  669.         // Enable the join button only if not already connecting to a game
  670.         if( !m_bConnecting )        
  671.             EnableWindow( GetDlgItem( hDlg, IDC_JOIN ), TRUE );
  672.         pDPHostEnum = m_DPHostEnumHead.pNext;
  673.         while ( pDPHostEnum != &m_DPHostEnumHead )
  674.         {
  675.             // Add host to list box if it is valid
  676.             if( pDPHostEnum->bValid )
  677.             {
  678.                 int nIndex = (int)SendMessage( hWndListBox, LB_ADDSTRING, 0,
  679.                                                (LPARAM)pDPHostEnum->szSession );
  680.                 SendMessage( hWndListBox, LB_SETITEMDATA, nIndex, (LPARAM)pDPHostEnum );
  681.                 if( bFindSelectedGUID )
  682.                 {
  683.                     // Look for the session the was selected before
  684.                     if( pDPHostEnum->pAppDesc->guidInstance == guidSelectedInstance )
  685.                     {
  686.                         SendMessage( hWndListBox, LB_SETCURSEL, nIndex, 0 );
  687.                         bFoundSelectedGUID = TRUE;
  688.                     }
  689.                 }
  690.             }
  691.             pDPHostEnum = pDPHostEnum->pNext;
  692.         }
  693.         if( !bFindSelectedGUID || !bFoundSelectedGUID )
  694.             SendMessage( hWndListBox, LB_SETCURSEL, 0, 0 );
  695.     }
  696.     else
  697.     {
  698.         // There are no active session, so just reset the listbox
  699.         SessionsDlgInitListbox( hDlg );
  700.     }
  701.     // Tell listbox to redraw itself now since the contents have changed
  702.     SendMessage( hWndListBox, WM_SETREDRAW, TRUE, 0 );
  703.     InvalidateRect( hWndListBox, NULL, FALSE );
  704.     LeaveCriticalSection( &m_csHostEnum );
  705.     return S_OK;
  706. }
  707. //-----------------------------------------------------------------------------
  708. // Name: SessionsDlgJoinGame()
  709. // Desc: Joins the selected DirectPlay session
  710. //-----------------------------------------------------------------------------
  711. HRESULT CNetClientWizard::SessionsDlgJoinGame( HWND hDlg )
  712. {
  713.     HRESULT         hr;
  714.     HWND            hWndListBox = GetDlgItem( hDlg, IDC_GAMES_LIST );
  715.     DPHostEnumInfo* pDPHostEnumSelected = NULL;
  716.     int             nItemSelected;
  717.     // Add status text in list box
  718.     nItemSelected = (int)SendMessage( hWndListBox, LB_GETCURSEL, 0, 0 );
  719.     EnterCriticalSection( &m_csHostEnum );
  720.     pDPHostEnumSelected = (DPHostEnumInfo*) SendMessage( hWndListBox, LB_GETITEMDATA,
  721.                                                          nItemSelected, 0 );
  722.     if( NULL == pDPHostEnumSelected )
  723.     {
  724.         MessageBox( hDlg, TEXT("There are no games to join."),
  725.                     TEXT("DirectPlay Sample"), MB_OK );
  726.         hr = S_OK;
  727.         goto LCleanReturn;
  728.     }
  729.     m_bConnecting = TRUE;
  730.     // Set the peer info
  731.     WCHAR wszPeerName[MAX_PLAYER_NAME];
  732.     GetDlgItemText( hDlg, IDC_PLAYER_NAME_EDIT, m_strLocalPlayerName, MAX_PLAYER_NAME );
  733.     DXUtil_ConvertGenericStringToWideCch( wszPeerName, m_strLocalPlayerName, MAX_PLAYER_NAME );
  734.     DPN_PLAYER_INFO dpPlayerInfo;
  735.     ZeroMemory( &dpPlayerInfo, sizeof(DPN_PLAYER_INFO) );
  736.     dpPlayerInfo.dwSize = sizeof(DPN_PLAYER_INFO);
  737.     dpPlayerInfo.dwInfoFlags = DPNINFO_NAME;
  738.     dpPlayerInfo.pwszName = wszPeerName;
  739.     // Set the peer info, and use the DPNOP_SYNC since by default this
  740.     // is an async call.  If it is not DPNOP_SYNC, then the peer info may not
  741.     // be set by the time we call Connect() below.
  742.     if( FAILED( hr = m_pDPClient->SetClientInfo( &dpPlayerInfo, NULL, NULL, DPNOP_SYNC ) ) )
  743.     {
  744.         DXTRACE_ERR_MSGBOX( TEXT("SetClientInfo"), hr );
  745.         goto LCleanReturn;
  746.     }
  747.     ResetEvent( m_hConnectCompleteEvent );
  748.     // Connect to an existing session. DPNCONNECT_OKTOQUERYFORADDRESSING allows
  749.     // DirectPlay to prompt the user using a dialog box for any device address
  750.     // or host address information that is missing
  751.     // We also pass in copies of the app desc and host addr, since pDPHostEnumSelected
  752.     // might be deleted from another thread that calls SessionsDlgExpireOldHostEnums().
  753.     // This process could also be done using reference counting instead.
  754.     hr = m_pDPClient->Connect( pDPHostEnumSelected->pAppDesc,       // the application desc
  755.                                pDPHostEnumSelected->pHostAddr,      // address of the host of the session
  756.                                pDPHostEnumSelected->pDeviceAddr,    // address of the local device the enum responses were received on
  757.                                NULL, NULL,                          // DPN_SECURITY_DESC, DPN_SECURITY_CREDENTIALS
  758.                                NULL, 0,                             // user data, user data size
  759.                                NULL, &m_hConnectAsyncOp,            // async context, async handle,
  760.                                DPNCONNECT_OKTOQUERYFORADDRESSING ); // flags
  761.     if( FAILED(hr) && hr != E_PENDING )
  762.     {
  763.         DXTRACE_ERR_MSGBOX( TEXT("Connect"), hr );
  764.         goto LCleanReturn;
  765.     }
  766.     
  767.     // Set a timer to wait for m_hConnectCompleteEvent to be signaled.
  768.     // This will tell us when DPN_MSGID_CONNECT_COMPLETE has been processed
  769.     // which lets us know if the connect was successful or not.
  770.     SetTimer( hDlg, TIMERID_CONNECT_COMPLETE, 100, NULL );
  771.     // Disable the join button until connect succeeds or fails
  772.     EnableWindow( GetDlgItem( hDlg, IDC_JOIN ), FALSE );
  773.     hr = S_OK;
  774. LCleanReturn:
  775.     LeaveCriticalSection( &m_csHostEnum );
  776.     return hr;
  777. }
  778. //-----------------------------------------------------------------------------
  779. // Name: SessionsDlgEnumListCleanup()
  780. // Desc: Deletes the linked list, g_DPHostEnumInfoHead
  781. //-----------------------------------------------------------------------------
  782. VOID CNetClientWizard::SessionsDlgEnumListCleanup()
  783. {
  784.     DPHostEnumInfo* pDPHostEnum = m_DPHostEnumHead.pNext;
  785.     DPHostEnumInfo* pDPHostEnumDelete;
  786.     while ( pDPHostEnum != &m_DPHostEnumHead )
  787.     {
  788.         pDPHostEnumDelete = pDPHostEnum;
  789.         pDPHostEnum = pDPHostEnum->pNext;
  790.         if( pDPHostEnumDelete->pAppDesc )
  791.         {
  792.             SAFE_DELETE_ARRAY( pDPHostEnumDelete->pAppDesc->pwszSessionName );
  793.             SAFE_DELETE( pDPHostEnumDelete->pAppDesc );
  794.         }
  795.         // Changed from array delete to Release
  796.         SAFE_RELEASE( pDPHostEnumDelete->pHostAddr );
  797.         SAFE_RELEASE( pDPHostEnumDelete->pDeviceAddr );
  798.         SAFE_DELETE( pDPHostEnumDelete );
  799.     }
  800.     // Re-link the g_DPHostEnumInfoHead circular linked list
  801.     m_DPHostEnumHead.pNext = &m_DPHostEnumHead;
  802. }
  803. //-----------------------------------------------------------------------------
  804. // Name: MessageHandler
  805. // Desc: Handler for DirectPlay messages.  This function is called by
  806. //       the DirectPlay message handler pool of threads, so be careful of thread
  807. //       synchronization problems with shared memory
  808. //-----------------------------------------------------------------------------
  809. HRESULT WINAPI CNetClientWizard::MessageHandler( PVOID pvUserContext,
  810.                                                   DWORD dwMessageId,
  811.                                                   PVOID pMsgBuffer )
  812. {
  813.     // Try not to stay in this message handler for too long, otherwise
  814.     // there will be a backlog of data.  The best solution is to
  815.     // queue data as it comes in, and then handle it on other threads.
  816.     // This function is called by the DirectPlay message handler pool of
  817.     // threads, so be careful of thread synchronization problems with shared memory
  818.     switch(dwMessageId)
  819.     {
  820.         case DPN_MSGID_ENUM_HOSTS_RESPONSE:
  821.         {
  822.             PDPNMSG_ENUM_HOSTS_RESPONSE pEnumHostsResponseMsg;
  823.             pEnumHostsResponseMsg = (PDPNMSG_ENUM_HOSTS_RESPONSE)pMsgBuffer;
  824.             // Take note of the host response
  825.             SessionsDlgNoteEnumResponse( pEnumHostsResponseMsg );
  826.             break;
  827.         }
  828.         case DPN_MSGID_ASYNC_OP_COMPLETE:
  829.         {
  830.             PDPNMSG_ASYNC_OP_COMPLETE pAsyncOpCompleteMsg;
  831.             pAsyncOpCompleteMsg = (PDPNMSG_ASYNC_OP_COMPLETE)pMsgBuffer;
  832.             if( pAsyncOpCompleteMsg->hAsyncOp == m_hEnumAsyncOp )
  833.             {
  834.                 SessionsDlgEnumListCleanup();
  835.                 // The user canceled the DirectPlay connection dialog,
  836.                 // so stop the search
  837.                 if( m_bSearchingForSessions )
  838.                 {
  839.                     CheckDlgButton( m_hDlg, IDC_SEARCH_CHECK, BST_UNCHECKED );
  840.                     SendMessage( m_hDlg, WM_COMMAND, IDC_SEARCH_CHECK, 0 );
  841.                 }
  842.                 m_hEnumAsyncOp = NULL;
  843.                 m_bSearchingForSessions = FALSE;
  844.             }
  845.             break;
  846.         }
  847.         case DPN_MSGID_CONNECT_COMPLETE:
  848.         {
  849.             PDPNMSG_CONNECT_COMPLETE pConnectCompleteMsg;
  850.             pConnectCompleteMsg = (PDPNMSG_CONNECT_COMPLETE)pMsgBuffer;
  851.             // Set m_hrConnectComplete, then set an event letting
  852.             // everyone know that the DPN_MSGID_CONNECT_COMPLETE msg
  853.             // has been handled
  854.             m_hrConnectComplete = pConnectCompleteMsg->hResultCode;
  855.             SetEvent( m_hConnectCompleteEvent );
  856.             break;
  857.         }
  858.     }
  859.     return S_OK;
  860. }
  861. //-----------------------------------------------------------------------------
  862. // Name: ConnectUsingLobbySettings
  863. // Desc: Call this after the DPL_MSGID_CONNECT has been processed to carry out
  864. //       the connection settings received by the lobby client.  DPL_MSGID_CONNECT
  865. //       will have already been processed if we were lobby launched, or after
  866. //       WaitForConnection returns without timing out.
  867. //-----------------------------------------------------------------------------
  868. HRESULT CNetClientWizard::ConnectUsingLobbySettings()
  869. {
  870.     HRESULT hr;
  871.     DPNHANDLE hAsync;
  872.     if( m_hLobbyClient == NULL )
  873.         return E_INVALIDARG;
  874.     DPL_CONNECTION_SETTINGS* pSettings = NULL;
  875.     DWORD dwSettingsSize = 0;
  876.     // Get the connection settings from the lobby.
  877.     hr = m_pLobbiedApp->GetConnectionSettings( m_hLobbyClient, pSettings, &dwSettingsSize, 0 );
  878.     if( hr != DPNERR_BUFFERTOOSMALL )
  879.     {
  880.         DXTRACE_ERR_MSGBOX( TEXT("GetConnectionSettings"), hr );
  881.         goto LCleanReturn;
  882.     }
  883.     pSettings = (DPL_CONNECTION_SETTINGS*) new BYTE[dwSettingsSize];
  884.     if( NULL == pSettings )
  885.     {
  886.         hr = E_OUTOFMEMORY;
  887.         DXTRACE_ERR_MSGBOX( TEXT("ConnectUsingLobbySettings"), hr );
  888.         goto LCleanReturn;
  889.     }
  890.     if( FAILED( hr = m_pLobbiedApp->GetConnectionSettings( m_hLobbyClient, pSettings, &dwSettingsSize, 0 ) ) )
  891.     {
  892.         DXTRACE_ERR_MSGBOX( TEXT("GetConnectionSettings"), hr );
  893.         goto LCleanReturn;
  894.     }
  895.     // Set the peer info
  896.     WCHAR wszPeerName[MAX_PLAYER_NAME];
  897.     DXUtil_ConvertGenericStringToWideCch( wszPeerName, m_strLocalPlayerName, MAX_PLAYER_NAME );
  898.     DPN_PLAYER_INFO dpPlayerInfo;
  899.     ZeroMemory( &dpPlayerInfo, sizeof(DPN_PLAYER_INFO) );
  900.     dpPlayerInfo.dwSize = sizeof(DPN_PLAYER_INFO);
  901.     dpPlayerInfo.dwInfoFlags = DPNINFO_NAME;
  902.     dpPlayerInfo.pwszName = wszPeerName;
  903.     // Set the peer info, and use the DPNOP_SYNC since by default this
  904.     // is an async call.  If it is not DPNOP_SYNC, then the peer info may not
  905.     // be set by the time we call Connect() below.
  906.     if( FAILED( hr = m_pDPClient->SetClientInfo( &dpPlayerInfo, NULL, NULL, DPNOP_SYNC ) ) )
  907.     {
  908.         DXTRACE_ERR_MSGBOX( TEXT("SetClientInfo"), hr );
  909.         goto LCleanReturn;
  910.     }
  911.     // Connect to an existing session. There should only be on device address in
  912.     // the connection settings structure when connecting to a session, so just
  913.     // pass in the first one.
  914.     // The enumeration is automatically cancelled after Connect is called 
  915.     hr = m_pDPClient->Connect( &pSettings->dpnAppDesc,              // the application desc
  916.                                pSettings->pdp8HostAddress,          // address of the host of the session
  917.                                pSettings->ppdp8DeviceAddresses[0],  // address of the local device used to connect to the host
  918.                                NULL, NULL,                          // DPN_SECURITY_DESC, DPN_SECURITY_CREDENTIALS
  919.                                NULL, 0,                             // user data, user data size
  920.                                NULL, &hAsync,                       // async context, async handle,
  921.                                0 );                                 // flags
  922.     if( hr != E_PENDING && FAILED(hr) )
  923.     {
  924.         DXTRACE_ERR_MSGBOX( TEXT("Connect"), hr );
  925.         goto LCleanReturn;
  926.     }
  927.     hr = S_OK; // Accept E_PENDING.
  928.     // Wait until the MessageHandler sets an event to tell us the
  929.     // DPN_MSGID_CONNECT_COMPLETE has been processed.  Then m_hrConnectComplete
  930.     // will be valid.
  931.     WaitForSingleObject( m_hConnectCompleteEvent, INFINITE );
  932.     if( FAILED( m_hrConnectComplete ) )
  933.     {
  934.         DXTRACE_ERR_MSGBOX( TEXT("DPN_MSGID_CONNECT_COMPLETE"), m_hrConnectComplete );
  935.         MessageBox( m_hDlg, TEXT("Unable to join game."),
  936.                     TEXT("DirectPlay Sample"),
  937.                     MB_OK | MB_ICONERROR );
  938.         hr = m_hrConnectComplete;
  939.     }
  940.     // Cleanup the addresses and memory obtained from GetConnectionSettings
  941. LCleanReturn:
  942.     if( pSettings )
  943.     {
  944.         SAFE_RELEASE( pSettings->pdp8HostAddress );
  945.         for( DWORD dwIndex=0; dwIndex < pSettings->cNumDeviceAddresses; dwIndex++ )
  946.             SAFE_RELEASE( pSettings->ppdp8DeviceAddresses[dwIndex] );
  947.         
  948.         SAFE_DELETE_ARRAY( pSettings );
  949.     }
  950.     return hr;
  951. }
  952. //-----------------------------------------------------------------------------
  953. // Name: LobbyMessageHandler
  954. // Desc: Handler for DirectPlay messages.  This function is called by
  955. //       the DirectPlay lobby message handler pool of threads, so be careful of thread
  956. //       synchronization problems with shared memory
  957. //-----------------------------------------------------------------------------
  958. HRESULT WINAPI CNetClientWizard::LobbyMessageHandler( PVOID pvUserContext,
  959.                                                        DWORD dwMessageId,
  960.                                                        PVOID pMsgBuffer )
  961. {
  962.     HRESULT hr = S_OK;
  963.     switch(dwMessageId)
  964.     {
  965.         case DPL_MSGID_CONNECT:
  966.         {
  967.             // This message will be processed when a lobby connection has been
  968.             // established. If you were lobby launched then
  969.             // IDirectPlay8LobbiedApplication::Initialize()
  970.             // waits until this message has been processed before returning, so
  971.             // take care not to deadlock by making calls that need to be handled by
  972.             // the thread who called Initialize().  The same is true for WaitForConnection()
  973.             PDPL_MESSAGE_CONNECT pConnectMsg;
  974.             pConnectMsg = (PDPL_MESSAGE_CONNECT)pMsgBuffer;
  975.             PDPL_CONNECTION_SETTINGS pSettings = pConnectMsg->pdplConnectionSettings;
  976.             m_hLobbyClient = pConnectMsg->hConnectId;
  977.             if( FAILED( hr = m_pDPClient->RegisterLobby( m_hLobbyClient, m_pLobbiedApp,
  978.                                                    DPNLOBBY_REGISTER ) ) )
  979.                 return DXTRACE_ERR_MSGBOX( TEXT("RegisterLobby"), hr );
  980.             if( pSettings == NULL )
  981.             {
  982.                 // There aren't connection settings from the lobby
  983.                 m_bHaveConnectionSettingsFromLobby = FALSE;
  984.             }
  985.             else
  986.             {
  987.                 // Record the player name if found
  988.                 if( pSettings->pwszPlayerName != NULL )
  989.                 {
  990.                     TCHAR strPlayerName[MAX_PLAYER_NAME];
  991.                     DXUtil_ConvertWideStringToGenericCch( strPlayerName, pSettings->pwszPlayerName, MAX_PLAYER_NAME );
  992.                     _tcsncpy( m_strLocalPlayerName, strPlayerName, MAX_PLAYER_NAME );
  993.                     m_strLocalPlayerName[MAX_PLAYER_NAME-1] = 0;
  994.                 }
  995.                 else
  996.                 {
  997.                     _tcsncpy( m_strLocalPlayerName, TEXT("Unknown"), MAX_PLAYER_NAME );
  998.                     m_strLocalPlayerName[MAX_PLAYER_NAME-1] = 0;
  999.                 }
  1000.                 m_bHaveConnectionSettingsFromLobby = TRUE;
  1001.             }
  1002.             // Tell everyone we have a lobby connection now
  1003.             SetEvent( m_hLobbyConnectionEvent );
  1004.             break;
  1005.         }
  1006.     }
  1007.     return S_OK;
  1008. }
  1009. //-----------------------------------------------------------------------------
  1010. // Name: StaticLobbyWaitDlgProc()
  1011. // Desc: Static msg handler which passes messages
  1012. //-----------------------------------------------------------------------------
  1013. INT_PTR CALLBACK CNetClientWizard::StaticLobbyWaitDlgProc( HWND hDlg, UINT uMsg,
  1014.                                                                 WPARAM wParam, LPARAM lParam )
  1015. {
  1016.     if( g_pNCW )
  1017.         return g_pNCW->LobbyWaitDlgProc( hDlg, uMsg, wParam, lParam );
  1018.     return FALSE; // Message not handled
  1019. }
  1020. //-----------------------------------------------------------------------------
  1021. // Name: LobbyWaitDlgProc()
  1022. // Desc: Handles messages for the lobby wait status dialog
  1023. //-----------------------------------------------------------------------------
  1024. INT_PTR CALLBACK CNetClientWizard::LobbyWaitDlgProc( HWND hDlg, UINT msg,
  1025.                                                       WPARAM wParam, LPARAM lParam )
  1026. {
  1027.     switch( msg )
  1028.     {
  1029.         case WM_INITDIALOG:
  1030. #if defined(WIN32_PLATFORM_PSPC) && (_WIN32_WCE >= 300)
  1031. SHINITDLGINFO shidi;
  1032. memset(&shidi, 0, sizeof(SHINITDLGINFO));
  1033. shidi.dwMask = SHIDIM_FLAGS;
  1034. shidi.dwFlags = SHIDIF_SIPDOWN | SHIDIF_SIZEDLGFULLSCREEN;
  1035. shidi.hDlg = hDlg;
  1036.          SetForegroundWindow(hDlg);
  1037.          SHInitDialog(&shidi);
  1038. #endif // WIN32_PLATFORM_PSPC
  1039. // Set a timer to wait for m_hConnectCompleteEvent to be signaled.
  1040.             // This will tell us when DPN_MSGID_CONNECT_COMPLETE has been processed
  1041.             // which lets us know if the connect was successful or not.
  1042.             SetTimer( hDlg, TIMERID_CONNECT_COMPLETE, 100, NULL );
  1043.             SetDlgItemText( hDlg, IDC_WAIT_TEXT, TEXT("Waiting for lobby connection...") );
  1044.             return TRUE;
  1045.         case WM_COMMAND:
  1046.             switch( LOWORD(wParam) )
  1047.             {
  1048.                 case IDCANCEL:
  1049.                     EndDialog( hDlg, IDCANCEL );
  1050.                     return TRUE;
  1051.             }
  1052.             break;
  1053.         case WM_TIMER:
  1054.         {
  1055.             if( wParam == TIMERID_CONNECT_COMPLETE )
  1056.             {
  1057.                 // Wait for a lobby connection.  If this call
  1058.                 // returns WAIT_OBJECT_0 then the DPL_MSGID_CONNECT will
  1059.                 // have already been processed.
  1060.                 DWORD dwResult = WaitForSingleObject( m_hLobbyConnectionEvent, 100 );
  1061.                 if( dwResult != WAIT_TIMEOUT )
  1062.                     EndDialog( hDlg, IDOK );
  1063.             }
  1064.             break;
  1065.         }
  1066.     }
  1067.     return FALSE; // Didn't handle message
  1068. }