//----------------------------------------------------------------------------
// File: LobbyClient.cpp
//
// Desc: 
//
// Copyright (c) 1999-2000 Microsoft Corp. All rights reserved.
//-----------------------------------------------------------------------------
#define STRICT
#include <winsock.h>
#include <windows.h>
#include <basetsd.h>
#include <dplay8.h>
#include <dplobby8.h>
#include <dpaddr.h>
#include <dxerr8.h>
#include <cguid.h>
#include <tchar.h>
#include "DXUtil.h"
#include "resource.h"



//-----------------------------------------------------------------------------
// Defines, and constants
//-----------------------------------------------------------------------------
#define DPLAY_SAMPLE_KEY        TEXT("Software\\Microsoft\\DirectX DirectPlay Samples")
#define WM_APP_APPDISCONNECTED  (WM_APP + 1)
#define WM_APP_SETSTATUS        (WM_APP + 2)




//-----------------------------------------------------------------------------
// Global variables
//-----------------------------------------------------------------------------
IDirectPlay8Peer*  g_pDP                         = NULL;    // DirectPlay peer object
IDirectPlay8LobbyClient* g_pLobbyClient          = NULL;    // DirectPlay lobby client
HINSTANCE          g_hInst                       = NULL;    // HINST of app
HWND               g_hDlg                        = NULL;    // HWND of main dialog
TCHAR              g_strAppName[256]             = TEXT("LobbyClient");
GUID*              g_pCurSPGuid                  = NULL;    // Currently selected guid
TCHAR              g_strPlayerName[MAX_PATH];               // Local player name
TCHAR              g_strSessionName[MAX_PATH];              // Session name
TCHAR              g_strPreferredProvider[MAX_PATH];        // Provider string
TCHAR              g_strLocalIP[MAX_PATH];                  // Provider string




//-----------------------------------------------------------------------------
// Function-prototypes
//-----------------------------------------------------------------------------
HRESULT WINAPI   DirectPlayMessageHandler( PVOID pvUserContext, DWORD dwMessageId, PVOID pMsgBuffer );
HRESULT WINAPI   DirectPlayLobbyMessageHandler( PVOID pvUserContext, DWORD dwMessageId, PVOID pMsgBuffer );
INT_PTR CALLBACK LobbyClientDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam );
HRESULT          InitDirectPlay();
HRESULT          OnInitDialog( HWND hDlg );
VOID             SetupAddressFields( HWND hDlg );
HRESULT          AllocAndInitConnectSettings( HWND hDlg, GUID* pAppGuid, DPL_CONNECTION_SETTINGS** ppdplConnectSettings );
VOID             FreeConnectSettings( DPL_CONNECTION_SETTINGS* pSettings );
HRESULT          LaunchApp( HWND hDlg );
HRESULT          EnumRegisteredApplications( HWND hDlg );
HRESULT          EnumServiceProviders( HWND hDlg );
HRESULT          EnumAdapters( HWND hDlg, GUID* pSPGuid );
HRESULT          SendMsgToApp( HWND hDlg );
HRESULT          DisconnectFromApp( HWND hDlg );







//-----------------------------------------------------------------------------
// Name: WinMain()
// Desc: Entry point for the application.  Since we use a simple dialog for 
//       user interaction we don't need to pump messages.
//-----------------------------------------------------------------------------
INT APIENTRY WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, 
                      LPSTR pCmdLine, INT nCmdShow )
{
    HRESULT hr;
    HKEY    hDPlaySampleRegKey;

    g_hInst = hInst; 

    // Read persistent state information from registry
    RegCreateKeyEx( HKEY_CURRENT_USER, DPLAY_SAMPLE_KEY, 0, NULL,
                    REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, 
                    &hDPlaySampleRegKey, NULL );
    DXUtil_ReadStringRegKey( hDPlaySampleRegKey, TEXT("Player Name"), 
                             g_strPlayerName, MAX_PATH, TEXT("TestPlayer") );
    DXUtil_ReadStringRegKey( hDPlaySampleRegKey, TEXT("Session Name"), 
                             g_strSessionName, MAX_PATH, TEXT("TestGame") );
    DXUtil_ReadStringRegKey( hDPlaySampleRegKey, TEXT("Preferred Provider"), 
                             g_strPreferredProvider, MAX_PATH, 
                             TEXT("DirectPlay8 TCP/IP Service Provider") );

    // Init COM so we can use CoCreateInstance
    CoInitializeEx( NULL, COINIT_MULTITHREADED );

    if( FAILED( hr = InitDirectPlay() ) )
    {
        DXTRACE_ERR( TEXT("InitDirectPlay"), hr );
        MessageBox( NULL, TEXT("Failed initializing IDirectPlay8Peer. ")
                    TEXT("The sample will now quit."),
                    g_strAppName, MB_OK | MB_ICONERROR );
        return FALSE;
    }

    DialogBox( hInst, MAKEINTRESOURCE(IDD_LOBBY_CLIENT), NULL, 
               (DLGPROC) LobbyClientDlgProc );

    if( g_pLobbyClient )
    {
        g_pLobbyClient->Close( 0 );
        SAFE_RELEASE( g_pLobbyClient );
    }

    if( g_pDP )
    {
        g_pDP->Close(0);
        SAFE_RELEASE( g_pDP );
    }

    CoUninitialize();

    // Write information to the registry
    DXUtil_WriteStringRegKey( hDPlaySampleRegKey, TEXT("Player Name"), g_strPlayerName );
    DXUtil_WriteStringRegKey( hDPlaySampleRegKey, TEXT("Session Name"), g_strSessionName );
    DXUtil_WriteStringRegKey( hDPlaySampleRegKey, TEXT("Preferred Provider"), g_strPreferredProvider );

    return TRUE;
}




