home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / Common / src / netclient.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-31  |  45.3 KB  |  1,200 lines

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