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

游戏

开发平台:

Visual C++

  1. //-----------------------------------------------------------------------------
  2. // File: NetConnect.cpp
  3. //
  4. // Desc: This is a class that given a IDirectPlay8Peer, then DoConnectWizard()
  5. //       will enumerate service providers, enumerate hosts, and allow the
  6. //       user to either join or host a session.  The class uses
  7. //       dialog boxes and GDI for the interactive UI.  Most games will
  8. //       want to change the graphics to use Direct3D or another graphics
  9. //       layer, but this simplistic sample uses dialog boxes.  Feel 
  10. //       free to use this class as a starting point for adding extra 
  11. //       functionality.
  12. //
  13. // Copyright (c) Microsoft Corporation. All rights reserved.
  14. //-----------------------------------------------------------------------------
  15. #ifndef STRICT
  16. #define STRICT
  17. #endif // !STRICT
  18. #include <windows.h>
  19. #include <basetsd.h>
  20. #include <stdio.h>
  21. #include <mmsystem.h>
  22. #include <dxerr9.h>
  23. #include <dplay8.h>
  24. #include <dpaddr.h>
  25. #include <dplobby8.h>
  26. #include "NetConnect.h"
  27. #include "NetConnectRes.h"
  28. #include "DXUtil.h"
  29. #if defined(WIN32_PLATFORM_PSPC) && (_WIN32_WCE >= 300)
  30. #include <aygshell.h>
  31. #endif // PocketPC
  32. //-----------------------------------------------------------------------------
  33. // Global variables
  34. //-----------------------------------------------------------------------------
  35. CNetConnectWizard* g_pNCW = NULL;           // Pointer to the net connect wizard
  36. //-----------------------------------------------------------------------------
  37. // Name: CNetConnectWizard
  38. // Desc: Init the class
  39. //-----------------------------------------------------------------------------
  40. CNetConnectWizard::CNetConnectWizard( HINSTANCE hInst, HWND hWndParent, 
  41.                                       LPCTSTR strAppName, GUID* pGuidApp )
  42. {
  43.     g_pNCW              = this;
  44.     m_hInst             = hInst;
  45.     m_hWndParent        = hWndParent;
  46.     m_pDP               = NULL;
  47.     m_pLobbiedApp       = NULL;
  48.     m_bHaveConnectionSettingsFromLobby = FALSE;
  49.     m_hLobbyClient      = NULL;
  50.     m_guidApp           = *pGuidApp;
  51.     m_hDlg              = NULL;
  52.     m_bConnecting       = FALSE;
  53.     m_hConnectAsyncOp   = NULL;
  54.     m_hEnumAsyncOp      = NULL;
  55.     m_bMigrateHost      = FALSE;
  56.     m_bUseDPNSVR        = FALSE;
  57.     m_dwPort            = 0;
  58.     m_eSigningType      = SIGN_NONE;
  59.     m_dwEnumHostExpireInterval = 0;
  60.     ZeroMemory(&m_guidSP, sizeof(GUID));
  61.     // Set the max players unlimited by default.  This can be changed by the app
  62.     // by calling SetMaxPlayers()
  63.     m_dwMaxPlayers   = 0;
  64.     _tcsncpy( m_strAppName, strAppName, MAX_PATH-1 );
  65.     m_strAppName[ MAX_PATH-1 ] = 0; 
  66.     
  67.     _tcsncpy( m_strPreferredProvider, TEXT("DirectPlay8 TCP/IP Service Provider"), MAX_PATH-1 );
  68.     m_strPreferredProvider[ MAX_PATH-1 ] = 0; 
  69.     _tcsncpy( m_strHostname, TEXT(""), MAX_PATH-1 );
  70.     InitializeCriticalSection( &m_csHostEnum );
  71.     m_hConnectCompleteEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  72.     m_hLobbyConnectionEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  73.     // Setup the m_DPHostEnumHead circular linked list
  74.     ZeroMemory( &m_DPHostEnumHead, sizeof( DPHostEnumInfo ) );
  75.     m_DPHostEnumHead.pNext = &m_DPHostEnumHead;
  76. }
  77. //-----------------------------------------------------------------------------
  78. // Name: ~CNetConnectWizard
  79. // Desc: Cleanup the class
  80. //-----------------------------------------------------------------------------
  81. CNetConnectWizard::~CNetConnectWizard()
  82. {
  83.     DeleteCriticalSection( &m_csHostEnum );
  84.     CloseHandle( m_hConnectCompleteEvent );
  85.     CloseHandle( m_hLobbyConnectionEvent );
  86. }
  87. //-----------------------------------------------------------------------------
  88. // Name: Init
  89. // Desc:
  90. //-----------------------------------------------------------------------------
  91. HRESULT CNetConnectWizard::Init( IDirectPlay8Peer* pDP,
  92.                                  IDirectPlay8LobbiedApplication* pLobbiedApp )
  93. {
  94.     if( NULL == pDP || NULL == pLobbiedApp )
  95.         return E_INVALIDARG;
  96.     m_pDP               = pDP;
  97.     m_pLobbiedApp       = pLobbiedApp;
  98.     m_bHaveConnectionSettingsFromLobby = FALSE;
  99.     m_hLobbyClient      = NULL;
  100.     return S_OK;
  101. }
  102. //-----------------------------------------------------------------------------
  103. // Name: Shutdown
  104. // Desc: Releases the DirectPlay interfaces
  105. //-----------------------------------------------------------------------------
  106. VOID CNetConnectWizard::Shutdown()
  107. {
  108. }
  109. //-----------------------------------------------------------------------------
  110. // Name: DoConnectWizard
  111. // Desc: This is the main external function.  This will launch a series of
  112. //       dialog boxes that enumerate service providers, enumerate hosts,
  113. //       and allow the user to either join or host a session
  114. //-----------------------------------------------------------------------------
  115. HRESULT CNetConnectWizard::DoConnectWizard( BOOL bBackTrack )
  116. {
  117.     if( m_pDP == NULL )
  118.         return E_INVALIDARG;
  119.     int nStep;
  120.     // If the back track flag is true, then the user has already been through
  121.     // the connect process once, and has back tracked out of the main game
  122.     // so start at the last dialog box
  123.     if( bBackTrack )
  124.         nStep = 1;
  125.     else
  126.         nStep = 0;
  127.     // Show the dialog boxes to connect
  128.     for( ;; )
  129.     {
  130.         m_hrDialog = S_OK;
  131.         switch( nStep )
  132.         {
  133.             case 0:
  134.                 // Display the multiplayer connect dialog box.
  135.                 DialogBox( m_hInst, MAKEINTRESOURCE(IDD_MULTIPLAYER_CONNECT),
  136.                            m_hWndParent, (DLGPROC) StaticConnectionsDlgProc );
  137.                 break;
  138.             case 1:
  139.                 // Display the multiplayer games dialog box.
  140.                 DialogBox( m_hInst, MAKEINTRESOURCE(IDD_MULTIPLAYER_GAMES),
  141.                            m_hWndParent, (DLGPROC) StaticSessionsDlgProc );
  142.                 break;
  143.         }
  144.         if( FAILED( m_hrDialog ) ||
  145.             m_hrDialog == NCW_S_QUIT ||
  146.             m_hrDialog == NCW_S_LOBBYCONNECT )
  147.             break;
  148.         if( m_hrDialog == NCW_S_BACKUP )
  149.             nStep--;
  150.         else
  151.             nStep++;
  152.         // If we go beyond the last step in the wizard, then stop
  153.         // and return.
  154.         if( nStep == 2 )
  155.             break;
  156.     }
  157.     // Depending upon a successful m_hrDialog the user has
  158.     // either successfully join or created a game, depending on m_bHostPlayer
  159.     m_pDP = NULL;
  160.     return m_hrDialog;
  161. }
  162. //-----------------------------------------------------------------------------
  163. // Name: StaticConnectionsDlgProc()
  164. // Desc: Static msg handler which passes messages
  165. //-----------------------------------------------------------------------------
  166. INT_PTR CALLBACK CNetConnectWizard::StaticConnectionsDlgProc( HWND hDlg, UINT uMsg,
  167.                                                               WPARAM wParam, LPARAM lParam )
  168. {
  169.     if( g_pNCW )
  170.         return g_pNCW->ConnectionsDlgProc( hDlg, uMsg, wParam, lParam );
  171.     return FALSE; // Message not handled
  172. }
  173. //-----------------------------------------------------------------------------
  174. // Name: ConnectionsDlgProc()
  175. // Desc: Handles messages for the multiplayer connect dialog
  176. //-----------------------------------------------------------------------------
  177. INT_PTR CALLBACK CNetConnectWizard::ConnectionsDlgProc( HWND hDlg, UINT msg,
  178.                                                         WPARAM wParam, LPARAM lParam )
  179. {
  180.     UNREFERENCED_PARAMETER( lParam );
  181.     
  182.     switch( msg )
  183.     {
  184.         case WM_INITDIALOG:
  185.             {
  186. #if defined(WIN32_PLATFORM_PSPC) && (_WIN32_WCE >= 300)
  187.                 SHINITDLGINFO   shidi;
  188.                 memset(&shidi, 0, sizeof(SHINITDLGINFO));
  189.                 shidi.dwMask = SHIDIM_FLAGS;
  190.                 shidi.dwFlags = SHIDIF_SIPDOWN | SHIDIF_SIZEDLGFULLSCREEN;
  191.                 shidi.hDlg = hDlg;
  192.                 SetForegroundWindow(hDlg);
  193.                 SHInitDialog(&shidi);
  194. #endif // WIN32_PLATFORM_PSPC
  195.                 SetDlgItemText( hDlg, IDC_PLAYER_NAME_EDIT, m_strLocalPlayerName );
  196.                 // Limit the player name size
  197.                 SendDlgItemMessage( hDlg, IDC_PLAYER_NAME_EDIT, EM_LIMITTEXT, MAX_PLAYER_NAME-1, 0 );
  198.                 // Load and set the icon
  199.                 HICON hIcon = LoadIcon( m_hInst, MAKEINTRESOURCE( IDI_MAIN ) );
  200.                 SendMessage( hDlg, WM_SETICON, ICON_BIG,   (LPARAM) hIcon );  // Set big icon
  201.                 SendMessage( hDlg, WM_SETICON, ICON_SMALL, (LPARAM) hIcon );  // Set small icon
  202.                 // Set the window title
  203.                 TCHAR strWindowTitle[256];
  204.                 wsprintf( strWindowTitle, TEXT("%s - Multiplayer Connect"), m_strAppName );
  205.                 SetWindowText( hDlg, strWindowTitle );
  206.                 // Fill the list box with the service providers
  207.                 if( FAILED( m_hrDialog = ConnectionsDlgFillListBox( hDlg ) ) )
  208.                 {
  209.                     DXTRACE_ERR_MSGBOX( TEXT("ConnectionsDlgFillListBox"), m_hrDialog );
  210.                     EndDialog( hDlg, 0 );
  211.                 }
  212.             }
  213.             break;
  214.         case WM_COMMAND:
  215.             switch( LOWORD(wParam) )
  216.             {
  217.                 case IDC_CONNECTION_LIST:
  218.                     if( HIWORD(wParam) != LBN_DBLCLK )
  219.                         break;
  220.                     // Fall through
  221.                 case IDOK:
  222.                     if( FAILED( m_hrDialog = ConnectionsDlgOnOK( hDlg ) ) )
  223.                     {
  224.                         DXTRACE_ERR_MSGBOX( TEXT("ConnectionsDlgOnOK"), m_hrDialog );
  225.                         EndDialog( hDlg, 0 );
  226.                     }
  227.                     if( m_hrDialog == NCW_S_LOBBYCONNECT )
  228.                     {
  229.                         EndDialog( hDlg, 0 );
  230.                     }
  231.                     break;
  232.                 case IDCANCEL:
  233.                     m_hrDialog = NCW_S_QUIT;
  234.                     EndDialog( hDlg, 0 );
  235.                     break;
  236.                 default:
  237.                     return FALSE; // Message not handled
  238.             }
  239.             break;
  240.         case WM_DESTROY:
  241.             ConnectionsDlgCleanup( hDlg );
  242.             break;
  243.         default:
  244.             return FALSE; // Message not handled
  245.     }
  246.     // Message was handled
  247.     return TRUE;
  248. }
  249. //-----------------------------------------------------------------------------
  250. // Name: ConnectionsDlgFillListBox()
  251. // Desc: Fills the DirectPlay connection listbox with service providers,
  252. //       and also adds a "Wait for Lobby" connection option.
  253. //-----------------------------------------------------------------------------
  254. HRESULT CNetConnectWizard::ConnectionsDlgFillListBox( HWND hDlg )
  255. {
  256.     HRESULT                     hr;
  257.     int                         iLBIndex;
  258.     DWORD                       dwItems     = 0;
  259.     DPN_SERVICE_PROVIDER_INFO*  pdnSPInfo   = NULL;
  260.     DWORD                       dwSize      = 0;
  261.     HWND                        hWndListBox = GetDlgItem( hDlg, IDC_CONNECTION_LIST );
  262.     TCHAR                       strName[MAX_PATH];
  263.     // Enumerate all DirectPlay service providers, and store them in the listbox
  264.     // Get required space for all providers
  265.     hr = m_pDP->EnumServiceProviders( NULL, NULL, pdnSPInfo, &dwSize,
  266.                                       &dwItems, 0 );
  267.     if( FAILED(hr) && hr != DPNERR_BUFFERTOOSMALL )
  268.     {
  269.         DXTRACE_ERR_MSGBOX( TEXT("EnumServiceProviders"), hr );
  270.         goto LCleanReturn;
  271.     }
  272.     // Allocate required space
  273.     pdnSPInfo = (DPN_SERVICE_PROVIDER_INFO*) new BYTE[dwSize];
  274.     if( NULL == pdnSPInfo )
  275.     {
  276.         hr = E_OUTOFMEMORY;
  277.         DXTRACE_ERR_MSGBOX( TEXT("ConnectionsDlgFillListBox"), hr );
  278.         goto LCleanReturn;
  279.     }
  280.     // Perform the enumeration
  281.     hr = m_pDP->EnumServiceProviders( NULL, NULL, pdnSPInfo,
  282.                                       &dwSize, &dwItems, 0 );
  283.     if( FAILED(hr) )
  284.     {
  285.         DXTRACE_ERR_MSGBOX( TEXT("EnumServiceProviders"), hr );
  286.         goto LCleanReturn;
  287.     }
  288.     // For each detected provider, add an item to the listbox
  289.     DPN_SERVICE_PROVIDER_INFO* pdnSPInfoEnum;
  290.     pdnSPInfoEnum = pdnSPInfo;
  291.     DWORD i;
  292.     for ( i = 0; i < dwItems; i++ )
  293.     {
  294.         DXUtil_ConvertWideStringToGenericCch( strName, pdnSPInfoEnum->pwszName, MAX_PATH );
  295.         // Found a service provider, so put it in the listbox
  296.         iLBIndex = (int)SendMessage( hWndListBox, LB_ADDSTRING, 0,
  297.                                      (LPARAM)strName );
  298.         if( iLBIndex == CB_ERR )
  299.         {
  300.             // Error, stop enumerating
  301.             hr = E_FAIL;
  302.             DXTRACE_ERR_MSGBOX( TEXT("ConnectionsDlgFillListBox"), hr );
  303.             goto LCleanReturn;
  304.         }
  305.         // Store pointer to GUID in listbox
  306.         GUID* pGuid = new GUID;
  307.         if( NULL == pGuid )
  308.         {
  309.             hr = E_OUTOFMEMORY;
  310.             DXTRACE_ERR_MSGBOX( TEXT("ConnectionsDlgFillListBox"), hr );
  311.             goto LCleanReturn;
  312.         }
  313.         memcpy( pGuid, &pdnSPInfoEnum->guid, sizeof(GUID) );
  314.         SendMessage( hWndListBox, LB_SETITEMDATA, iLBIndex,
  315.                      (LPARAM)pGuid );
  316.         // Advance to next provider
  317.         pdnSPInfoEnum++;
  318.     }
  319.     // Add "Wait for Lobby Connection" selection in list box
  320.     SendMessage( hWndListBox, LB_ADDSTRING, 0,
  321.                  (LPARAM) TEXT("Wait for Lobby Connection") );
  322.     SetFocus( hWndListBox );
  323.     // Try to select the default preferred provider
  324.     iLBIndex = (int)SendMessage( hWndListBox, LB_FINDSTRINGEXACT, (WPARAM)-1,
  325.                                 (LPARAM)m_strPreferredProvider );
  326.     if( iLBIndex != LB_ERR )
  327.         SendMessage( hWndListBox, LB_SETCURSEL, iLBIndex, 0 );
  328.     else
  329.         SendMessage( hWndListBox, LB_SETCURSEL, 0, 0 );
  330.     hr = S_OK;
  331. LCleanReturn:
  332.     SAFE_DELETE_ARRAY( pdnSPInfo );
  333.     return hr;
  334. }
  335. //-----------------------------------------------------------------------------
  336. // Name: ConnectionsDlgOnOK()
  337. // Desc: Stores the player name m_strPlayerName, and in creates a IDirectPlay
  338. //       object based on the connection type the user selected.
  339. //-----------------------------------------------------------------------------
  340. HRESULT CNetConnectWizard::ConnectionsDlgOnOK( HWND hDlg )
  341. {
  342.     LRESULT iIndex;
  343.     HRESULT hr;
  344.     GetDlgItemText( hDlg, IDC_PLAYER_NAME_EDIT, m_strLocalPlayerName, MAX_PLAYER_NAME );
  345.     if( _tcslen( m_strLocalPlayerName ) == 0 )
  346.     {
  347.         MessageBox( hDlg, TEXT("You must enter a valid player name."),
  348.                     TEXT("DirectPlay Sample"), MB_OK );
  349.         return S_OK;
  350.     }
  351.     HWND hWndListBox = GetDlgItem( hDlg, IDC_CONNECTION_LIST );
  352.     iIndex = SendMessage( hWndListBox, LB_GETCURSEL, 0, 0 );
  353.     SendMessage( hWndListBox, LB_GETTEXT, iIndex, (LPARAM)m_strPreferredProvider );
  354.     GUID* pGuid = (GUID*) SendMessage( hWndListBox, LB_GETITEMDATA, iIndex, 0 );
  355.     if( NULL == pGuid )
  356.     {
  357.         // 'Wait for lobby launch' SP has been selected, so wait for a connection
  358.         if( FAILED( hr = m_pLobbiedApp->SetAppAvailable( TRUE, 0 ) ) )
  359.             return DXTRACE_ERR_MSGBOX( TEXT("SetAppAvailable"), hr );
  360.         // Display the multiplayer connect dialog box.
  361.         DialogBox( m_hInst, MAKEINTRESOURCE(IDD_LOBBY_WAIT_STATUS),
  362.                    hDlg, (DLGPROC) StaticLobbyWaitDlgProc );
  363.         if( m_bHaveConnectionSettingsFromLobby )
  364.         {
  365.             if( FAILED( hr = ConnectUsingLobbySettings() ) )
  366.                 return DXTRACE_ERR_MSGBOX( TEXT("ConnectUsingLobbySettings"), hr );
  367.             return NCW_S_LOBBYCONNECT;
  368.         }
  369.         // 'Wait for lobby launch' was canceled, so don't wait for a connection anymore
  370.         if( FAILED( hr = m_pLobbiedApp->SetAppAvailable( FALSE, 0 ) ) )
  371.             return DXTRACE_ERR_MSGBOX( TEXT("SetAppAvailable"), hr );
  372.         return S_OK;
  373.     }
  374.     // Query for the enum host timeout for this SP
  375.     DPN_SP_CAPS dpspCaps;
  376.     ZeroMemory( &dpspCaps, sizeof(DPN_SP_CAPS) );
  377.     dpspCaps.dwSize = sizeof(DPN_SP_CAPS);
  378.     if( FAILED( hr = m_pDP->GetSPCaps( pGuid, &dpspCaps, 0 ) ) )
  379.         return DXTRACE_ERR_MSGBOX( TEXT("GetSPCaps"), hr );
  380.     // Set the host expire time to around 3 times
  381.     // length of the dwDefaultEnumRetryInterval
  382.     m_dwEnumHostExpireInterval = dpspCaps.dwDefaultEnumRetryInterval * 3;
  383.     m_guidSP = *pGuid;
  384.     // The SP has been chosen, so move forward in the wizard
  385.     m_hrDialog = NCW_S_FORWARD;
  386.     EndDialog( hDlg, 0 );
  387.     return S_OK;
  388. }
  389. //-----------------------------------------------------------------------------
  390. // Name: ConnectionsDlgCleanup()
  391. // Desc: Deletes the connection buffers from the listbox
  392. //-----------------------------------------------------------------------------
  393. VOID CNetConnectWizard::ConnectionsDlgCleanup( HWND hDlg )
  394. {
  395.     GUID*   pGuid = NULL;
  396.     DWORD   iIndex;
  397.     DWORD   dwCount;
  398.     HWND hWndListBox = GetDlgItem( hDlg, IDC_CONNECTION_LIST );
  399.     dwCount = (DWORD)SendMessage( hWndListBox, LB_GETCOUNT, 0, 0 );
  400.     for( iIndex = 0; iIndex < dwCount; iIndex++ )
  401.     {
  402.         pGuid = (GUID*) SendMessage( hWndListBox, LB_GETITEMDATA,
  403.                                      iIndex, 0 );
  404.         SAFE_DELETE( pGuid );
  405.     }
  406. }
  407. //-----------------------------------------------------------------------------
  408. // Name: StaticSessionsDlgProc()
  409. // Desc: Static msg handler which passes messages
  410. //-----------------------------------------------------------------------------
  411. INT_PTR CALLBACK CNetConnectWizard::StaticSessionsDlgProc( HWND hDlg, UINT uMsg,
  412.                                                            WPARAM wParam, LPARAM lParam )
  413. {
  414.     if( g_pNCW )
  415.         return g_pNCW->SessionsDlgProc( hDlg, uMsg, wParam, lParam );
  416.     return FALSE; // Message not handled
  417. }
  418. //-----------------------------------------------------------------------------
  419. // Name: SessionsDlgProc()
  420. // Desc: Handles messages fro the multiplayer games dialog
  421. //-----------------------------------------------------------------------------
  422. INT_PTR CALLBACK CNetConnectWizard::SessionsDlgProc( HWND hDlg, UINT msg,
  423.                                                      WPARAM wParam, LPARAM lParam )
  424. {
  425.     UNREFERENCED_PARAMETER( lParam );
  426.     HRESULT hr;
  427.     switch( msg )
  428.     {
  429.         case WM_INITDIALOG:
  430.             {
  431. #if defined(WIN32_PLATFORM_PSPC) && (_WIN32_WCE >= 300)
  432.                 SHINITDLGINFO   shidi;
  433.                 memset(&shidi, 0, sizeof(SHINITDLGINFO));
  434.                 shidi.dwMask = SHIDIM_FLAGS;
  435.                 shidi.dwFlags = SHIDIF_SIPDOWN | SHIDIF_SIZEDLGFULLSCREEN;
  436.                 shidi.hDlg = hDlg;
  437.                 SetForegroundWindow(hDlg);
  438.                 SHInitDialog(&shidi);
  439. #endif // WIN32_PLATFORM_PSPC
  440.                 m_hDlg = hDlg;
  441.                 // Load and set the icon
  442.                 HICON hIcon = LoadIcon( m_hInst, MAKEINTRESOURCE( IDI_MAIN ) );
  443.                 SendMessage( hDlg, WM_SETICON, ICON_BIG,   (LPARAM) hIcon );  // Set big icon
  444.                 SendMessage( hDlg, WM_SETICON, ICON_SMALL, (LPARAM) hIcon );  // Set small icon
  445.                 // Set the window title
  446.                 TCHAR strWindowTitle[256];
  447.                 wsprintf( strWindowTitle, TEXT("%s - Multiplayer Games"), m_strAppName );
  448.                 SetWindowText( hDlg, strWindowTitle );
  449.                 // Init the search portion of the dialog
  450.                 m_bSearchingForSessions = FALSE;
  451.                 
  452.                 // Check to see if a former search is still waiting to end
  453.                 if(m_hEnumAsyncOp != NULL)
  454.                 {
  455.                     EnableWindow( GetDlgItem(hDlg, IDC_SEARCH_CHECK), FALSE );
  456.                     SetDlgItemText( hDlg, IDC_SEARCH_CHECK, TEXT("Stopping...") );
  457.                 }
  458.                 SessionsDlgInitListbox( hDlg );
  459.             }
  460.             break;
  461.         case WM_TIMER:
  462.             // Upon this timer message, then refresh the list of hosts
  463.             // by expiring old hosts, and displaying the list in the
  464.             // dialog box
  465.             if( wParam == TIMERID_DISPLAY_HOSTS )
  466.             {
  467.                 // Don't refresh if we are not enumerating hosts
  468.                 if( !m_bSearchingForSessions )
  469.                     break;
  470.                 // Expire all of the hosts that haven't
  471.                 // refreshed in a certain period of time
  472.                 SessionsDlgExpireOldHostEnums();
  473.                 // Display the list of hosts in the dialog
  474.                 if( FAILED( hr = SessionsDlgDisplayEnumList( hDlg ) ) )
  475.                 {
  476.                     DXTRACE_ERR_MSGBOX( TEXT("SessionsDlgDisplayEnumList"), hr );
  477.                     KillTimer( hDlg, TIMERID_DISPLAY_HOSTS );
  478.                     MessageBox( hDlg, TEXT("Error enumerating DirectPlay games."),
  479.                                 TEXT("DirectPlay Sample"),
  480.                                 MB_OK | MB_ICONERROR );
  481.                 }
  482.             }
  483.             else if( wParam == TIMERID_CONNECT_COMPLETE )
  484.             {
  485.                 // Check to see if the MessageHandler has set an event to tell us the
  486.                 // DPN_MSGID_CONNECT_COMPLETE has been processed.  Now m_hrConnectComplete
  487.                 // is valid.
  488.                 if( WAIT_OBJECT_0 == WaitForSingleObject( m_hConnectCompleteEvent, 0 ) )
  489.                 {
  490.                     m_bConnecting = FALSE;
  491.                     // Re-enable create button
  492.                     EnableWindow( GetDlgItem( hDlg, IDC_CREATE ), TRUE );
  493.                     if( FAILED( m_hrConnectComplete ) )
  494.                     {
  495.                         DXTRACE_ERR_MSGBOX( TEXT("DPN_MSGID_CONNECT_COMPLETE"), m_hrConnectComplete );
  496.                         MessageBox( m_hDlg, TEXT("Unable to join game."),
  497.                                     TEXT("DirectPlay Sample"),
  498.                                     MB_OK | MB_ICONERROR );
  499.                     }
  500.                     else
  501.                     {
  502.                         // DirectPlay connect successful, so end dialog
  503.                         m_hrDialog = NCW_S_FORWARD;
  504.                         EndDialog( m_hDlg, 0 );
  505.                     }
  506.                     KillTimer( hDlg, TIMERID_CONNECT_COMPLETE );
  507.                 }
  508.             }
  509.             break;
  510.         case WM_COMMAND:
  511.             switch( LOWORD(wParam) )
  512.             {
  513.                 case IDC_SEARCH_CHECK:
  514.                     m_bSearchingForSessions = !m_bSearchingForSessions;
  515.                     if( m_bSearchingForSessions )
  516.                     {
  517.                         // Ask the user for the remote address
  518.                         if( SPRequiresPort( &m_guidSP ) )
  519.                         {
  520.                             int nResult = (int)DialogBox( m_hInst, MAKEINTRESOURCE(IDD_MULTIPLAYER_ADDRESS),
  521.                                                           hDlg, (DLGPROC) StaticAddressDlgProc );
  522.                     
  523.                             // If the user cancelled the remote address dialog box, 
  524.                             // don't start the search
  525.                             if( nResult == IDCANCEL )
  526.                             {
  527.                                 m_bSearchingForSessions = FALSE;
  528.                                 break;
  529.                             }
  530.                         }
  531.                         SetDlgItemText( hDlg, IDC_SEARCH_CHECK, TEXT("Searching...") );
  532.                         // Start the timer to display the host list every so often
  533.                         SetTimer( hDlg, TIMERID_DISPLAY_HOSTS, DISPLAY_REFRESH_RATE, NULL );
  534.                         // Start the async enumeration
  535.                         if( FAILED( hr = SessionsDlgEnumHosts( hDlg ) ) )
  536.                         {
  537.                             DXTRACE_ERR_MSGBOX( TEXT("SessionsDlgEnumHosts"), hr );
  538.                             KillTimer( hDlg, TIMERID_DISPLAY_HOSTS );
  539.                             MessageBox( hDlg, TEXT("Error enumerating DirectPlay games."),
  540.                                         TEXT("DirectPlay Sample"),
  541.                                         MB_OK | MB_ICONERROR );
  542.                         }
  543.                     }
  544.                     else
  545.                     {
  546.                         SessionsDlgStopEnumHosts( hDlg );
  547.                     }
  548.                     break;
  549.                 case IDC_GAMES_LIST:
  550.                     if( HIWORD(wParam) != LBN_DBLCLK )
  551.                         break;
  552.                     // Fall through
  553.                 case IDC_JOIN:
  554.                     if( FAILED( hr = SessionsDlgJoinGame( hDlg ) ) )
  555.                     {
  556.                         DXTRACE_ERR_MSGBOX( TEXT("SessionsDlgJoinGame"), hr );
  557.                         MessageBox( hDlg, TEXT("Unable to join game."),
  558.                                     TEXT("DirectPlay Sample"),
  559.                                     MB_OK | MB_ICONERROR );
  560.                     }
  561.                     break;
  562.                 case IDC_CREATE:
  563.                     if( FAILED( hr = SessionsDlgCreateGame( hDlg ) ) )
  564.                     {
  565.                         DXTRACE_ERR_MSGBOX( TEXT("SessionsDlgCreateGame"), hr );
  566.                         MessageBox( hDlg, TEXT("Unable to create game."),
  567.                                     TEXT("DirectPlay Sample"),
  568.                                     MB_OK | MB_ICONERROR );
  569.                     }
  570.                     break;
  571.                 case IDCANCEL: // The close button was press
  572.                     m_hrDialog = NCW_S_QUIT;
  573.                     EndDialog(hDlg, 0);
  574.                     break;
  575.                 case IDC_BACK: // Cancel button was pressed
  576.                     m_hrDialog = NCW_S_BACKUP;
  577.                     EndDialog(hDlg, 0);
  578.                     break;
  579.                 default:
  580.                     return FALSE; // Message not handled
  581.             }
  582.             break;
  583.         
  584.             case WM_DESTROY:
  585.                 SessionsDlgStopEnumHosts(hDlg);
  586.                 break;
  587.         default:
  588.             return FALSE; // Message not handled
  589.     }
  590.     // Message was handled
  591.     return TRUE;
  592. }
  593. //-----------------------------------------------------------------------------
  594. // Name: SessionsDlgInitListbox()
  595. // Desc: Initializes the listbox
  596. //-----------------------------------------------------------------------------
  597. VOID CNetConnectWizard::SessionsDlgInitListbox( HWND hDlg )
  598. {
  599.     HWND hWndListBox = GetDlgItem( hDlg, IDC_GAMES_LIST );
  600.     // Clear the contents from the list box, and
  601.     // display "Looking for games" text in listbox
  602.     SendMessage( hWndListBox, LB_RESETCONTENT, 0, 0 );
  603.     if( m_bSearchingForSessions )
  604.     {
  605.         SendMessage( hWndListBox, LB_ADDSTRING, 0,
  606.                      (LPARAM) TEXT("Looking for games...") );
  607.     }
  608.     else
  609.     {
  610.         SendMessage( hWndListBox, LB_ADDSTRING, 0,
  611.                      (LPARAM) TEXT("Click Start Search to see a list of games."));
  612.         SendMessage( hWndListBox, LB_ADDSTRING, 0,
  613.                      (LPARAM) TEXT("Click Create to start a new game.") );
  614.     }
  615.     SendMessage( hWndListBox, LB_SETITEMDATA,  0, NULL );
  616.     SendMessage( hWndListBox, LB_SETCURSEL,    0, 0 );
  617.     // Disable the join button until sessions are found
  618.     EnableWindow( GetDlgItem( hDlg, IDC_JOIN ), FALSE );
  619. }
  620. //-----------------------------------------------------------------------------
  621. // Name: SessionsDlgEnumHosts()
  622. // Desc: Enumerates the DirectPlay sessions, and displays them in the listbox
  623. //-----------------------------------------------------------------------------
  624. HRESULT CNetConnectWizard::SessionsDlgEnumHosts( HWND hDlg )
  625. {
  626.     UNREFERENCED_PARAMETER( hDlg );
  627.     HRESULT hr = S_OK;
  628.     m_bEnumListChanged = TRUE;
  629.     IDirectPlay8Address*   pDP8AddressHost  = NULL;
  630.     IDirectPlay8Address*   pDP8AddressLocal = NULL;
  631.     // Create the local device address object
  632.     if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay8Address, NULL, 
  633.                                        CLSCTX_ALL, IID_IDirectPlay8Address,
  634.                                        (LPVOID*) &pDP8AddressLocal ) ) )
  635.     {
  636.         DXTRACE_ERR_MSGBOX( TEXT("CoCreateInstance"), hr );
  637.         goto LCleanup;
  638.     }
  639.     // Set local service provider
  640.     if( FAILED( hr = pDP8AddressLocal->SetSP( &m_guidSP ) ) )
  641.     {
  642.         DXTRACE_ERR_MSGBOX( TEXT("SetSP"), hr );
  643.         goto LCleanup;
  644.     }
  645.     // Create the remote host address object
  646.     if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay8Address, NULL, 
  647.                                        CLSCTX_ALL, IID_IDirectPlay8Address,
  648.                                        (LPVOID*) &pDP8AddressHost ) ) )
  649.     {
  650.         DXTRACE_ERR_MSGBOX( TEXT("CoCreateInstance"), hr );
  651.         goto LCleanup;
  652.     }
  653.     // Set remote service provider
  654.     if( FAILED( hr = pDP8AddressHost->SetSP( &m_guidSP ) ) )
  655.     {
  656.         DXTRACE_ERR_MSGBOX( TEXT("SetSP"), hr );
  657.         goto LCleanup;
  658.     }
  659.     // If we're using a TCP/IP (including network simulator) or IPX
  660.     // service provider, the user was given an option for hostname and
  661.     // port before the search started. 
  662.     if( SPRequiresPort( &m_guidSP ) ) 
  663.     {
  664.         // Add the hostname. If this is blank, DirectPlay will attempt
  665.         // to search the local network.
  666.         if( _tcscmp(m_strHostname, TEXT("")) != 0 )
  667.         {
  668.             WCHAR wszHostName[MAX_PATH];
  669.             DXUtil_ConvertGenericStringToWideCch( wszHostName, m_strHostname, MAX_PATH );
  670.             hr = pDP8AddressHost->AddComponent( DPNA_KEY_HOSTNAME, wszHostName, 
  671.                                                 (DWORD) (wcslen(wszHostName)+1)*sizeof(WCHAR), 
  672.                                                 DPNA_DATATYPE_STRING );
  673.             if( FAILED(hr) )
  674.             {
  675.                 DXTRACE_ERR_MSGBOX( TEXT("AddComponent"), hr );
  676.                 goto LCleanup;
  677.             }
  678.         }
  679.         // Add the requested port value. The port value is required in order to
  680.         // receive any search hits if DPNSVR isn't running on the remote machine.
  681.         // Games will typically hard code the port so the user need not know it
  682.         if( m_dwPort != 0 )
  683.         {
  684.             hr = pDP8AddressHost->AddComponent( DPNA_KEY_PORT, 
  685.                                                 &m_dwPort, sizeof(m_dwPort),
  686.                                                 DPNA_DATATYPE_DWORD );
  687.             if( FAILED(hr) )
  688.             {
  689.                 DXTRACE_ERR_MSGBOX( TEXT("AddComponent"), hr );
  690.                 goto LCleanup;
  691.             }
  692.         }
  693.     }
  694.     // Enumerate hosts
  695.     DPN_APPLICATION_DESC    dnAppDesc;
  696.     ZeroMemory( &dnAppDesc, sizeof(DPN_APPLICATION_DESC) );
  697.     dnAppDesc.dwSize          = sizeof(DPN_APPLICATION_DESC);
  698.     dnAppDesc.guidApplication = m_guidApp;
  699.     DWORD dwFlags;
  700.     dwFlags = 0;
  701.     // For certain service providers the user has not been
  702.     // asked to fill in the components of the remote address,
  703.     // so DirectPlay should ask for required fields.
  704.     if( !SPRequiresPort( &m_guidSP ) )
  705.     {
  706.         dwFlags = DPNENUMHOSTS_OKTOQUERYFORADDRESSING;
  707.     }
  708.     // Enumerate all the active DirectPlay games on the selected connection
  709.     hr = m_pDP->EnumHosts( &dnAppDesc,                            // application description
  710.                            pDP8AddressHost,                       // host address
  711.                            pDP8AddressLocal,                      // device address
  712.                            NULL,                                  // pointer to user data
  713.                            0,                                     // user data size
  714.                            INFINITE,                              // retry count (forever)
  715.                            0,                                     // retry interval (0=default)
  716.                            INFINITE,                              // time out (forever)
  717.                            NULL,                                  // user context
  718.                            &m_hEnumAsyncOp,                       // async handle
  719.                            dwFlags                                // flags
  720.                            );
  721.     if( FAILED(hr) )
  722.     {
  723.         DXTRACE_ERR_MSGBOX( TEXT("EnumHosts"), hr );
  724.         goto LCleanup;
  725.     }
  726. LCleanup:
  727.     SAFE_RELEASE( pDP8AddressHost);
  728.     SAFE_RELEASE( pDP8AddressLocal );
  729.     return hr;
  730. }
  731. //-----------------------------------------------------------------------------
  732. // Name: SessionsDlgStopEnumHosts()
  733. // Desc: Stops the running session enumeration
  734. //-----------------------------------------------------------------------------
  735. VOID CNetConnectWizard::SessionsDlgStopEnumHosts( HWND hDlg )
  736. {
  737.     HRESULT hr;
  738.     // Update the UI 
  739.     EnableWindow( GetDlgItem( hDlg, IDC_SEARCH_CHECK ), FALSE );
  740.     SetDlgItemText( hDlg, IDC_SEARCH_CHECK, TEXT("Stopping...") );
  741.     // Stop the timer, and stop the async enumeration
  742.     KillTimer( hDlg, TIMERID_DISPLAY_HOSTS );
  743.     // Until the CancelAsyncOperation returns, it is possible
  744.     // to still receive host enumerations. Instruct DirectPlay to
  745.     // stop the current enumeration and handle the rest of the cleanup
  746.     // when receiving the ASYNC_OP_COMPLETE message. If this method
  747.     // fails we should assume there's no active enumeration and call
  748.     // the finalization method directly.
  749.     if( m_hEnumAsyncOp )
  750.     {
  751.         hr = m_pDP->CancelAsyncOperation( m_hEnumAsyncOp, 0 );
  752.         if( FAILED(hr) )
  753.             SessionsDlgFinalizeEnumHosts( hDlg );
  754.     }
  755.     else
  756.     {
  757.         SessionsDlgFinalizeEnumHosts( hDlg );
  758.     }
  759. }
  760. //-----------------------------------------------------------------------------
  761. // Name: SessionsDlgFinalizeEnumHosts()
  762. // Desc: This method should be called when we receive confirmation from 
  763. //       DirectPlay that the enumeration has completed. It reset the search
  764. //       UI and state variables, and enables the search button.
  765. //-----------------------------------------------------------------------------
  766. VOID CNetConnectWizard::SessionsDlgFinalizeEnumHosts( HWND hDlg )
  767. {
  768.     // Clear the data list
  769.     SessionsDlgEnumListCleanup();
  770.     // Reset the search state variables
  771.     m_hEnumAsyncOp = NULL;
  772.     m_bSearchingForSessions = FALSE;
  773.     
  774.     // Reset the search portion of the dialog
  775.     SessionsDlgInitListbox( hDlg ); 
  776.     CheckDlgButton( hDlg, IDC_SEARCH_CHECK, BST_UNCHECKED );
  777.     SetDlgItemText( hDlg, IDC_SEARCH_CHECK, TEXT("Start &Search") );
  778.     EnableWindow( GetDlgItem( hDlg, IDC_SEARCH_CHECK ), TRUE );
  779. }
  780. //-----------------------------------------------------------------------------
  781. // Name: SessionsDlgNoteEnumResponse()
  782. // Desc: Stores them in the linked list, m_DPHostEnumHead.  This is
  783. //       called from the DirectPlay message handler so it could be
  784. //       called simultaneously from multiple threads.
  785. //-----------------------------------------------------------------------------
  786. HRESULT CNetConnectWizard::SessionsDlgNoteEnumResponse( PDPNMSG_ENUM_HOSTS_RESPONSE pEnumHostsResponseMsg )
  787. {
  788.     HRESULT hr = S_OK;
  789.     BOOL    bFound;
  790.     // This function is called from the DirectPlay message handler so it could be
  791.     // called simultaneously from multiple threads, so enter a critical section
  792.     // to assure that it we don't get race conditions.  Locking the entire
  793.     // function is crude, and could be more optimal but is effective for this
  794.     // simple sample
  795.     EnterCriticalSection( &m_csHostEnum );
  796.     DPHostEnumInfo* pDPHostEnum          = m_DPHostEnumHead.pNext;
  797.     DPHostEnumInfo* pDPHostEnumNext      = NULL;
  798.     const DPN_APPLICATION_DESC* pResponseMsgAppDesc =
  799.                             pEnumHostsResponseMsg->pApplicationDescription;
  800.     // Look for a matching session instance GUID.
  801.     bFound = FALSE;
  802.     while ( pDPHostEnum != &m_DPHostEnumHead )
  803.     {
  804.         if( pResponseMsgAppDesc->guidInstance == pDPHostEnum->pAppDesc->guidInstance )
  805.         {
  806.             bFound = TRUE;
  807.             break;
  808.         }
  809.         pDPHostEnumNext = pDPHostEnum;
  810.         pDPHostEnum = pDPHostEnum->pNext;
  811.     }
  812.     if( !bFound )
  813.     {
  814.         m_bEnumListChanged = TRUE;
  815.         // If there's no match, then look for invalid session and use it
  816.         pDPHostEnum = m_DPHostEnumHead.pNext;
  817.         while ( pDPHostEnum != &m_DPHostEnumHead )
  818.         {
  819.             if( !pDPHostEnum->bValid )
  820.                 break;
  821.             pDPHostEnum = pDPHostEnum->pNext;
  822.         }
  823.         // If no invalid sessions are found then make a new one
  824.         if( pDPHostEnum == &m_DPHostEnumHead )
  825.         {
  826.             // Found a new session, so create a new node
  827.             pDPHostEnum = new DPHostEnumInfo;
  828.             if( NULL == pDPHostEnum )
  829.             {
  830.                 hr = E_OUTOFMEMORY;
  831.                 goto LCleanup;
  832.             }
  833.             ZeroMemory( pDPHostEnum, sizeof(DPHostEnumInfo) );
  834.             // Add pDPHostEnum to the circular linked list, m_DPHostEnumHead
  835.             pDPHostEnum->pNext = m_DPHostEnumHead.pNext;
  836.             m_DPHostEnumHead.pNext = pDPHostEnum;
  837.         }
  838.     }
  839.     // Update the pDPHostEnum with new information
  840.     TCHAR strName[MAX_PATH];
  841.     if( pResponseMsgAppDesc->pwszSessionName )
  842.     {
  843.         DXUtil_ConvertWideStringToGenericCch( strName, pResponseMsgAppDesc->pwszSessionName, MAX_PATH );
  844.     }
  845.     // Cleanup any old enum
  846.     if( pDPHostEnum->pAppDesc )
  847.     {
  848.         SAFE_DELETE_ARRAY( pDPHostEnum->pAppDesc->pwszSessionName );
  849.         SAFE_DELETE( pDPHostEnum->pAppDesc );
  850.     }
  851.     SAFE_RELEASE( pDPHostEnum->pHostAddr );
  852.     SAFE_RELEASE( pDPHostEnum->pDeviceAddr );
  853.     //
  854.     // Duplicate pEnumHostsResponseMsg->pAddressSender in pDPHostEnum->pHostAddr.
  855.     // Duplicate pEnumHostsResponseMsg->pAddressDevice in pDPHostEnum->pDeviceAddr.
  856.     //
  857.     if( FAILED( hr = pEnumHostsResponseMsg->pAddressSender->Duplicate( &pDPHostEnum->pHostAddr ) ) )
  858.     {
  859.         DXTRACE_ERR_MSGBOX( TEXT("Duplicate"), hr );
  860.         goto LCleanup;
  861.     }
  862.     if( FAILED( hr = pEnumHostsResponseMsg->pAddressDevice->Duplicate( &pDPHostEnum->pDeviceAddr ) ) )
  863.     {
  864.         DXTRACE_ERR_MSGBOX( TEXT("Duplicate"), hr );
  865.         goto LCleanup;
  866.     }
  867.     // Deep copy the DPN_APPLICATION_DESC from
  868.     pDPHostEnum->pAppDesc = new DPN_APPLICATION_DESC;
  869.     if( NULL == pDPHostEnum->pAppDesc )
  870.     {
  871.         hr = E_OUTOFMEMORY;
  872.         DXTRACE_ERR_MSGBOX( TEXT("SessionsDlgNoteEnumResponse"), hr );
  873.         goto LCleanup;
  874.     }
  875.     ZeroMemory( pDPHostEnum->pAppDesc, sizeof(DPN_APPLICATION_DESC) );
  876.     memcpy( pDPHostEnum->pAppDesc, pResponseMsgAppDesc, sizeof(DPN_APPLICATION_DESC) );
  877.     if( pResponseMsgAppDesc->pwszSessionName )
  878.     {
  879.         pDPHostEnum->pAppDesc->pwszSessionName = new WCHAR[ wcslen(pResponseMsgAppDesc->pwszSessionName)+1 ];
  880.         wcscpy( pDPHostEnum->pAppDesc->pwszSessionName,
  881.                 pResponseMsgAppDesc->pwszSessionName );
  882.     }
  883.     // Update the time this was done, so that we can expire this host
  884.     // if it doesn't refresh w/in a certain amount of time
  885.     pDPHostEnum->dwLastPollTime = GETTIMESTAMP();
  886.     // Check to see if the current number of players changed
  887.     TCHAR szSessionTemp[MAX_PATH];
  888.     if( pResponseMsgAppDesc->dwMaxPlayers > 0 )
  889.     {
  890.         _sntprintf( szSessionTemp, MAX_PATH-1, TEXT("%s (%d/%d) (%dms)"), strName,
  891.                     pResponseMsgAppDesc->dwCurrentPlayers,
  892.                     pResponseMsgAppDesc->dwMaxPlayers,
  893.                     pEnumHostsResponseMsg->dwRoundTripLatencyMS );
  894.         // Null terminate
  895.         szSessionTemp[ MAX_PATH-1 ] = 0;
  896.     }
  897.     else
  898.     {
  899.         _sntprintf( szSessionTemp, MAX_PATH-1, TEXT("%s (%d) (%dms)"), strName,
  900.                     pResponseMsgAppDesc->dwCurrentPlayers,
  901.                     pEnumHostsResponseMsg->dwRoundTripLatencyMS );
  902.         // Null terminate
  903.         szSessionTemp[ MAX_PATH-1 ] = 0;
  904.     }
  905.     // if this node was previously invalidated, or the session name is now
  906.     // different the session list in the dialog needs to be updated
  907.     if( ( pDPHostEnum->bValid == FALSE ) ||
  908.         ( _tcscmp( pDPHostEnum->szSession, szSessionTemp ) != 0 ) )
  909.     {
  910.         m_bEnumListChanged = TRUE;
  911.     }
  912.     _tcscpy( pDPHostEnum->szSession, szSessionTemp );
  913.     // This host is now valid
  914.     pDPHostEnum->bValid = TRUE;
  915. LCleanup:
  916.     LeaveCriticalSection( &m_csHostEnum );
  917.     return hr;
  918. }
  919. //-----------------------------------------------------------------------------
  920. // Name: SessionsDlgExpireOldHostEnums
  921. // Desc: Check all nodes to see if any have expired yet.
  922. //-----------------------------------------------------------------------------
  923. VOID CNetConnectWizard::SessionsDlgExpireOldHostEnums()
  924. {
  925.     DWORD dwCurrentTime = GETTIMESTAMP();
  926.     // This is called from the dialog UI thread, SessionsDlgNoteEnumResponse
  927.     // is called from the DirectPlay message handler threads so
  928.     // they may also be inside it at this time, so we need to go into the
  929.     // critical section first
  930.     EnterCriticalSection( &m_csHostEnum );
  931.     DPHostEnumInfo* pDPHostEnum = m_DPHostEnumHead.pNext;
  932.     while ( pDPHostEnum != &m_DPHostEnumHead )
  933.     {
  934.         // Check the poll time to expire stale entries.  Also check to see if
  935.         // the entry is already invalid.  If so, don't note that the enum list
  936.         // changed because that causes the list in the dialog to constantly redraw.
  937.         if( ( pDPHostEnum->bValid != FALSE ) &&
  938.             ( pDPHostEnum->dwLastPollTime < dwCurrentTime - m_dwEnumHostExpireInterval ) )
  939.         {
  940.             // This node has expired, so invalidate it.
  941.             pDPHostEnum->bValid = FALSE;
  942.             m_bEnumListChanged  = TRUE;
  943.         }
  944.         pDPHostEnum = pDPHostEnum->pNext;
  945.     }
  946.     LeaveCriticalSection( &m_csHostEnum );
  947. }
  948. //-----------------------------------------------------------------------------
  949. // Name: SessionsDlgDisplayEnumList
  950. // Desc: Display the list of hosts in the dialog box
  951. //-----------------------------------------------------------------------------
  952. HRESULT CNetConnectWizard::SessionsDlgDisplayEnumList( HWND hDlg )
  953. {
  954.     HWND           hWndListBox   = GetDlgItem( hDlg, IDC_GAMES_LIST );
  955.     DPHostEnumInfo* pDPHostEnumSelected = NULL;
  956.     GUID           guidSelectedInstance;
  957.     BOOL           bFindSelectedGUID;
  958.     BOOL           bFoundSelectedGUID;
  959.     int            nItemSelected;
  960.     // This is called from the dialog UI thread, SessionsDlgNoteEnumResponse
  961.     // is called from the DirectPlay message handler threads so
  962.     // they may also be inside it at this time, so we need to go into the
  963.     // critical section first
  964.     EnterCriticalSection( &m_csHostEnum );
  965.     // Only update the display list if it has changed since last time
  966.     if( !m_bEnumListChanged )
  967.     {
  968.         LeaveCriticalSection( &m_csHostEnum );
  969.         return S_OK;
  970.     }
  971.     m_bEnumListChanged = FALSE;
  972.     bFindSelectedGUID  = FALSE;
  973.     bFoundSelectedGUID = FALSE;
  974.     // Try to keep the same session selected unless it goes away or
  975.     // there is no real session currently selected
  976.     nItemSelected = (int)SendMessage( hWndListBox, LB_GETCURSEL, 0, 0 );
  977.     if( nItemSelected != LB_ERR )
  978.     {
  979.         pDPHostEnumSelected = (DPHostEnumInfo*) SendMessage( hWndListBox, LB_GETITEMDATA,
  980.                                                              nItemSelected, 0 );
  981.         if( pDPHostEnumSelected != NULL && pDPHostEnumSelected->bValid )
  982.         {
  983.             guidSelectedInstance = pDPHostEnumSelected->pAppDesc->guidInstance;
  984.             bFindSelectedGUID = TRUE;
  985.         }
  986.     }
  987.     // Tell listbox not to redraw itself since the contents are going to change
  988.     SendMessage( hWndListBox, WM_SETREDRAW, FALSE, 0 );
  989.     // Test to see if any sessions exist in the linked list
  990.     DPHostEnumInfo* pDPHostEnum = m_DPHostEnumHead.pNext;
  991.     while ( pDPHostEnum != &m_DPHostEnumHead )
  992.     {
  993.         if( pDPHostEnum->bValid )
  994.             break;
  995.         pDPHostEnum = pDPHostEnum->pNext;
  996.     }
  997.     // If there are any sessions in list,
  998.     // then add them to the listbox
  999.     if( pDPHostEnum != &m_DPHostEnumHead )
  1000.     {
  1001.         // Clear the contents from the list box and enable the join button
  1002.         SendMessage( hWndListBox, LB_RESETCONTENT, 0, 0 );
  1003.         // Enable the join button only if not already connecting to a game
  1004.         if( !m_bConnecting )
  1005.             EnableWindow( GetDlgItem( hDlg, IDC_JOIN ), TRUE );
  1006.         pDPHostEnum = m_DPHostEnumHead.pNext;
  1007.         while ( pDPHostEnum != &m_DPHostEnumHead )
  1008.         {
  1009.             // Add host to list box if it is valid
  1010.             if( pDPHostEnum->bValid )
  1011.             {
  1012.                 int nIndex = (int)SendMessage( hWndListBox, LB_ADDSTRING, 0,
  1013.                                                (LPARAM)pDPHostEnum->szSession );
  1014.                 SendMessage( hWndListBox, LB_SETITEMDATA, nIndex, (LPARAM)pDPHostEnum );
  1015.                 if( bFindSelectedGUID )
  1016.                 {
  1017.                     // Look for the session the was selected before
  1018.                     if( pDPHostEnum->pAppDesc->guidInstance == guidSelectedInstance )
  1019.                     {
  1020.                         SendMessage( hWndListBox, LB_SETCURSEL, nIndex, 0 );
  1021.                         bFoundSelectedGUID = TRUE;
  1022.                     }
  1023.                 }
  1024.             }
  1025.             pDPHostEnum = pDPHostEnum->pNext;
  1026.         }
  1027.         if( !bFindSelectedGUID || !bFoundSelectedGUID )
  1028.             SendMessage( hWndListBox, LB_SETCURSEL, 0, 0 );
  1029.     }
  1030.     else
  1031.     {
  1032.         // There are no active session, so just reset the listbox
  1033.         SessionsDlgInitListbox( hDlg );
  1034.     }
  1035.     // Tell listbox to redraw itself now since the contents have changed
  1036.     SendMessage( hWndListBox, WM_SETREDRAW, TRUE, 0 );
  1037.     InvalidateRect( hWndListBox, NULL, FALSE );
  1038.     LeaveCriticalSection( &m_csHostEnum );
  1039.     return S_OK;
  1040. }
  1041. //-----------------------------------------------------------------------------
  1042. // Name: SessionsDlgJoinGame()
  1043. // Desc: Joins the selected DirectPlay session
  1044. //-----------------------------------------------------------------------------
  1045. HRESULT CNetConnectWizard::SessionsDlgJoinGame( HWND hDlg )
  1046. {
  1047.     HRESULT         hr;
  1048.     HWND            hWndListBox = GetDlgItem( hDlg, IDC_GAMES_LIST );
  1049.     DPHostEnumInfo* pDPHostEnumSelected = NULL;
  1050.     int             nItemSelected;
  1051.     m_bHostPlayer = FALSE;
  1052.     // Add status text in list box
  1053.     nItemSelected = (int)SendMessage( hWndListBox, LB_GETCURSEL, 0, 0 );
  1054.     EnterCriticalSection( &m_csHostEnum );
  1055.     pDPHostEnumSelected = (DPHostEnumInfo*) SendMessage( hWndListBox, LB_GETITEMDATA,
  1056.                                                          nItemSelected, 0 );
  1057.     if( NULL == pDPHostEnumSelected )
  1058.     {
  1059.         MessageBox( hDlg, TEXT("There are no games to join."),
  1060.                     TEXT("DirectPlay Sample"), MB_OK );
  1061.         hr = S_OK;
  1062.         goto LCleanReturn;
  1063.     }
  1064.     m_bConnecting = TRUE;
  1065.     // Set the peer info
  1066.     WCHAR wszPeerName[MAX_PLAYER_NAME];
  1067.     DXUtil_ConvertGenericStringToWideCch( wszPeerName, m_strLocalPlayerName, MAX_PLAYER_NAME );
  1068.     DPN_PLAYER_INFO dpPlayerInfo;
  1069.     ZeroMemory( &dpPlayerInfo, sizeof(DPN_PLAYER_INFO) );
  1070.     dpPlayerInfo.dwSize = sizeof(DPN_PLAYER_INFO);
  1071.     dpPlayerInfo.dwInfoFlags = DPNINFO_NAME;
  1072.     dpPlayerInfo.pwszName = wszPeerName;
  1073.     // Set the peer info, and use the DPNOP_SYNC since by default this
  1074.     // is an async call.  If it is not DPNOP_SYNC, then the peer info may not
  1075.     // be set by the time we call Connect() below.
  1076.     if( FAILED( hr = m_pDP->SetPeerInfo( &dpPlayerInfo, NULL, NULL, DPNOP_SYNC ) ) )
  1077.     {
  1078.         DXTRACE_ERR_MSGBOX( TEXT("SetPeerInfo"), hr );
  1079.         goto LCleanReturn;
  1080.     }
  1081.     ResetEvent( m_hConnectCompleteEvent );
  1082.     // Connect to an existing session. DPNCONNECT_OKTOQUERYFORADDRESSING allows
  1083.     // DirectPlay to prompt the user using a dialog box for any device address
  1084.     // or host address information that is missing
  1085.     // We also pass in copies of the app desc and host addr, since pDPHostEnumSelected
  1086.     // might be deleted from another thread that calls SessionsDlgExpireOldHostEnums().
  1087.     // This process could also be done using reference counting instead.
  1088.     hr = m_pDP->Connect( pDPHostEnumSelected->pAppDesc,       // the application desc
  1089.                          pDPHostEnumSelected->pHostAddr,      // address of the host of the session
  1090.                          pDPHostEnumSelected->pDeviceAddr,    // address of the local device the enum responses were received on
  1091.                          NULL, NULL,                          // DPN_SECURITY_DESC, DPN_SECURITY_CREDENTIALS
  1092.                          NULL, 0,                             // user data, user data size
  1093.                          NULL,                                // player context,
  1094.                          NULL, &m_hConnectAsyncOp,            // async context, async handle,
  1095.                          DPNCONNECT_OKTOQUERYFORADDRESSING ); // flags
  1096.     if( FAILED(hr) && hr != E_PENDING )
  1097.     {
  1098.         DXTRACE_ERR_MSGBOX( TEXT("Connect"), hr );
  1099.         goto LCleanReturn;
  1100.     }
  1101.     // Set a timer to wait for m_hConnectCompleteEvent to be signaled.
  1102.     // This will tell us when DPN_MSGID_CONNECT_COMPLETE has been processed
  1103.     // which lets us know if the connect was successful or not.
  1104.     SetTimer( hDlg, TIMERID_CONNECT_COMPLETE, 100, NULL );
  1105.     // Disable the create/join buttons until connect succeeds or fails
  1106.     EnableWindow( GetDlgItem( hDlg, IDC_JOIN ), FALSE );
  1107.     EnableWindow( GetDlgItem( hDlg, IDC_CREATE ), FALSE );
  1108.     hr = S_OK;
  1109. LCleanReturn:
  1110.     LeaveCriticalSection( &m_csHostEnum );
  1111.     return hr;
  1112. }
  1113. //-----------------------------------------------------------------------------
  1114. // Name: SessionsDlgCreateGame()
  1115. // Desc: Asks the user the session name, and creates a new DirectPlay session
  1116. //-----------------------------------------------------------------------------
  1117. HRESULT CNetConnectWizard::SessionsDlgCreateGame( HWND hDlg )
  1118. {
  1119.     HRESULT hr = S_OK;
  1120.     int     nResult;
  1121.     // Display a modal multiplayer connect dialog box.
  1122.     EnableWindow( hDlg, FALSE );
  1123.     nResult = (int)DialogBox( m_hInst, MAKEINTRESOURCE(IDD_MULTIPLAYER_CREATE),
  1124.                               hDlg, (DLGPROC) StaticCreateSessionDlgProc );
  1125.     EnableWindow( hDlg, TRUE );
  1126.     if( nResult == IDCANCEL )
  1127.         return S_OK;
  1128.     // Stop the search if we are about to connect
  1129.     if( m_bSearchingForSessions )
  1130.     {
  1131.         CheckDlgButton( m_hDlg, IDC_SEARCH_CHECK, BST_UNCHECKED );
  1132.         SendMessage( m_hDlg, WM_COMMAND, IDC_SEARCH_CHECK, 0 );
  1133.     }
  1134.     m_bHostPlayer = TRUE;
  1135.     IDirectPlay8Address*   pDP8AddressHost  = NULL;
  1136.     WCHAR*                 wszHostName      = NULL;
  1137.     // Create the local host address object
  1138.     if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay8Address, NULL, 
  1139.                                        CLSCTX_ALL, IID_IDirectPlay8Address,
  1140.                                        (LPVOID*) &pDP8AddressHost ) ) )
  1141.     {
  1142.         DXTRACE_ERR_MSGBOX( TEXT("CoCreateInstance"), hr );
  1143.         goto LCleanup;
  1144.     }
  1145.     // Set service provider
  1146.     if( FAILED( hr = pDP8AddressHost->SetSP( &m_guidSP ) ) )
  1147.     {
  1148.         DXTRACE_ERR_MSGBOX( TEXT("SetSP"), hr );
  1149.         goto LCleanup;
  1150.     }
  1151.     // If were are using a service provider that requires a port value, 
  1152.     // and the user has requested a particular port number, set that
  1153.     // port as a component of the host address. If no port is specified, 
  1154.     // DirectPlay will automatically select an open port
  1155.     if( m_dwPort != 0 && SPRequiresPort( &m_guidSP ) )
  1156.     {
  1157.         hr = pDP8AddressHost->AddComponent( DPNA_KEY_PORT, 
  1158.                                             &m_dwPort, 
  1159.                                             sizeof(m_dwPort),
  1160.                                             DPNA_DATATYPE_DWORD );
  1161.         if( FAILED(hr) )
  1162.         {
  1163.             DXTRACE_ERR_MSGBOX( TEXT("AddComponent"), hr );
  1164.             goto LCleanup;
  1165.         }
  1166.     }
  1167.     // Set peer info name
  1168.     WCHAR wszPeerName[MAX_PLAYER_NAME];
  1169.     DXUtil_ConvertGenericStringToWideCch( wszPeerName, m_strLocalPlayerName, MAX_PLAYER_NAME );
  1170.     DPN_PLAYER_INFO dpPlayerInfo;
  1171.     ZeroMemory( &dpPlayerInfo, sizeof(DPN_PLAYER_INFO) );
  1172.     dpPlayerInfo.dwSize = sizeof(DPN_PLAYER_INFO);
  1173.     dpPlayerInfo.dwInfoFlags = DPNINFO_NAME;
  1174.     dpPlayerInfo.pwszName = wszPeerName;
  1175.     // Set the peer info, and use the DPNOP_SYNC since by default this
  1176.     // is an async call.  If it is not DPNOP_SYNC, then the peer info may not
  1177.     // be set by the time we call Host() below.
  1178.     if( FAILED( hr = m_pDP->SetPeerInfo( &dpPlayerInfo, NULL, NULL, DPNOP_SYNC ) ) )
  1179.     {
  1180.         DXTRACE_ERR_MSGBOX( TEXT("SetPeerInfo"), hr );
  1181.         goto LCleanup;
  1182.     }
  1183.     WCHAR wszSessionName[MAX_PATH];
  1184.     DXUtil_ConvertGenericStringToWideCch( wszSessionName, m_strSessionName, MAX_PATH );
  1185.     // Setup the application desc
  1186.     DPN_APPLICATION_DESC dnAppDesc;
  1187.     ZeroMemory( &dnAppDesc, sizeof(DPN_APPLICATION_DESC) );
  1188.     dnAppDesc.dwSize          = sizeof(DPN_APPLICATION_DESC);
  1189.     dnAppDesc.guidApplication = m_guidApp;
  1190.     dnAppDesc.pwszSessionName = wszSessionName;
  1191.     dnAppDesc.dwMaxPlayers    = m_dwMaxPlayers;
  1192.     dnAppDesc.dwFlags         = 0;
  1193.     if( m_bMigrateHost )
  1194.         dnAppDesc.dwFlags |= DPNSESSION_MIGRATE_HOST;
  1195.     if( !m_bUseDPNSVR )
  1196.         dnAppDesc.dwFlags |= DPNSESSION_NODPNSVR;
  1197.     if( SIGN_FAST == m_eSigningType )
  1198.         dnAppDesc.dwFlags |= DPNSESSION_FAST_SIGNED;
  1199.     else if( SIGN_FULL == m_eSigningType )
  1200.         dnAppDesc.dwFlags |= DPNSESSION_FULL_SIGNED;
  1201.     
  1202.     // Host a game on m_pDeviceAddress as described by dnAppDesc
  1203.     // DPNHOST_OKTOQUERYFORADDRESSING allows DirectPlay to prompt the user
  1204.     // using a dialog box for any device address information that is missing
  1205.     if( FAILED( hr = m_pDP->Host( &dnAppDesc,               // the application desc
  1206.                                   &pDP8AddressHost,         // array of addresses of the local devices used to connect to the host
  1207.                                   1,                        // number in array
  1208.                                   NULL, NULL,               // DPN_SECURITY_DESC, DPN_SECURITY_CREDENTIALS
  1209.                                   NULL,                     // player context
  1210.                                   DPNHOST_OKTOQUERYFORADDRESSING ) ) ) // flags
  1211.     { 
  1212.         // This error is often caused by a port conflict
  1213.         if( hr == DPNERR_INVALIDDEVICEADDRESS && 
  1214.             m_dwPort != 0 && SPRequiresPort( &m_guidSP ) )
  1215.         {
  1216.             MessageBox( hDlg, TEXT("This error is often caused by a port conflict.nn")
  1217.                               TEXT("If another application is already using the port you specified,n")
  1218.                               TEXT("try creating the game using a different port number."),
  1219.                         TEXT("Invalid Device Address"), MB_OK | MB_ICONINFORMATION );
  1220.         }
  1221.         DXTRACE_ERR_MSGBOX( TEXT("Host"), hr );
  1222.         goto LCleanup;
  1223.     }
  1224.     // DirectPlay connect successful, so end dialog
  1225.     m_hrDialog = NCW_S_FORWARD;
  1226.     EndDialog( hDlg, 0 );
  1227. LCleanup:
  1228.     SAFE_RELEASE( pDP8AddressHost );
  1229.     SAFE_DELETE( wszHostName );
  1230.     return hr;
  1231. }
  1232. //-----------------------------------------------------------------------------
  1233. // Name: StaticConnectionsDlgProc()
  1234. // Desc: Static msg handler which passes messages
  1235. //-----------------------------------------------------------------------------
  1236. INT_PTR CALLBACK CNetConnectWizard::StaticCreateSessionDlgProc( HWND hDlg, UINT uMsg,
  1237.                                                                 WPARAM wParam, LPARAM lParam )
  1238. {
  1239.     if( g_pNCW )
  1240.         return g_pNCW->CreateSessionDlgProc( hDlg, uMsg, wParam, lParam );
  1241.     return FALSE; // Message not handled
  1242. }
  1243. //-----------------------------------------------------------------------------
  1244. // Name: CreateSessionDlgProc()
  1245. // Desc: Handles messages fro the multiplayer create game dialog
  1246. //-----------------------------------------------------------------------------
  1247. INT_PTR CALLBACK CNetConnectWizard::CreateSessionDlgProc( HWND hDlg, UINT msg,
  1248.                                                           WPARAM wParam, LPARAM lParam )
  1249. {
  1250.     UNREFERENCED_PARAMETER( lParam );
  1251.     DWORD dwNameLength;
  1252.     switch( msg )
  1253.     {
  1254.         case WM_INITDIALOG:
  1255. #if defined(WIN32_PLATFORM_PSPC) && (_WIN32_WCE >= 300)
  1256.             SHINITDLGINFO   shidi;
  1257.             memset(&shidi, 0, sizeof(SHINITDLGINFO));
  1258.             shidi.dwMask = SHIDIM_FLAGS;
  1259.             shidi.dwFlags = SHIDIF_SIPDOWN | SHIDIF_SIZEDLGFULLSCREEN;
  1260.             shidi.hDlg = hDlg;
  1261.             SetForegroundWindow(hDlg);
  1262.             SHInitDialog(&shidi);
  1263. #endif // WIN32_PLATFORM_PSPC
  1264.             SetDlgItemText( hDlg, IDC_EDIT_SESSION_NAME, m_strSessionName );
  1265.             CheckDlgButton( hDlg, IDC_MIGRATE_HOST, BST_CHECKED );
  1266.             CheckDlgButton( hDlg, IDC_SIGNING_FAST, BST_CHECKED );
  1267.             // Fill in the port value if the calling application gave us a default
  1268.             if( m_dwPort != 0 )
  1269.             {
  1270.                 TCHAR strPort[40];
  1271.                 _itot( m_dwPort, strPort, 10 );
  1272.                 SetDlgItemText( hDlg, IDC_LOCAL_PORT, strPort );
  1273.             }
  1274.             // Hide the port field for service providers which don't use it
  1275.             if( !SPRequiresPort( &m_guidSP ) )
  1276.             {
  1277.                 ShowWindow( GetDlgItem( hDlg, IDC_LOCAL_PORT ), SW_HIDE );
  1278.                 ShowWindow( GetDlgItem( hDlg, IDC_LOCAL_PORT_TEXT ), SW_HIDE );
  1279.             }
  1280.             
  1281.             return TRUE;
  1282. // Context-sensitive help is not supported on PocketPC
  1283. #ifndef UNDER_CE
  1284.         case WM_HELP:
  1285.             LPHELPINFO lphi;
  1286.             lphi = (LPHELPINFO) lParam;
  1287.             switch( lphi->iCtrlId )
  1288.             {
  1289.                 case IDC_EDIT_SESSION_NAME:
  1290.                     MessageBox( hDlg, TEXT("The name used to help other players identifyn")
  1291.                                       TEXT("your session across the network."), 
  1292.                                 TEXT("Game Name"), MB_OK | MB_ICONQUESTION );
  1293.                     break;
  1294.                 
  1295.                 case IDC_SIGNING_FULL:
  1296.                 case IDC_SIGNING_FAST:
  1297.                     MessageBox( hDlg, TEXT("The level of cryptographic security to use forn")
  1298.                                       TEXT("network data; full-signing provides the mostn")
  1299.                                       TEXT("protection against spoofing at the cost ofn")
  1300.                                       TEXT("additional packet size."), 
  1301.                                 TEXT("Session Signing"), MB_OK | MB_ICONQUESTION );
  1302.                     break;
  1303.                 case IDC_MIGRATE_HOST:
  1304.                     MessageBox( hDlg, TEXT("If enabled, DirectPlay will automatically selectn")
  1305.                                       TEXT("a new session host if the current host exits."), 
  1306.                                 TEXT("Host Migration"), MB_OK | MB_ICONQUESTION );
  1307.                     break;
  1308.                 case IDC_USE_DPNSVR:
  1309.                     MessageBox( hDlg, TEXT("This DirectPlay-managed application acceptsn")
  1310.                                       TEXT("enumeration requests on a known port and forwardsn")
  1311.                                       TEXT("incoming requests to all sessions on the machine,n")
  1312.                                       TEXT("which allows the querying player to find sessionsn")
  1313.                                       TEXT("without knowing the port address.nn")
  1314.                                       TEXT("This service may not work properly with certainn")
  1315.                                       TEXT("NAT configurations."), 
  1316.                                 TEXT("DPNSVR"), MB_OK | MB_ICONQUESTION );
  1317.                     break;
  1318.                 case IDC_LOCAL_PORT:
  1319.                     MessageBox( hDlg, TEXT("Specifies the local port on which to host the newn")
  1320.                                       TEXT("session. If set blank, DirectPlay will automaticallyn")
  1321.                                       TEXT("select an open port."), 
  1322.                                 TEXT("Local Port"), MB_OK | MB_ICONQUESTION );
  1323.                     break;
  1324.                 default:
  1325.                     return FALSE; // Not handled
  1326.             }
  1327.             return TRUE;
  1328. #endif // !UNDER_CE
  1329.         case WM_COMMAND:
  1330.             switch( LOWORD(wParam) )
  1331.             {
  1332.                 case IDOK:
  1333.                     dwNameLength = GetDlgItemText( hDlg, IDC_EDIT_SESSION_NAME,
  1334.                                                    m_strSessionName,
  1335.                                                    MAX_PATH );
  1336.                     if( dwNameLength == 0 )
  1337.                         return TRUE; // Don't accept blank session names
  1338.                     m_bMigrateHost = ( IsDlgButtonChecked( hDlg,
  1339.                                        IDC_MIGRATE_HOST ) == BST_CHECKED );
  1340.                     m_bUseDPNSVR = ( IsDlgButtonChecked( hDlg,
  1341.                                      IDC_USE_DPNSVR ) == BST_CHECKED );
  1342.                     // Set the desired port value
  1343.                     TCHAR strPort[40];
  1344.                     GetDlgItemText( hDlg, IDC_LOCAL_PORT, strPort, 40 );
  1345.                     strPort[39] = 0;
  1346.                     m_dwPort = _ttoi( strPort );
  1347.                     // Set session signing options
  1348.                     if( BST_CHECKED == IsDlgButtonChecked( hDlg, IDC_SIGNING_FAST ) )
  1349.                         m_eSigningType = SIGN_FAST;
  1350.                     else if( BST_CHECKED == IsDlgButtonChecked( hDlg, IDC_SIGNING_FULL ) )
  1351.                         m_eSigningType = SIGN_FULL;
  1352.                     else
  1353.                         m_eSigningType = SIGN_NONE;
  1354.                     EndDialog( hDlg, IDOK );
  1355.                     return TRUE;
  1356.                 case IDCANCEL:
  1357.                     EndDialog( hDlg, IDCANCEL );
  1358.                     return TRUE;
  1359.             }
  1360.             break;
  1361.     }
  1362.     return FALSE; // Didn't handle message
  1363. }
  1364. //-----------------------------------------------------------------------------
  1365. // Name: SessionsDlgEnumListCleanup()
  1366. // Desc: Deletes the linked list, g_DPHostEnumInfoHead
  1367. //-----------------------------------------------------------------------------
  1368. VOID CNetConnectWizard::SessionsDlgEnumListCleanup()
  1369. {
  1370.     DPHostEnumInfo* pDPHostEnum = m_DPHostEnumHead.pNext;
  1371.     DPHostEnumInfo* pDPHostEnumDelete;
  1372.     while ( pDPHostEnum != &m_DPHostEnumHead )
  1373.     {
  1374.         pDPHostEnumDelete = pDPHostEnum;
  1375.         pDPHostEnum = pDPHostEnum->pNext;
  1376.         if( pDPHostEnumDelete->pAppDesc )
  1377.         {
  1378.             SAFE_DELETE_ARRAY( pDPHostEnumDelete->pAppDesc->pwszSessionName );
  1379.             SAFE_DELETE( pDPHostEnumDelete->pAppDesc );
  1380.         }
  1381.         // Changed from array delete to Release
  1382.         SAFE_RELEASE( pDPHostEnumDelete->pHostAddr );
  1383.         SAFE_RELEASE( pDPHostEnumDelete->pDeviceAddr );
  1384.         SAFE_DELETE( pDPHostEnumDelete );
  1385.     }
  1386.     // Re-link the g_DPHostEnumInfoHead circular linked list
  1387.     m_DPHostEnumHead.pNext = &m_DPHostEnumHead;
  1388. }
  1389. //-----------------------------------------------------------------------------
  1390. // Name: StaticAddressDlgProc()
  1391. // Desc: Static msg handler which passes messages
  1392. //-----------------------------------------------------------------------------
  1393. INT_PTR CALLBACK CNetConnectWizard::StaticAddressDlgProc( HWND hDlg, UINT uMsg,
  1394.                                                           WPARAM wParam, LPARAM lParam )
  1395. {
  1396.     if( g_pNCW )
  1397.         return g_pNCW->AddressDlgProc( hDlg, uMsg, wParam, lParam );
  1398.     return FALSE; // Message not handled
  1399. }
  1400. //-----------------------------------------------------------------------------
  1401. // Name: AddressDlgProc()
  1402. // Desc: Handles messages for the multiplayer connect dialog
  1403. //-----------------------------------------------------------------------------
  1404. INT_PTR CALLBACK CNetConnectWizard::AddressDlgProc( HWND hDlg, UINT msg,
  1405.                                                     WPARAM wParam, LPARAM lParam )
  1406. {
  1407.     UNREFERENCED_PARAMETER( lParam );
  1408.     
  1409.     switch( msg )
  1410.     {
  1411.         case WM_INITDIALOG:
  1412.         {
  1413. #if defined(WIN32_PLATFORM_PSPC) && (_WIN32_WCE >= 300)
  1414.             SHINITDLGINFO   shidi;
  1415.             memset(&shidi, 0, sizeof(SHINITDLGINFO));
  1416.             shidi.dwMask = SHIDIM_FLAGS;
  1417.             shidi.dwFlags = SHIDIF_SIPDOWN | SHIDIF_SIZEDLGFULLSCREEN;
  1418.             shidi.hDlg = hDlg;
  1419.             SetForegroundWindow(hDlg);
  1420.             SHInitDialog(&shidi);
  1421. #endif // WIN32_PLATFORM_PSPC
  1422.             // Set the default port
  1423.             if( m_dwPort != 0 )
  1424.             {
  1425.                 TCHAR strPort[40];
  1426.                 _itot( m_dwPort, strPort, 10 );
  1427.                 SetDlgItemText( hDlg, IDC_REMOTE_PORT, strPort );
  1428.             }
  1429.             return TRUE;
  1430.         }
  1431.         case WM_COMMAND:
  1432.         {
  1433.             switch( LOWORD(wParam) )
  1434.             {
  1435.                 case IDOK:
  1436.                     // Store the user's choices
  1437.                     TCHAR strPort[40];
  1438.                     GetDlgItemText( hDlg, IDC_REMOTE_PORT, strPort, 40 );
  1439.                     strPort[39] = 0;
  1440.                     m_dwPort = _ttoi( strPort );
  1441.                     GetDlgItemText( hDlg, IDC_REMOTE_HOSTNAME, m_strHostname, MAX_PATH );
  1442.                     m_strHostname[MAX_PATH-1] = 0;
  1443.                     EndDialog( hDlg, IDOK );
  1444.                     return TRUE;
  1445.                 case IDCANCEL:
  1446.                     EndDialog( hDlg, IDCANCEL );
  1447.                     return TRUE;
  1448.             }
  1449.             break;
  1450.         }
  1451.     }
  1452.     return FALSE; // Not handled
  1453. }
  1454. //-----------------------------------------------------------------------------
  1455. // Name: MessageHandler
  1456. // Desc: Handler for DirectPlay messages.  This function is called by
  1457. //       the DirectPlay message handler pool of threads, so be careful of thread
  1458. //       synchronization problems with shared memory
  1459. //-----------------------------------------------------------------------------
  1460. HRESULT WINAPI CNetConnectWizard::MessageHandler( PVOID pvUserContext,
  1461.                                                   DWORD dwMessageId,
  1462.                                                   PVOID pMsgBuffer )
  1463. {
  1464.     UNREFERENCED_PARAMETER( pvUserContext );
  1465.     
  1466.     // Try not to stay in this message handler for too long, otherwise
  1467.     // there will be a backlog of data.  The best solution is to
  1468.     // queue data as it comes in, and then handle it on other threads.
  1469.     // This function is called by the DirectPlay message handler pool of
  1470.     // threads, so be careful of thread synchronization problems with shared memory
  1471.     switch(dwMessageId)
  1472.     {
  1473.         case DPN_MSGID_ENUM_HOSTS_RESPONSE:
  1474.         {
  1475.             PDPNMSG_ENUM_HOSTS_RESPONSE pEnumHostsResponseMsg;
  1476.             pEnumHostsResponseMsg = (PDPNMSG_ENUM_HOSTS_RESPONSE)pMsgBuffer;
  1477.             // Take note of the host response
  1478.             SessionsDlgNoteEnumResponse( pEnumHostsResponseMsg );
  1479.             break;
  1480.         }
  1481.         case DPN_MSGID_ASYNC_OP_COMPLETE:
  1482.         {
  1483.             PDPNMSG_ASYNC_OP_COMPLETE pAsyncOpCompleteMsg;
  1484.             pAsyncOpCompleteMsg = (PDPNMSG_ASYNC_OP_COMPLETE)pMsgBuffer;
  1485.             if( pAsyncOpCompleteMsg->hAsyncOp == m_hEnumAsyncOp )
  1486.                 SessionsDlgFinalizeEnumHosts( m_hDlg );
  1487.             break;
  1488.         }
  1489.         case DPN_MSGID_CONNECT_COMPLETE:
  1490.         {
  1491.             PDPNMSG_CONNECT_COMPLETE pConnectCompleteMsg;
  1492.             pConnectCompleteMsg = (PDPNMSG_CONNECT_COMPLETE)pMsgBuffer;
  1493.             // Set m_hrConnectComplete, then set an event letting
  1494.             // everyone know that the DPN_MSGID_CONNECT_COMPLETE msg
  1495.             // has been handled
  1496.             m_hrConnectComplete = pConnectCompleteMsg->hResultCode;
  1497.             SetEvent( m_hConnectCompleteEvent );
  1498.             break;
  1499.         }
  1500.     }
  1501.     return S_OK;
  1502. }
  1503. //-----------------------------------------------------------------------------
  1504. // Name: ConnectUsingLobbySettings
  1505. // Desc: Call this after the DPL_MSGID_CONNECT has been processed to carry out
  1506. //       the connection settings received by the lobby client.  DPL_MSGID_CONNECT
  1507. //       will have already been processed if we were lobby launched, or after
  1508. //       WaitForConnection returns without timing out.
  1509. //-----------------------------------------------------------------------------
  1510. HRESULT CNetConnectWizard::ConnectUsingLobbySettings()
  1511. {
  1512.     HRESULT hr;
  1513.     DPNHANDLE hAsync;
  1514.     if( m_hLobbyClient == NULL )
  1515.         return E_INVALIDARG;
  1516.     DPL_CONNECTION_SETTINGS* pSettings = NULL;
  1517.     DWORD dwSettingsSize = 0;
  1518.     // Get the connection settings from the lobby.
  1519.     hr = m_pLobbiedApp->GetConnectionSettings( m_hLobbyClient, pSettings, &dwSettingsSize, 0 );
  1520.     if( hr != DPNERR_BUFFERTOOSMALL )
  1521.     {
  1522.         DXTRACE_ERR_MSGBOX( TEXT("GetConnectionSettings"), hr );
  1523.         goto LCleanReturn;
  1524.     }
  1525.     pSettings = (DPL_CONNECTION_SETTINGS*) new BYTE[dwSettingsSize];
  1526.     if( NULL == pSettings )
  1527.     {
  1528.         hr = E_OUTOFMEMORY;
  1529.         DXTRACE_ERR_MSGBOX( TEXT("ConnectUsingLobbySettings"), hr );
  1530.         goto LCleanReturn;
  1531.     }
  1532.     if( FAILED( hr = m_pLobbiedApp->GetConnectionSettings( m_hLobbyClient, pSettings, &dwSettingsSize, 0 ) ) )
  1533.     {
  1534.         DXTRACE_ERR_MSGBOX( TEXT("GetConnectionSettings"), hr );
  1535.         goto LCleanReturn;
  1536.     }
  1537.     // Check if the lobby told us to host the game
  1538.     m_bHostPlayer = (pSettings->dwFlags & DPLCONNECTSETTINGS_HOST);
  1539.     // Set the peer info
  1540.     WCHAR wszPeerName[MAX_PLAYER_NAME];
  1541.     DXUtil_ConvertGenericStringToWideCch( wszPeerName, m_strLocalPlayerName, MAX_PLAYER_NAME);
  1542.     DPN_PLAYER_INFO dpPlayerInfo;
  1543.     ZeroMemory( &dpPlayerInfo, sizeof(DPN_PLAYER_INFO) );
  1544.     dpPlayerInfo.dwSize = sizeof(DPN_PLAYER_INFO);
  1545.     dpPlayerInfo.dwInfoFlags = DPNINFO_NAME;
  1546.     dpPlayerInfo.pwszName = wszPeerName;
  1547.     // Set the peer info, and use the DPNOP_SYNC since by default this
  1548.     // is an async call.  If it is not DPNOP_SYNC, then the peer info may not
  1549.     // be set by the time we call Connect() below.
  1550.     if( FAILED( hr = m_pDP->SetPeerInfo( &dpPlayerInfo, NULL, NULL, DPNOP_SYNC ) ) )
  1551.     {
  1552.         DXTRACE_ERR_MSGBOX( TEXT("SetPeerInfo"), hr );
  1553.         goto LCleanReturn;
  1554.     }
  1555.     if( m_bHostPlayer )
  1556.     {
  1557.         // Enable host migrate by default.
  1558.         pSettings->dpnAppDesc.dwFlags |= DPNSESSION_MIGRATE_HOST;
  1559.         // Disable DPNSVR by default
  1560.         pSettings->dpnAppDesc.dwFlags |= DPNSESSION_NODPNSVR;
  1561.         // Host a game as described by pSettings
  1562.         if( FAILED( hr = m_pDP->Host( &pSettings->dpnAppDesc,               // the application desc
  1563.                                       pSettings->ppdp8DeviceAddresses,      // array of addresses of the local devices used to connect to the host
  1564.                                       pSettings->cNumDeviceAddresses,       // number in array
  1565.                                       NULL, NULL,                           // DPN_SECURITY_DESC, DPN_SECURITY_CREDENTIALS
  1566.                                       NULL,                                 // player context
  1567.                                       0 ) ) )                               // flags
  1568.         {
  1569.             DXTRACE_ERR_MSGBOX( TEXT("Host"), hr );
  1570.             goto LCleanReturn;
  1571.         }
  1572.     }
  1573.     else
  1574.     {
  1575.         // Connect to an existing session. There should only be on device address in
  1576.         // the connection settings structure when connecting to a session, so just
  1577.         // pass in the first one.
  1578.         // The enumeration is automatically cancelled after Connect is called 
  1579.         hr = m_pDP->Connect( &pSettings->dpnAppDesc,              // the application desc
  1580.                              pSettings->pdp8HostAddress,          // address of the host of the session
  1581.                              pSettings->ppdp8DeviceAddresses[0],  // address of the local device used to connect to the host
  1582.                              NULL, NULL,                          // DPN_SECURITY_DESC, DPN_SECURITY_CREDENTIALS
  1583.                              NULL, 0,                             // user data, user data size
  1584.                              NULL,                                // player context,
  1585.                              NULL, &hAsync,                       // async context, async handle,
  1586.                              0 );                                 // flags
  1587.         if( hr != E_PENDING && FAILED(hr) )
  1588.         {
  1589.             DXTRACE_ERR_MSGBOX( TEXT("Connect"), hr );
  1590.             goto LCleanReturn;
  1591.         }
  1592.         hr = S_OK; // Accept E_PENDING.
  1593.         // Wait until the MessageHandler sets an event to tell us the
  1594.         // DPN_MSGID_CONNECT_COMPLETE has been processed.  Then m_hrConnectComplete
  1595.         // will be valid.
  1596.         WaitForSingleObject( m_hConnectCompleteEvent, INFINITE );
  1597.         if( FAILED( m_hrConnectComplete ) )
  1598.         {
  1599.             DXTRACE_ERR_MSGBOX( TEXT("DPN_MSGID_CONNECT_COMPLETE"), m_hrConnectComplete );
  1600.             MessageBox( m_hDlg, TEXT("Unable to join game."),
  1601.                         TEXT("DirectPlay Sample"),
  1602.                         MB_OK | MB_ICONERROR );
  1603.             hr = m_hrConnectComplete;
  1604.         }
  1605.     }
  1606. LCleanReturn:
  1607.     // Cleanup the addresses and memory obtained from GetConnectionSettings
  1608.     
  1609.     if( pSettings )
  1610.     {
  1611.         SAFE_RELEASE( pSettings->pdp8HostAddress );
  1612.         
  1613.         for( DWORD dwIndex=0; dwIndex < pSettings->cNumDeviceAddresses; dwIndex++ )
  1614.             SAFE_RELEASE( pSettings->ppdp8DeviceAddresses[dwIndex] );
  1615.         SAFE_DELETE_ARRAY( pSettings );
  1616.     }
  1617.     return hr;
  1618. }
  1619. //-----------------------------------------------------------------------------
  1620. // Name: LobbyMessageHandler
  1621. // Desc: Handler for DirectPlay messages.  This function is called by
  1622. //       the DirectPlay lobby message handler pool of threads, so be careful of thread
  1623. //       synchronization problems with shared memory
  1624. //-----------------------------------------------------------------------------
  1625. HRESULT WINAPI CNetConnectWizard::LobbyMessageHandler( PVOID pvUserContext,
  1626.                                                        DWORD dwMessageId,
  1627.                                                        PVOID pMsgBuffer )
  1628. {
  1629.     UNREFERENCED_PARAMETER( pvUserContext );
  1630.     HRESULT hr = S_OK;
  1631.     switch(dwMessageId)
  1632.     {
  1633.         case DPL_MSGID_CONNECT:
  1634.         {
  1635.             // This message will be processed when a lobby connection has been
  1636.             // established. If you were lobby launched then
  1637.             // IDirectPlay8LobbiedApplication::Initialize()
  1638.             // waits until this message has been processed before returning, so
  1639.             // take care not to deadlock by making calls that need to be handled by
  1640.             // the thread who called Initialize().  The same is true for WaitForConnection()
  1641.             PDPL_MESSAGE_CONNECT pConnectMsg;
  1642.             pConnectMsg = (PDPL_MESSAGE_CONNECT)pMsgBuffer;
  1643.             PDPL_CONNECTION_SETTINGS pSettings = pConnectMsg->pdplConnectionSettings;
  1644.             m_hLobbyClient = pConnectMsg->hConnectId;
  1645.             if( FAILED( hr = m_pDP->RegisterLobby( m_hLobbyClient, m_pLobbiedApp,
  1646.                                                    DPNLOBBY_REGISTER ) ) )
  1647.                 return DXTRACE_ERR_MSGBOX( TEXT("RegisterLobby"), hr );
  1648.             if( pSettings == NULL )
  1649.             {
  1650.                 // There aren't connection settings from the lobby
  1651.                 m_bHaveConnectionSettingsFromLobby = FALSE;
  1652.             }
  1653.             else
  1654.             {
  1655.                 // Record the player name if found
  1656.                 if( pSettings->pwszPlayerName != NULL )
  1657.                 {
  1658.                     TCHAR strPlayerName[MAX_PLAYER_NAME];
  1659.                     DXUtil_ConvertWideStringToGenericCch( strPlayerName, pSettings->pwszPlayerName, MAX_PLAYER_NAME );
  1660.                     _tcsncpy( m_strLocalPlayerName, strPlayerName, MAX_PLAYER_NAME-1 );
  1661.                 }
  1662.                 else
  1663.                 {
  1664.                     _tcsncpy( m_strLocalPlayerName, TEXT("Unknown"), MAX_PLAYER_NAME-1 );
  1665.                 }
  1666.                 m_bHaveConnectionSettingsFromLobby = TRUE;
  1667.             }
  1668.             // Tell everyone we have a lobby connection now
  1669.             SetEvent( m_hLobbyConnectionEvent );
  1670.             break;
  1671.         }
  1672.     }
  1673.     return S_OK;
  1674. }
  1675. //-----------------------------------------------------------------------------
  1676. // Name: StaticLobbyWaitDlgProc()
  1677. // Desc: Static msg handler which passes messages
  1678. //-----------------------------------------------------------------------------
  1679. INT_PTR CALLBACK CNetConnectWizard::StaticLobbyWaitDlgProc( HWND hDlg, UINT uMsg,
  1680.                                                                 WPARAM wParam, LPARAM lParam )
  1681. {
  1682.     if( g_pNCW )
  1683.         return g_pNCW->LobbyWaitDlgProc( hDlg, uMsg, wParam, lParam );
  1684.     return FALSE; // Message not handled
  1685. }
  1686. //-----------------------------------------------------------------------------
  1687. // Name: LobbyWaitDlgProc()
  1688. // Desc: Handles messages for the lobby wait status dialog
  1689. //-----------------------------------------------------------------------------
  1690. INT_PTR CALLBACK CNetConnectWizard::LobbyWaitDlgProc( HWND hDlg, UINT msg,
  1691.                                                       WPARAM wParam, LPARAM lParam )
  1692. {
  1693.     UNREFERENCED_PARAMETER( lParam );
  1694.     switch( msg )
  1695.     {
  1696.         case WM_INITDIALOG:
  1697. #if defined(WIN32_PLATFORM_PSPC) && (_WIN32_WCE >= 300)
  1698.             SHINITDLGINFO   shidi;
  1699.             memset(&shidi, 0, sizeof(SHINITDLGINFO));
  1700.             shidi.dwMask = SHIDIM_FLAGS;
  1701.             shidi.dwFlags = SHIDIF_SIPDOWN | SHIDIF_SIZEDLGFULLSCREEN;
  1702.             shidi.hDlg = hDlg;
  1703.             SetForegroundWindow(hDlg);
  1704.             SHInitDialog(&shidi);
  1705. #endif // WIN32_PLATFORM_PSPC
  1706.             // Set a timer to wait for m_hConnectCompleteEvent to be signaled.
  1707.             // This will tell us when DPN_MSGID_CONNECT_COMPLETE has been processed
  1708.             // which lets us know if the connect was successful or not.
  1709.             SetTimer( hDlg, TIMERID_CONNECT_COMPLETE, 100, NULL );
  1710.             SetDlgItemText( hDlg, IDC_WAIT_TEXT, TEXT("Waiting for lobby connection...") );
  1711.             return TRUE;
  1712.         case WM_COMMAND:
  1713.             switch( LOWORD(wParam) )
  1714.             {
  1715.                 case IDCANCEL:
  1716.                     EndDialog( hDlg, IDCANCEL );
  1717.                     return TRUE;
  1718.             }
  1719.             break;
  1720.         case WM_TIMER:
  1721.         {
  1722.             if( wParam == TIMERID_CONNECT_COMPLETE )
  1723.             {
  1724.                 // Wait for a lobby connection.  If this call
  1725.                 // returns WAIT_OBJECT_0 then the DPL_MSGID_CONNECT will
  1726.                 // have already been processed.
  1727.                 DWORD dwResult = WaitForSingleObject( m_hLobbyConnectionEvent, 100 );
  1728.                 if( dwResult != WAIT_TIMEOUT )
  1729.                     EndDialog( hDlg, IDOK );
  1730.             }
  1731.             break;
  1732.         }
  1733.     }
  1734.     return FALSE; // Didn't handle message
  1735. }