//-----------------------------------------------------------------------------
// Name: InitDirectPlay()
// Desc: 
//-----------------------------------------------------------------------------
HRESULT InitDirectPlay()
{
    HRESULT hr;

    // Create and init IDirectPlay8Peer
    if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay8Peer, NULL, 
                                       CLSCTX_INPROC_SERVER,
                                       IID_IDirectPlay8Peer, 
                                       (LPVOID*) &g_pDP ) ) )
        return DXTRACE_ERR( TEXT("CoCreateInstance"), hr );

    if( FAILED( hr = g_pDP->Initialize( NULL, DirectPlayMessageHandler, 0 ) ) )
        return DXTRACE_ERR( TEXT("Initialize"), hr );

    // Create and init IDirectPlay8LobbyClient
    if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay8LobbyClient, NULL, 
                                       CLSCTX_INPROC_SERVER,
                                       IID_IDirectPlay8LobbyClient, 
                                       (LPVOID*) &g_pLobbyClient ) ) )
        return DXTRACE_ERR( TEXT("CoCreateInstance"), hr );

    if( FAILED( hr = g_pLobbyClient->Initialize( NULL, DirectPlayLobbyMessageHandler, 0 ) ) )
        return DXTRACE_ERR( TEXT("Initialize"), hr );

    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: LobbyClientDlgProc()
// Desc: Handles dialog messages
//-----------------------------------------------------------------------------
INT_PTR CALLBACK LobbyClientDlgProc( HWND hDlg, UINT msg, 
                                     WPARAM wParam, LPARAM lParam )
{
    HRESULT hr;

    switch( msg ) 
    {
        case WM_INITDIALOG:
        {
            g_hDlg = hDlg;
            if( FAILED( hr = OnInitDialog( hDlg ) ) )
            {
                DXTRACE_ERR( TEXT("OnInitDialog"), hr );
                MessageBox( NULL, TEXT("Failed initializing dialog box. ")
                            TEXT("The sample will now quit."),
                            g_strAppName, MB_OK | MB_ICONERROR );
                EndDialog( hDlg, 0 );
            }
            break;
        }

        case WM_APP_APPDISCONNECTED:
        {
            // This is an app defined msg that keeps the 
            // lobby message handler from blocking on the dialog thread.
            DPNHANDLE hDisconnectId = (DPNHANDLE) lParam;
            HRESULT   hrReason      = (HRESULT) wParam;

            // Remove this connection from the list
            TCHAR strBuffer[200];
            wsprintf( strBuffer, "0x%x", hDisconnectId );
            int nIndex = (int) SendDlgItemMessage( g_hDlg, IDC_ACTIVE_CONNECTIONS, 
                                                   LB_FINDSTRINGEXACT, -1, (LPARAM) strBuffer );
            SendDlgItemMessage( g_hDlg, IDC_ACTIVE_CONNECTIONS, LB_DELETESTRING, nIndex, 0 );

            // Tell the user
            wsprintf( strBuffer, "0x%x was disconnected. Reason: 0x%0.8x", 
                      hDisconnectId, hrReason );
            MessageBox( g_hDlg, strBuffer, g_strAppName, MB_OK );
            break;
        }

        case WM_APP_SETSTATUS:
        {
            // This is an app defined msg that keeps the 
            // lobby message handler from blocking on the dialog thread.
            TCHAR* strBuffer = (TCHAR*) lParam;                     

            if( strBuffer )
            {
                SetDlgItemText( g_hDlg, IDC_STATUS, strBuffer );
                SAFE_DELETE_ARRAY( strBuffer );
            }
            break;
        }

        case WM_COMMAND:
        {
            switch( LOWORD(wParam) )
            {
                case IDC_HOST_SESSION:
                    SetupAddressFields( hDlg );
                    break;

                case IDC_SP_COMBO:
                {
                    // If the pSPGuid changed then re-enum the adapters, and
                    // update the address fields.
                    int nSPIndex = (int) SendDlgItemMessage( hDlg, IDC_SP_COMBO, CB_GETCURSEL, 0, 0 );
                    GUID* pSPGuid = (GUID*) SendDlgItemMessage( hDlg, IDC_SP_COMBO, CB_GETITEMDATA, nSPIndex, 0 );
                    if( pSPGuid != NULL && g_pCurSPGuid != pSPGuid )
                    {
                        g_pCurSPGuid = pSPGuid;
                        SetupAddressFields( hDlg );
                        EnumAdapters( hDlg, pSPGuid );
                    }
                    break;
                }

                case IDC_LAUNCH:
                    if( FAILED( hr = LaunchApp( hDlg ) ) )
                    {
                        DXTRACE_ERR( TEXT("LaunchApp"), hr );
                        MessageBox( NULL, TEXT("Failure trying to launch app. ")
                                    TEXT("The sample will now quit."),
                                    g_strAppName, MB_OK | MB_ICONERROR );
                        EndDialog( hDlg, 0 );
                    }
                    break;

                case IDC_SEND_MSG:
                    // Send a dummy message to a connected app for demo purposes
                    SendMsgToApp( hDlg );
                    break;

                case IDC_DISCONNECT:
                    // Disconnect from an app
                    DisconnectFromApp( hDlg );
                    break;

                case IDCANCEL:
                    EndDialog( hDlg, 0 );
                    return TRUE;
            }
            break;
        }

        case WM_DESTROY:
        {
            GetDlgItemText( hDlg, IDC_PLAYER_NAME, g_strPlayerName, MAX_PATH );
            GetDlgItemText( hDlg, IDC_SESSION_NAME, g_strSessionName, MAX_PATH );
            int nIndex = (int) SendDlgItemMessage( hDlg, IDC_SP_COMBO, CB_GETCURSEL, 0, 0 );
            SendDlgItemMessage( hDlg, IDC_SP_COMBO, CB_GETLBTEXT, nIndex, (LPARAM) g_strPreferredProvider );

            GUID* pGuid;
            int nCount,i;
            nCount = (int)SendDlgItemMessage( hDlg, IDC_APP_LIST, LB_GETCOUNT, 0, 0 );
            for( i=0; i<nCount; i++ )
            {
                pGuid = (LPGUID) SendDlgItemMessage( hDlg, IDC_APP_LIST, LB_GETITEMDATA, i, 0 );
                SAFE_DELETE( pGuid );
            }

            nCount = (int)SendDlgItemMessage( hDlg, IDC_SP_COMBO, CB_GETCOUNT, 0, 0 );
            for( i=0; i<nCount; i++ )
            {
                pGuid = (LPGUID) SendDlgItemMessage( hDlg, IDC_SP_COMBO, CB_GETITEMDATA, i, 0 );
                SAFE_DELETE( pGuid );
            }

            nCount = (int)SendDlgItemMessage( hDlg, IDC_ADAPTER_COMBO, CB_GETCOUNT, 0, 0 );
            for( i=0; i<nCount; i++ )
            {
                pGuid = (LPGUID) SendDlgItemMessage( hDlg, IDC_ADAPTER_COMBO, CB_GETITEMDATA, i, 0 );
                SAFE_DELETE( pGuid );
            }

            break;
        }
    }

    return FALSE; // Didn't handle message
}




//-----------------------------------------------------------------------------
// Name: OnInitDialog
// Desc: 
//-----------------------------------------------------------------------------
HRESULT OnInitDialog( HWND hDlg )
{
    HRESULT hr;

    // Load and set the icon
    HICON hIcon = LoadIcon( g_hInst, MAKEINTRESOURCE( IDI_MAIN ) );
    SendMessage( hDlg, WM_SETICON, ICON_BIG,   (LPARAM) hIcon );  // Set big icon
    SendMessage( hDlg, WM_SETICON, ICON_SMALL, (LPARAM) hIcon );  // Set small icon

    CheckDlgButton( hDlg, IDC_HOST_SESSION, BST_CHECKED );
    CheckRadioButton( hDlg, IDC_LAUNCH_NEW, IDC_DONT_LAUNCH, IDC_LAUNCH_NOT_FOUND );

    SetDlgItemText( hDlg, IDC_PLAYER_NAME, g_strPlayerName );
    SetDlgItemText( hDlg, IDC_SESSION_NAME, g_strSessionName );

    if( FAILED( hr = EnumRegisteredApplications( hDlg ) ) )
        return DXTRACE_ERR( TEXT("EnumRegisteredApplications"), hr );

    if( FAILED( hr = EnumServiceProviders( hDlg ) ) )
        return DXTRACE_ERR( TEXT("EnumServiceProviders"), hr );
    
    WSADATA WSAData;
    _tcscpy( g_strLocalIP, TEXT("") );
    if( WSAStartup (MAKEWORD(1,0), &WSAData) == 0) 
    {
        CHAR strLocalHostName[MAX_PATH];
        gethostname( strLocalHostName, MAX_PATH );
        HOSTENT* pHostEnt = gethostbyname( strLocalHostName );
        if( pHostEnt )
        {
            in_addr* pInAddr = (in_addr*) pHostEnt->h_addr_list[0];
            char* strLocalIP = inet_ntoa( *pInAddr );
            if( strLocalIP )
                DXUtil_ConvertAnsiStringToGeneric( g_strLocalIP, strLocalIP );
        }

        WSACleanup();
    }
    
    SetupAddressFields( hDlg );

    int nSPIndex = (int) SendDlgItemMessage( hDlg, IDC_SP_COMBO, CB_GETCURSEL, 0, 0 );
    GUID* pSPGuid = (GUID*) SendDlgItemMessage( hDlg, IDC_SP_COMBO, CB_GETITEMDATA, nSPIndex, 0 );
    if( pSPGuid != NULL )
    {
        g_pCurSPGuid = pSPGuid;
        EnumAdapters( hDlg, pSPGuid );
    }

    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: SetupAddressFields
// Desc: Based on the SP selected, update the address UI 
//-----------------------------------------------------------------------------
VOID SetupAddressFields( HWND hDlg )
{
    int nSPIndex = (int) SendDlgItemMessage( hDlg, IDC_SP_COMBO, CB_GETCURSEL, 0, 0 );
    if( nSPIndex == LB_ERR )
        return;
    GUID* pGuid = (GUID*) SendDlgItemMessage( hDlg, IDC_SP_COMBO, CB_GETITEMDATA, nSPIndex, 0 );
    if( pGuid == NULL )
        return;

    BOOL bHosting = IsDlgButtonChecked( hDlg, IDC_HOST_SESSION );

    if( *pGuid == CLSID_DP8SP_TCPIP )
    {
        EnableWindow( GetDlgItem(hDlg, IDC_ADDRESS_LINE2), TRUE );
        SetDlgItemText( hDlg, IDC_ADDRESS_LINE2, TEXT("2323") );
        EnableWindow( GetDlgItem(hDlg, IDC_ADDRESS_LINE2_TEXT), TRUE );
        SetDlgItemText( hDlg, IDC_ADDRESS_LINE2_TEXT, TEXT("Port:") );

        if( bHosting )
        {
            EnableWindow( GetDlgItem(hDlg, IDC_ADDRESS_LINE1), FALSE );
            SetDlgItemText( hDlg, IDC_ADDRESS_LINE1, TEXT("") );
            EnableWindow( GetDlgItem(hDlg, IDC_ADDRESS_LINE1_TEXT), FALSE );
            SetDlgItemText( hDlg, IDC_ADDRESS_LINE1_TEXT, TEXT("") );
        }
        else
        {
            EnableWindow( GetDlgItem(hDlg, IDC_ADDRESS_LINE1), TRUE );
            SetDlgItemText( hDlg, IDC_ADDRESS_LINE1, g_strLocalIP );
            EnableWindow( GetDlgItem(hDlg, IDC_ADDRESS_LINE1_TEXT), TRUE );
            SetDlgItemText( hDlg, IDC_ADDRESS_LINE1_TEXT, TEXT("IP Address:") );
        }
    }
    else if( *pGuid == CLSID_DP8SP_MODEM )
    {
        EnableWindow( GetDlgItem(hDlg, IDC_ADDRESS_LINE1), TRUE );
        SetDlgItemText( hDlg, IDC_ADDRESS_LINE1, TEXT("") );
        EnableWindow( GetDlgItem(hDlg, IDC_ADDRESS_LINE1_TEXT), TRUE );
        SetDlgItemText( hDlg, IDC_ADDRESS_LINE1_TEXT, TEXT("Phone Number:") );

        EnableWindow( GetDlgItem(hDlg, IDC_ADDRESS_LINE2), FALSE );
        SetDlgItemText( hDlg, IDC_ADDRESS_LINE2, TEXT("") );
        EnableWindow( GetDlgItem(hDlg, IDC_ADDRESS_LINE2_TEXT), FALSE );
        SetDlgItemText( hDlg, IDC_ADDRESS_LINE2_TEXT, TEXT("") );
    }
    else 
    {
        // pGuid is CLSID_DP8SP_IPX or CLSID_DP8SP_SERIAL or unknown 
        // so disable all the address lines 
        EnableWindow( GetDlgItem(hDlg, IDC_ADDRESS_LINE1), FALSE );
        SetDlgItemText( hDlg, IDC_ADDRESS_LINE1, TEXT("") );
        EnableWindow( GetDlgItem(hDlg, IDC_ADDRESS_LINE1_TEXT), FALSE );
        SetDlgItemText( hDlg, IDC_ADDRESS_LINE1_TEXT, TEXT("") );
        EnableWindow( GetDlgItem(hDlg, IDC_ADDRESS_LINE2), FALSE );
        SetDlgItemText( hDlg, IDC_ADDRESS_LINE2, TEXT("") );
        EnableWindow( GetDlgItem(hDlg, IDC_ADDRESS_LINE2_TEXT), FALSE );
        SetDlgItemText( hDlg, IDC_ADDRESS_LINE2_TEXT, TEXT("") );
    }
}




//-----------------------------------------------------------------------------
// Name: EnumRegisteredApplications
// Desc: Enum all the lobby launchable apps and put them in the listbox
//-----------------------------------------------------------------------------
HRESULT EnumRegisteredApplications( HWND hDlg )
{
    HRESULT hr;
    DWORD   dwSize     = 0;
    DWORD   dwPrograms = 0;
    DWORD   iProgram;
    BYTE*   pData   = NULL;

    hr = g_pLobbyClient->EnumLocalPrograms( NULL, pData, &dwSize, &dwPrograms, 0 );
    if( hr != DPNERR_BUFFERTOOSMALL && FAILED(hr) )
        return DXTRACE_ERR( TEXT("EnumLocalPrograms"), hr );

    if( dwSize == 0 )
    {
        MessageBox( NULL, TEXT("There are no applications registered as lobby launchable. ")
                    TEXT("The sample will now quit."),
                    g_strAppName, MB_OK | MB_ICONERROR );
        EndDialog( hDlg, 0 );
        return S_OK;
    }

    pData = new BYTE[dwSize];
    if( FAILED( hr = g_pLobbyClient->EnumLocalPrograms( NULL, pData, &dwSize, &dwPrograms, 0 ) ) )
        return DXTRACE_ERR( TEXT("EnumLocalPrograms"), hr );

    DPL_APPLICATION_INFO* pAppInfo = (DPL_APPLICATION_INFO*) pData;
    for( iProgram=0; iProgram<dwPrograms; iProgram++ )
    {
        TCHAR strAppName[MAX_PATH];
        DXUtil_ConvertWideStringToGeneric( strAppName, pAppInfo->pwszApplicationName );

        // Add the name to the listbox
        int nIndex = (int) SendDlgItemMessage( hDlg, IDC_APP_LIST, LB_ADDSTRING, 0, (LPARAM) strAppName );

        // Store the guid in the listbox
        GUID* pGuid = new GUID;
        memcpy( pGuid, &pAppInfo->guidApplication, sizeof(GUID) );
        SendDlgItemMessage( hDlg, IDC_APP_LIST, LB_SETITEMDATA, nIndex, (LPARAM) pGuid );

        pAppInfo++;
    }

    SAFE_DELETE_ARRAY( pData );

    SendDlgItemMessage( hDlg, IDC_APP_LIST, LB_SETCURSEL, 0, 0 );

    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: EnumServiceProviders()
// Desc: Fills the combobox with service providers
//-----------------------------------------------------------------------------
HRESULT EnumServiceProviders( HWND hDlg )
{
    DPN_SERVICE_PROVIDER_INFO* pdnSPInfo = NULL;
    HRESULT hr;
    DWORD   dwItems = 0;
    DWORD   dwSize  = 0;
    int     nIndex;

    // Enumerate all DirectPlay service providers, and store them in the listbox
    hr = g_pDP->EnumServiceProviders( NULL, NULL, pdnSPInfo, &dwSize,
                                      &dwItems, 0 );
    if( hr != DPNERR_BUFFERTOOSMALL )
        return DXTRACE_ERR( TEXT("EnumServiceProviders"), hr );

    pdnSPInfo = (DPN_SERVICE_PROVIDER_INFO*) new BYTE[dwSize];
    if( FAILED( hr = g_pDP->EnumServiceProviders( NULL, NULL, pdnSPInfo,
                                                  &dwSize, &dwItems, 0 ) ) )
        return DXTRACE_ERR( TEXT("EnumServiceProviders"), hr );

    DPN_SERVICE_PROVIDER_INFO* pdnSPInfoEnum = pdnSPInfo;
    for ( DWORD i = 0; i < dwItems; i++ )
    {
        TCHAR strName[MAX_PATH];
        DXUtil_ConvertWideStringToGeneric( strName, pdnSPInfoEnum->pwszName );

        // Found a service provider, so put it in the listbox
        nIndex = (int)SendDlgItemMessage( hDlg, IDC_SP_COMBO, CB_ADDSTRING, 
                                              0, (LPARAM) strName );

        // Store pointer to GUID in listbox
        GUID* pGuid = new GUID;
        memcpy( pGuid, &pdnSPInfoEnum->guid, sizeof(GUID) );
        SendDlgItemMessage( hDlg, IDC_SP_COMBO, CB_SETITEMDATA, 
                            nIndex, (LPARAM) pGuid );

        pdnSPInfoEnum++;
    }

    SAFE_DELETE_ARRAY( pdnSPInfo );

    // Try to select the default preferred provider
    nIndex = (int)SendDlgItemMessage( hDlg, IDC_SP_COMBO, CB_FINDSTRINGEXACT, (WPARAM)-1,
                                      (LPARAM)g_strPreferredProvider );
    if( nIndex != LB_ERR )
        SendDlgItemMessage( hDlg, IDC_SP_COMBO, CB_SETCURSEL, nIndex, 0 );
    else
        SendDlgItemMessage( hDlg, IDC_SP_COMBO, CB_SETCURSEL, 0, 0 );

    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: EnumAdapters()
// Desc: Fills the combobox with adapters for a specified SP
//-----------------------------------------------------------------------------
HRESULT EnumAdapters( HWND hDlg, GUID* pSPGuid )
{
    DPN_SERVICE_PROVIDER_INFO* pdnSPInfo = NULL;
    TCHAR   strName[MAX_PATH];
    HRESULT hr;
    DWORD   dwItems = 0;
    DWORD   dwSize  = 0;
    int     nIndex;
    int     nAllAdaptersIndex = 0;

    SendDlgItemMessage( hDlg, IDC_ADAPTER_COMBO, CB_RESETCONTENT, 0, 0 );

    // Enumerate all DirectPlay service providers, and store them in the listbox
    hr = g_pDP->EnumServiceProviders( pSPGuid, NULL, pdnSPInfo, &dwSize,
                                      &dwItems, 0 );
    if( hr != DPNERR_BUFFERTOOSMALL )
        return DXTRACE_ERR( TEXT("EnumServiceProviders"), hr );

    pdnSPInfo = (DPN_SERVICE_PROVIDER_INFO*) new BYTE[dwSize];
    if( FAILED( hr = g_pDP->EnumServiceProviders( pSPGuid, NULL, pdnSPInfo,
                                                  &dwSize, &dwItems, 0 ) ) )
        return DXTRACE_ERR( TEXT("EnumServiceProviders"), hr );

    DPN_SERVICE_PROVIDER_INFO* pdnSPInfoEnum = pdnSPInfo;
    for ( DWORD i = 0; i < dwItems; i++ )
    {
        DXUtil_ConvertWideStringToGeneric( strName, pdnSPInfoEnum->pwszName );

        // Found a service provider, so put it in the listbox
        nIndex = (int)SendDlgItemMessage( hDlg, IDC_ADAPTER_COMBO, CB_ADDSTRING, 
                                          0, (LPARAM) strName );

        if( _tcscmp( strName, "All Adapters" ) == 0 )
            nAllAdaptersIndex = nIndex;

        // Store pointer to GUID in listbox
        GUID* pGuid = new GUID;
        memcpy( pGuid, &pdnSPInfoEnum->guid, sizeof(GUID) );

        SendDlgItemMessage( hDlg, IDC_ADAPTER_COMBO, CB_SETITEMDATA, 
                            nIndex, (LPARAM) pGuid );

        pdnSPInfoEnum++;
    }

    SAFE_DELETE_ARRAY( pdnSPInfo );

    SendDlgItemMessage( hDlg, IDC_ADAPTER_COMBO, CB_SETCURSEL, nAllAdaptersIndex, 0 );

    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: LaunchApp
// Desc: Launch the selected app based on the UI settings
//-----------------------------------------------------------------------------
HRESULT LaunchApp( HWND hDlg )
{
    HRESULT   hr;
    DPNHANDLE hApplication = NULL;

    BOOL bLaunchNew      = IsDlgButtonChecked( hDlg, IDC_LAUNCH_NEW );
    BOOL bLaunchNotFound = IsDlgButtonChecked( hDlg, IDC_LAUNCH_NOT_FOUND );

    int nAppIndex = (int) SendDlgItemMessage( hDlg, IDC_APP_LIST, LB_GETCURSEL, 0, 0 );
    GUID* pAppGuid = (GUID*) SendDlgItemMessage( hDlg, IDC_APP_LIST, LB_GETITEMDATA, nAppIndex, 0 );
    if( nAppIndex == LB_ERR || pAppGuid == NULL )
        return E_INVALIDARG;

    // Setup the DPL_CONNECT_INFO struct
    DPL_CONNECT_INFO dnConnectInfo;
    ZeroMemory( &dnConnectInfo, sizeof(DPL_CONNECT_INFO) );
    dnConnectInfo.dwSize = sizeof(DPL_CONNECT_INFO);
    dnConnectInfo.pvLobbyConnectData = NULL;
    dnConnectInfo.dwLobbyConnectDataSize = 0;
    dnConnectInfo.dwFlags = 0;
    if( bLaunchNew )
        dnConnectInfo.dwFlags |= DPLCONNECT_LAUNCHNEW;
    if( bLaunchNotFound )
        dnConnectInfo.dwFlags |= DPLCONNECT_LAUNCHNOTFOUND;
    dnConnectInfo.guidApplication = *pAppGuid;
    if( FAILED( hr = AllocAndInitConnectSettings( hDlg, pAppGuid, &dnConnectInfo.pdplConnectionSettings ) ) ) 
        return S_FALSE;

    hr = g_pLobbyClient->ConnectApplication( &dnConnectInfo, NULL, &hApplication, 
                                             INFINITE, 0 );
    if( FAILED(hr) )
    {
        if( hr == DPNERR_NOCONNECTION && !bLaunchNew && !bLaunchNotFound )
        {
            MessageBox( NULL, TEXT("There was no waiting application. "),
                        g_strAppName, MB_OK | MB_ICONERROR );
        }
        else
        {
            return DXTRACE_ERR( TEXT("ConnectApplication"), hr );
        }
    }
    else
    {
        TCHAR strBuffer[20];
        wsprintf( strBuffer, "0x%x", hApplication );
        int nIndex = (int) SendDlgItemMessage( hDlg, IDC_ACTIVE_CONNECTIONS, LB_ADDSTRING, 
                                         0, (LPARAM) strBuffer );
        SendDlgItemMessage( hDlg, IDC_ACTIVE_CONNECTIONS, LB_SETITEMDATA, 
                            nIndex, (LPARAM) hApplication );

        if( LB_ERR == SendDlgItemMessage( hDlg, IDC_ACTIVE_CONNECTIONS, LB_GETCURSEL, 0, 0 ) )
            SendDlgItemMessage( hDlg, IDC_ACTIVE_CONNECTIONS, LB_SETCURSEL, 0, 0 );
    }

    FreeConnectSettings( dnConnectInfo.pdplConnectionSettings );

    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: AllocAndInitConnectSettings
// Desc: Alloc and fill up a DPL_CONNECTION_SETTINGS.  Call FreeConnectSettings
//       later to free it.
//-----------------------------------------------------------------------------
HRESULT AllocAndInitConnectSettings( HWND hDlg, GUID* pAppGuid, 
                                     DPL_CONNECTION_SETTINGS** ppdplConnectSettings ) 
{
    HRESULT hr;

    BOOL bHosting    = IsDlgButtonChecked( hDlg, IDC_HOST_SESSION );
    BOOL bNoSettings = IsDlgButtonChecked( hDlg, IDC_NO_SETTINGS );
    IDirectPlay8Address* pHostAddress   = NULL;
    IDirectPlay8Address* pDeviceAddress = NULL;

    if( bNoSettings )
    {
        *ppdplConnectSettings = NULL;
        return S_OK;
    }

    int nSPIndex = (int) SendDlgItemMessage( hDlg, IDC_SP_COMBO, CB_GETCURSEL, 0, 0 );
    GUID* pSPGuid = (GUID*) SendDlgItemMessage( hDlg, IDC_SP_COMBO, CB_GETITEMDATA, nSPIndex, 0 );

    if( *pSPGuid == CLSID_DP8SP_IPX && !bHosting )
    {
        MessageBox( hDlg, "A typical lobby client would have sent a complete IPX address, this " \
                          "lobby client does not support this. ", g_strAppName, MB_OK );
        return E_NOTIMPL;
    }

    if( !bHosting )
    {
        // Create a host address if connecting to a host, 
        // otherwise keep it as NULL
        if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay8Address, NULL, CLSCTX_INPROC_SERVER, 
                                           IID_IDirectPlay8Address, (void **) &pHostAddress ) ) )
            return DXTRACE_ERR( TEXT("CoCreateInstance"), hr );

        // Set the SP to pHostAddress
        if( FAILED( hr = pHostAddress->SetSP( pSPGuid ) ) )
            return DXTRACE_ERR( TEXT("SetSP"), hr );
    }

    // Create a device address to specify which device we are using 
    if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay8Address, NULL, CLSCTX_INPROC_SERVER, 
                                       IID_IDirectPlay8Address, (void **) &pDeviceAddress ) ) )
        return DXTRACE_ERR( TEXT("CoCreateInstance"), hr );

    // Set the SP to pDeviceAddress
    if( FAILED( hr = pDeviceAddress->SetSP( pSPGuid ) ) )
        return DXTRACE_ERR( TEXT("SetSP"), hr );

    // Add the adapter to pHostAddress
    int nAdapterIndex = (int) SendDlgItemMessage( hDlg, IDC_ADAPTER_COMBO, CB_GETCURSEL, 0, 0 );
    GUID* pAdapterGuid = (GUID*) SendDlgItemMessage( hDlg, IDC_ADAPTER_COMBO, CB_GETITEMDATA, 
                                                     nAdapterIndex, 0 );
    if( FAILED( hr = pDeviceAddress->SetDevice( pAdapterGuid ) ) )
        return DXTRACE_ERR( TEXT("SetDevice"), hr );

    if( *pSPGuid == CLSID_DP8SP_TCPIP )
    {
        TCHAR strIP[MAX_PATH];
        TCHAR strPort[MAX_PATH];

        GetDlgItemText( hDlg, IDC_ADDRESS_LINE1, strIP, MAX_PATH );
        GetDlgItemText( hDlg, IDC_ADDRESS_LINE2, strPort, MAX_PATH );

        if( bHosting )
        {
            // Add the port to pDeviceAddress
            DWORD dwPort = atoi( strPort );
            if( FAILED( hr = pDeviceAddress->AddComponent( DPNA_KEY_PORT, 
                                                           &dwPort, sizeof(dwPort),
                                                           DPNA_DATATYPE_DWORD ) ) )
                return DXTRACE_ERR( TEXT("AddComponent"), hr );
        }
        else
        {
            // Add the IP address to pHostAddress
            if( _tcslen( strIP ) > 0 )
            {
                WCHAR wstrIP[MAX_PATH];
                DXUtil_ConvertGenericStringToWide( wstrIP, strIP );

                if( FAILED( hr = pHostAddress->AddComponent( DPNA_KEY_HOSTNAME, 
                                                             wstrIP, (wcslen(wstrIP)+1)*sizeof(WCHAR), 
                                                             DPNA_DATATYPE_STRING ) ) )
                    return DXTRACE_ERR( TEXT("AddComponent"), hr );
            }

            // Add the port to pHostAddress
            DWORD dwPort = atoi( strPort );
            if( FAILED( hr = pHostAddress->AddComponent( DPNA_KEY_PORT, 
                                                         &dwPort, sizeof(dwPort),
                                                         DPNA_DATATYPE_DWORD ) ) )
                return DXTRACE_ERR( TEXT("AddComponent"), hr );
        }
    }
    else if( *pSPGuid == CLSID_DP8SP_MODEM )
    {
        TCHAR strPhone[MAX_PATH];
        GetDlgItemText( hDlg, IDC_ADDRESS_LINE1, strPhone, MAX_PATH );

        if( !bHosting )
        {
            // Add the phonenumber to pHostAddress
            if( _tcslen( strPhone ) > 0 )
            {
                WCHAR wstrPhone[MAX_PATH];
                DXUtil_ConvertGenericStringToWide( wstrPhone, strPhone );

                if( FAILED( hr = pHostAddress->AddComponent( DPNA_KEY_PHONENUMBER, 
                                                             wstrPhone, (wcslen(wstrPhone)+1)*sizeof(WCHAR), 
                                                             DPNA_DATATYPE_STRING ) ) )
                    return DXTRACE_ERR( TEXT("AddComponent"), hr );
            }
        }
    }
    else if( *pSPGuid == CLSID_DP8SP_SERIAL )
    {
        // This simple client doesn't have UI to query for the various
        // fields needed for the serial.  So we just let DPlay popup a dialog
        // to ask the user which settings are needed.
    }
    else
    {
        // Unknown SP, so leave as is
    }

    // Setup the DPL_CONNECTION_SETTINGS
    DPL_CONNECTION_SETTINGS* pSettings = new DPL_CONNECTION_SETTINGS;
    ZeroMemory( pSettings, sizeof(DPL_CONNECTION_SETTINGS) );
    pSettings->dwSize = sizeof(DPL_CONNECTION_SETTINGS);
    pSettings->dpnAppDesc.dwSize = sizeof(DPN_APPLICATION_DESC);
    pSettings->dwFlags = 0;
    if( bHosting )
        pSettings->dwFlags |= DPLCONNECTSETTINGS_HOST;
    pSettings->dpnAppDesc.guidApplication = *pAppGuid;
    pSettings->dpnAppDesc.guidInstance    = GUID_NULL;
    pSettings->pdp8HostAddress = pHostAddress;
    pSettings->ppdp8DeviceAddresses = new IDirectPlay8Address*;
    pSettings->ppdp8DeviceAddresses[0] = pDeviceAddress;
    pSettings->cNumDeviceAddresses = 1;

    // Set the pSettings->dpnAppDesc.pwszSessionName
    TCHAR strSessionName[MAX_PATH];
    GetDlgItemText( hDlg, IDC_SESSION_NAME, strSessionName, MAX_PATH );
    if( _tcslen( strSessionName ) == 0 )
    {
        pSettings->dpnAppDesc.pwszSessionName = NULL;
    }
    else
    {
        WCHAR wstrSessionName[MAX_PATH];
        DXUtil_ConvertGenericStringToWide( wstrSessionName, strSessionName );
        pSettings->dpnAppDesc.pwszSessionName = new WCHAR[wcslen(wstrSessionName)+1];
        wcscpy( pSettings->dpnAppDesc.pwszSessionName, wstrSessionName );
    }

    // Set the pSettings->pwszPlayerName
    TCHAR strPlayerName[MAX_PATH];
    GetDlgItemText( hDlg, IDC_PLAYER_NAME, strPlayerName, MAX_PATH );
    if( _tcslen( strPlayerName ) == 0 )
    {
        pSettings->pwszPlayerName = NULL;
    }
    else
    {
        WCHAR wstrPlayerName[MAX_PATH];
        DXUtil_ConvertGenericStringToWide( wstrPlayerName, strPlayerName );
        pSettings->pwszPlayerName = new WCHAR[wcslen(wstrPlayerName)+1];
        wcscpy( pSettings->pwszPlayerName, wstrPlayerName );
    }

    *ppdplConnectSettings = pSettings;

    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: FreeConnectSettings
// Desc: Releases everything involved in a DPL_CONNECTION_SETTINGS struct
//-----------------------------------------------------------------------------
VOID FreeConnectSettings( DPL_CONNECTION_SETTINGS* pSettings )
{
    if( !pSettings )
        return;

    SAFE_DELETE_ARRAY( pSettings->pwszPlayerName ); 
    SAFE_DELETE_ARRAY( pSettings->dpnAppDesc.pwszSessionName );
    SAFE_DELETE_ARRAY( pSettings->dpnAppDesc.pwszPassword );
    SAFE_DELETE_ARRAY( pSettings->dpnAppDesc.pvReservedData );
    SAFE_DELETE_ARRAY( pSettings->dpnAppDesc.pvApplicationReservedData );
    SAFE_RELEASE( pSettings->pdp8HostAddress );
    SAFE_RELEASE( pSettings->ppdp8DeviceAddresses[0] );
    SAFE_DELETE_ARRAY( pSettings->ppdp8DeviceAddresses );
    SAFE_DELETE( pSettings );
}




//-----------------------------------------------------------------------------
// Name: DirectPlayMessageHandler
// Desc: Handler for DirectPlay messages.  This function is called by
//       the DirectPlay message handler pool of threads, so be care of thread
//       synchronization problems with shared memory
//-----------------------------------------------------------------------------
HRESULT WINAPI DirectPlayMessageHandler( PVOID pvUserContext, 
                                         DWORD dwMessageId, 
                                         PVOID pMsgBuffer )
{
    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: DirectPlayLobbyMessageHandler
// Desc: Handler for DirectPlay lobby messages.  This function is called by
//       the DirectPlay lobby message handler pool of threads, so be careful of 
//       thread synchronization problems with shared memory
//-----------------------------------------------------------------------------
HRESULT WINAPI DirectPlayLobbyMessageHandler( PVOID pvUserContext, 
                                              DWORD dwMessageId, 
                                              PVOID pMsgBuffer )
{
    switch( dwMessageId )
    {
        case DPL_MSGID_DISCONNECT:
        {
            PDPL_MESSAGE_DISCONNECT pDisconnectMsg;
            pDisconnectMsg = (PDPL_MESSAGE_DISCONNECT)pMsgBuffer;

            // We should free any data associated with the 
            // app here, but there is none.

            // Tell the update the UI so show that a application was disconnected
            // Note: The lobby handler must not become blocked on the dialog thread, 
            // since the dialog thread will be blocked on lobby handler thread
            // when it calls ConnectApplication().  So to avoid blocking on
            // the dialog thread, we'll post a message to the dialog thread.
            PostMessage( g_hDlg, WM_APP_APPDISCONNECTED, 0, pDisconnectMsg->hDisconnectId );            
            break;
        }

        case DPL_MSGID_RECEIVE:
        {
            PDPL_MESSAGE_RECEIVE pReceiveMsg;
            pReceiveMsg = (PDPL_MESSAGE_RECEIVE)pMsgBuffer;

            // The lobby app sent us data.  This sample doesn't
            // expected data from the app, but it is useful 
            // for more complex lclients.
            break;
        }

        case DPL_MSGID_SESSION_STATUS:
        {
            PDPL_MESSAGE_SESSION_STATUS pStatusMsg;
            pStatusMsg = (PDPL_MESSAGE_SESSION_STATUS)pMsgBuffer;

            TCHAR* strBuffer = new TCHAR[200];
            wsprintf( strBuffer, "0x%x: ", pStatusMsg->hSender );
            switch( pStatusMsg->dwStatus )
            {
                case DPLSESSION_CONNECTED:
                    _tcscat( strBuffer, TEXT("Session connected") ); break;
                case DPLSESSION_COULDNOTCONNECT:
                    _tcscat( strBuffer, TEXT("Session could not connect") ); break;
                case DPLSESSION_DISCONNECTED:
                    _tcscat( strBuffer, TEXT("Session disconnected") ); break;
                case DPLSESSION_TERMINATED:
                    _tcscat( strBuffer, TEXT("Session terminated") ); break;
                case DPLSESSION_HOSTMIGRATED:
                	_tcscat( strBuffer, TEXT("Host migrated") ); break;
                case DPLSESSION_HOSTMIGRATEDHERE:
                	_tcscat( strBuffer, TEXT("Host migrated to this client") ); break;

                default:
                {
                    TCHAR strStatus[30];
                    wsprintf( strStatus, TEXT("%d"), pStatusMsg->dwStatus );
                    _tcscat( strBuffer, strStatus );
                    break;
                }
            }

            // Tell the update the UI so show that a application status changed
            // Note: The lobby handler must not become blocked on the dialog thread, 
            // since the dialog thread will be blocked on lobby handler thread
            // when it calls ConnectApplication().  So to avoid blocking on
            // the dialog thread, we'll post a message to the dialog thread.
            PostMessage( g_hDlg, WM_APP_SETSTATUS, 0, (LPARAM) strBuffer );            
            break;
        }

        case DPL_MSGID_CONNECTION_SETTINGS:
        {
            PDPL_MESSAGE_CONNECTION_SETTINGS pConnectionStatusMsg;
            pConnectionStatusMsg = (PDPL_MESSAGE_CONNECTION_SETTINGS)pMsgBuffer;

            // The app has changed the connection settings.  
            // This simple client doesn't handle this, but more complex clients may
            // want to.
            break;
        }
    }

    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: SendMsgToApp
// Desc: Send a dummy message to a connected app for demo purposes 
//-----------------------------------------------------------------------------
HRESULT SendMsgToApp( HWND hDlg )
{
    TCHAR strBuffer[MAX_PATH];
    HRESULT hr;

    int nConnectIndex = (int) SendDlgItemMessage( hDlg, IDC_ACTIVE_CONNECTIONS, LB_GETCURSEL, 0, 0 );
    if( nConnectIndex == LB_ERR )
        return S_OK;

    DPNHANDLE hApplication = (DPNHANDLE) SendDlgItemMessage( hDlg, IDC_ACTIVE_CONNECTIONS, 
                                                             LB_GETITEMDATA, nConnectIndex, 0 );

    // For demonstration purposes, just send a buffer to the app.  This can be used
    // by more complex lobby clients to pass custom information to apps that which
    // can then recieve and process it.
    BYTE buffer[20];
    memset( buffer, 0x03, 20 );
    if( FAILED( hr = g_pLobbyClient->Send( hApplication, buffer, 20, 0 ) ) )
    {
        DXTRACE_ERR( TEXT("Send"), hr );
        wsprintf( strBuffer, TEXT("Failure trying to send message to 0x%0.8x."), hApplication );
        MessageBox( NULL, strBuffer, g_strAppName, MB_OK | MB_ICONERROR );
    }
    else
    {
        wsprintf( strBuffer, TEXT("Successfully sent a message to 0x%0.8x."), hApplication );
        MessageBox( NULL, strBuffer, g_strAppName, MB_OK | MB_ICONERROR );
    }

    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: DisconnectFromApp
// Desc: Disconnect from an app
//-----------------------------------------------------------------------------
HRESULT DisconnectFromApp( HWND hDlg )
{
    HRESULT hr;

    int nConnectIndex = (int) SendDlgItemMessage( hDlg, IDC_ACTIVE_CONNECTIONS, LB_GETCURSEL, 0, 0 );
    if( nConnectIndex == LB_ERR )
        return S_OK;

    DPNHANDLE hApplication = (DPNHANDLE) SendDlgItemMessage( hDlg, IDC_ACTIVE_CONNECTIONS, 
                                                             LB_GETITEMDATA, nConnectIndex, 0 );

    if( FAILED( hr = g_pLobbyClient->ReleaseApplication( hApplication, 0 ) ) )
    {
        DXTRACE_ERR( TEXT("LaunchApp"), hr );
        MessageBox( NULL, TEXT("Failure trying to disconnect from app. "), 
                    g_strAppName, MB_OK | MB_ICONERROR );
    }

    SendDlgItemMessage( hDlg, IDC_ACTIVE_CONNECTIONS, LB_DELETESTRING, nConnectIndex, 0 );

    return S_OK;
}

