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


//----------------------------------------------------------------------------- 
// File: SessionInfo.cpp 
// 
// Desc: Implemenation for the CSessionInfo utility class. This utility stores 
//       player, group, and message information gathered from the application's 
//       DirectPlay message handler, and provides a dialog UI to display the 
//       data. 
// 
//       In order to use this class, simply create an instance using a 
//       pointer to the DirectPlay Peer, Client, or Server interface your  
//       application uses. Add a call to the MessageHandler member function at  
//       the beginning of your application's message handler, and call  
//       ShowDialog to launch the UI.  
// 
//       This class supports multiple concurrent modeless dialogs to help with 
//       debugging an application during runtime.   
// 
// Copyright (C) 2000-2001 Microsoft Corporation. All Rights Reserved. 
//----------------------------------------------------------------------------- 
#include "sessioninfo.h" 
 
 
// Global variables 
CSessionInfo* g_pSI = NULL;    // Global instance pointer 
  
// Custom fonts 
#ifdef UNDER_CE 
LOGFONT g_lfName =       { 20, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS, TEXT("MS Sans Serif") }; 
LOGFONT g_lfConnection = { 14, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN, TEXT("Courier New") }; 
#else 
LOGFONT g_lfName =       { 24, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_TT_ONLY_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, VARIABLE_PITCH | FF_SWISS, TEXT("MS Sans Serif") }; 
LOGFONT g_lfConnection = { 14, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN, TEXT("Courier New") }; 
#endif 
 
 
 
//----------------------------------------------------------------------------- 
// Name: CSIGroup() 
// Desc: Constructor 
//----------------------------------------------------------------------------- 
CSIGroup::CSIGroup( DPNID dpnid ) 
{ 
    id = dpnid; 
    lstrcpy( strName, TEXT("") ); 
    pMembers = new CArrayList( AL_VALUE, sizeof(DPNID) ); 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: ~CSIGroup() 
// Desc: Destructor 
//----------------------------------------------------------------------------- 
CSIGroup::~CSIGroup() 
{ 
    SAFE_DELETE( pMembers ); 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: CSIPlayer() 
// Desc: Constructor 
//----------------------------------------------------------------------------- 
CSIPlayer::CSIPlayer( DPNID dpnid )  
{ 
    id = dpnid; 
    bIsHost = FALSE; 
    lstrcpy( strName, TEXT("") ); 
    lstrcpy( strURL, TEXT("") ); 
} 
 
 
 
//----------------------------------------------------------------------------- 
// Name: AddMember() 
// Desc: Adds the given player ID to the list of member players in this group 
//----------------------------------------------------------------------------- 
HRESULT CSIGroup::AddMember( DPNID id ) 
{ 
    // Check to see if this Member is already present 
    if( IsMember( id ) ) 
        return S_OK; 
 
    // Add the new player id 
    return pMembers->Add( &id ); 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: RemoveMember 
// Desc: Removes the given player ID from the list of member players in this 
//       group 
//----------------------------------------------------------------------------- 
HRESULT CSIGroup::RemoveMember( DPNID id ) 
{ 
    // Find the Member 
    for( UINT i=0; i < pMembers->Count(); i++ ) 
    { 
        DPNID* pID = (DPNID*) pMembers->GetPtr( i ); 
     
        // Member found 
        if( id == *pID ) 
        { 
            // Remove the id and return 
            pMembers->Remove( i ); 
            return S_OK; 
        } 
    } 
 
    // Not found 
    return E_FAIL; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: CSessionInfo() 
// Desc: Constructor 
//----------------------------------------------------------------------------- 
CSessionInfo::CSessionInfo( IDirectPlay8Peer* pPeer )  
{ 
    Initialize(); 
   
    m_eType         = PEER; 
    m_pPeer         = pPeer; 
     
    m_pPeer->AddRef(); 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: CSessionInfo() 
// Desc: Constructor 
//----------------------------------------------------------------------------- 
CSessionInfo::CSessionInfo( IDirectPlay8Client* pClient )  
{ 
    Initialize(); 
 
    m_eType        = CLIENT; 
    m_pClient      = pClient; 
     
    m_pClient->AddRef(); 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: CSessionInfo() 
// Desc: Constructor 
//----------------------------------------------------------------------------- 
CSessionInfo::CSessionInfo( IDirectPlay8Server* pServer )  
{ 
    Initialize(); 
 
    m_eType         = SERVER; 
    m_pServer       = pServer; 
     
    m_pServer->AddRef(); 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: Initialize() 
// Desc: Performs common initialization for all connection types 
//----------------------------------------------------------------------------- 
VOID CSessionInfo::Initialize() 
{ 
    g_pSI   = this; 
    m_pPlayers       = new CArrayList( AL_REFERENCE ); 
    m_pGroups        = new CArrayList( AL_REFERENCE ); 
 
    m_eType         = INVALID; 
    m_pPeer         = NULL; 
    m_pClient       = NULL; 
    m_pServer       = NULL; 
 
    m_dpnidLocal    = 0; 
     
    m_hDlg          = NULL; 
    m_hDlgParent    = NULL; 
    m_hDlgPlayers   = NULL; 
    m_hDlgMessages  = NULL; 
    m_hDlgThread    = NULL; 
 
    // Load custom fonts and resources 
#ifndef UNDER_CE  
    HMODULE hShellLib = LoadLibraryEx( TEXT("Shell32.dll"), NULL, LOAD_LIBRARY_AS_DATAFILE ); 
    if( hShellLib ) 
    { 
        m_hPlayerIcon = (HICON) LoadImage( hShellLib, MAKEINTRESOURCE(18),   
                                           IMAGE_ICON, 24, 24, LR_LOADTRANSPARENT); 
        m_hGroupIcon =  (HICON) LoadImage( hShellLib, MAKEINTRESOURCE(273),  
                                           IMAGE_ICON, 24, 24, LR_LOADTRANSPARENT); 
        FreeLibrary( hShellLib ); 
    } 
#endif // !UNDER_CE 
 
    // Create display fonts 
    m_hNameFont = CreateFontIndirect( &g_lfName ); 
    m_hConnectionFont = CreateFontIndirect( &g_lfConnection ); 
 
    InitializeCriticalSection( &m_csLock ); 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: ~CSessionInfo() 
// Desc: Destructor 
//----------------------------------------------------------------------------- 
CSessionInfo::~CSessionInfo() 
{ 
    // Cleanup dialog 
    if( m_hDlg ) 
        SendMessage( m_hDlg, WM_CLOSE, 0, 0 ); 
     
    SafeDestroyThread( &m_hDlgThread ); 
 
    // Cleanup interfaces 
    SAFE_RELEASE( m_pPeer ); 
    SAFE_RELEASE( m_pClient ); 
    SAFE_RELEASE( m_pServer ); 
 
    for( UINT i=0; i < m_pPlayers->Count(); i++ ) 
    { 
        CSIPlayer* pPlayer = (CSIPlayer*) m_pPlayers->GetPtr( i ); 
        SAFE_DELETE( pPlayer ); 
    } 
 
    for( i=0; i < m_pGroups->Count(); i++ ) 
    { 
        CSIGroup* pGroup = (CSIGroup*) m_pGroups->GetPtr( i ); 
        SAFE_DELETE( pGroup ); 
    } 
 
    SAFE_DELETE( m_pPlayers ); 
    SAFE_DELETE( m_pGroups ); 
 
    DeleteObject( m_hNameFont ); 
    DeleteObject( m_hConnectionFont ); 
 
    DeleteCriticalSection( &m_csLock ); 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: InitializeLocalPlayer() 
// Desc: Initialize the local player given the guessed local dpnid.  
//----------------------------------------------------------------------------- 
HRESULT CSessionInfo::InitializeLocalPlayer( DPNID idLocal ) 
{ 
    HRESULT hr = S_OK; 
    DPN_PLAYER_INFO* pInfo = NULL; 
 
#ifdef _DEBUG 
    if( NULL == idLocal ) 
        return E_INVALIDARG; 
#endif // _DEBUG 
 
    hr = GetDpPlayerInfo( idLocal, &pInfo ); 
 
    switch( m_eType ) 
    { 
        // Peer types can query information for any peer, including 
        // themselves, so simply check the returned playerinfo structure 
        // for the local flag 
        case PEER: 
            if( SUCCEEDED(hr) ) 
            { 
                if( pInfo->dwPlayerFlags & DPNPLAYER_LOCAL ) 
                    m_dpnidLocal = idLocal; 
                if( pInfo->dwPlayerFlags & DPNPLAYER_HOST ) 
                    m_dpnidHost = idLocal; 
            } 
            break; 
 
        // Server types can't query for local information. In a typical game, 
        // the player context would be checked to determine whether the created 
        // player is the server; to avoid changing the player contexts for 
        // every application this class is used with, we'll simply take a best 
        // guess and assume that if the new player isn't a client it must be 
        // the server. 
        case SERVER: 
            if( hr == DPNERR_INVALIDPLAYER ) 
            { 
                m_dpnidLocal = m_dpnidHost = idLocal; 
                hr = S_OK; 
            } 
            break; 
 
        // Client types can simply wait for the server to send their local 
        // information. 
        case CLIENT: 
            break; 
    } 
 
    return hr; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: FindPlayer 
// Desc: Search for and return the player based on dpnid 
//----------------------------------------------------------------------------- 
CSIPlayer* CSessionInfo::FindPlayer( DPNID id ) 
{ 
    // Find the player 
    for( UINT i=0; i < m_pPlayers->Count(); i++ ) 
    { 
       CSIPlayer* pPlayer = (CSIPlayer*)m_pPlayers->GetPtr( i ); 
        
        // Player found 
        if( id == pPlayer->id ) 
            return pPlayer; 
    } 
 
    // Not found 
    return NULL; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: FindGroup 
// Desc: Search for and return the group based on dpnid 
//----------------------------------------------------------------------------- 
CSIGroup* CSessionInfo::FindGroup( DPNID id ) 
{ 
    // Find the group 
    for( UINT i=0; i < m_pGroups->Count(); i++ ) 
    { 
        CSIGroup* pGroup = (CSIGroup*) m_pGroups->GetPtr( i ); 
 
        // Group found 
        if( id == pGroup->id ) 
            return pGroup; 
    } 
 
    // Not found 
    return NULL; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: CreatePlayer() 
// Desc: Creates a new player with the given ID and name, and adds the new node 
//       to the player list 
//----------------------------------------------------------------------------- 
HRESULT CSessionInfo::CreatePlayer( DPNID id ) 
{ 
    HRESULT hr; 
 
    // Create a new player object 
    CSIPlayer* pNewPlayer = new CSIPlayer( id ); 
    if( NULL == pNewPlayer ) 
        return E_OUTOFMEMORY; 
 
    // Add the new object to the list 
    hr = m_pPlayers->Add( pNewPlayer ); 
    if( FAILED(hr) ) 
    { 
        // Release the allocated memory and return the error code 
        SAFE_DELETE( pNewPlayer ); 
        return hr; 
    } 
 
   return S_OK; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: DestroyPlayer() 
// Desc: Removes the player with the given ID from the player list 
//----------------------------------------------------------------------------- 
HRESULT CSessionInfo::DestroyPlayer( DPNID id ) 
{ 
    // Find the player 
    for( UINT i=0; i < m_pPlayers->Count(); i++ ) 
    { 
        CSIPlayer* pPlayer = (CSIPlayer*)m_pPlayers->GetPtr( i ); 
 
        // Player found 
        if( id == pPlayer->id ) 
        { 
            // Zero out the spot and return 
            m_pPlayers->Remove( i ); 
            SAFE_DELETE( pPlayer ); 
            return S_OK; 
        } 
    } 
 
    // Not found 
    return E_FAIL; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: CreateGroup() 
// Desc: Creates a group with the given ID and name, and adds the new node to 
//       the group list 
//----------------------------------------------------------------------------- 
HRESULT CSessionInfo::CreateGroup( DPNID id ) 
{ 
    HRESULT hr; 
 
    // Create a new group object 
    CSIGroup* pNewGroup = new CSIGroup( id ); 
    if( NULL == pNewGroup ) 
        return E_OUTOFMEMORY; 
 
    // Add the new object to the list 
    hr = m_pGroups->Add( pNewGroup ); 
    if( FAILED(hr) ) 
    { 
        // Release the allocated memory and return the error code 
        SAFE_DELETE( pNewGroup); 
        return hr; 
    } 
 
   return S_OK; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: DestroyGroup() 
// Desc: Removes the group with the given ID from the group list 
//----------------------------------------------------------------------------- 
HRESULT CSessionInfo::DestroyGroup( DPNID id ) 
{ 
    // Find the group 
    for( UINT i=0; i < m_pGroups->Count(); i++ ) 
    { 
        CSIGroup* pGroup = (CSIGroup*) m_pGroups->GetPtr( i ); 
 
        // Group found 
        if( id == pGroup->id ) 
        { 
            // Zero out the spot and return 
            m_pGroups->Remove( i ); 
            SAFE_DELETE( pGroup ); 
            return S_OK; 
        } 
    } 
 
    // Not found 
    return E_FAIL; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: AddPlayerToGroup() 
// Desc: Adds the given player ID to the list of member players in the group 
//       with the given group ID  
//----------------------------------------------------------------------------- 
HRESULT CSessionInfo::AddPlayerToGroup( DPNID idPlayer, DPNID idGroup ) 
{ 
    // Find the group 
    for( UINT i=0; i < m_pGroups->Count(); i++ ) 
    { 
        CSIGroup* pGroup = (CSIGroup*) m_pGroups->GetPtr( i ); 
 
        // Group found 
        if( idGroup == pGroup->id ) 
        { 
            // Add the dpnid and return 
            return pGroup->AddMember( idPlayer ); 
        } 
    } 
 
    // Not found 
    return E_FAIL; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: RemovePlayerFromGroup() 
// Desc: Removes the given player ID from the list of member players in the  
//       group with the given group ID 
//----------------------------------------------------------------------------- 
HRESULT CSessionInfo::RemovePlayerFromGroup( DPNID idPlayer, DPNID idGroup ) 
{ 
    // Find the group 
    for( UINT i=0; i < m_pGroups->Count(); i++ ) 
    { 
        CSIGroup* pGroup = (CSIGroup*) m_pGroups->GetPtr( i ); 
 
        // Group found 
        if( idGroup == pGroup->id ) 
        { 
            // Remove the dpnid and return 
            return pGroup->RemoveMember( idPlayer ); 
        } 
    } 
 
    // Not found 
    return E_FAIL; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: RefreshPlayerInfo() 
// Desc: Use DirectPlay to refresh all info for the player with the given ID 
//----------------------------------------------------------------------------- 
HRESULT CSessionInfo::RefreshPlayerInfo( DPNID id ) 
{ 
    HRESULT                 hr              = S_OK; 
    LPWSTR                  strURL          = NULL; 
    DWORD                   dwNumChars      = 0; 
    DPN_PLAYER_INFO*        pDpPlayerInfo   = NULL; 
    IDirectPlay8Address*    rpAddress[16]   = {0}; 
    DWORD                   dwNumAddresses  = 16; 
 
#ifdef _DEBUG 
    // Parameter validation 
    if( NULL == id ) 
        return E_INVALIDARG; 
#endif // _DEBUG 
 
    // Attempt to get the name and flags 
    hr = GetDpPlayerInfo( id, &pDpPlayerInfo ); 
    if( FAILED(hr) ) 
        goto LCleanReturn; 
 
 
    // If receiving information about the local player, determine 
    // whether this app is the session host 
    if( id == m_dpnidLocal ) 
    { 
        // Attempt to get the local address 
        switch( m_eType ) 
        { 
            case PEER: 
                hr = m_pPeer->GetLocalHostAddresses( rpAddress, &dwNumAddresses, 0 ); 
                break; 
 
            case SERVER: 
                hr = m_pServer->GetLocalHostAddresses( rpAddress, &dwNumAddresses, 0 ); 
                break; 
 
            default: 
                hr = E_FAIL; 
                break; 
        } 
    } 
    else 
    { 
        // Attempt to get the remote address 
        dwNumAddresses = 1; 
 
        switch( m_eType ) 
        { 
            case PEER: 
                hr = m_pPeer->GetPeerAddress( id, rpAddress, 0 ); 
                break; 
 
            case SERVER: 
                hr = m_pServer->GetClientAddress( id, rpAddress, 0 ); 
                break; 
 
            case CLIENT: 
                if( id == m_dpnidHost ) 
                    hr = m_pClient->GetServerAddress( rpAddress, 0 ); 
                else 
                    hr = DPNERR_INVALIDPLAYER; 
                break; 
 
            default: 
                hr = E_FAIL; 
                break; 
        } 
    } 
     
    // If the address was retrieved, extract the URL 
    if( SUCCEEDED(hr) ) 
    { 
        // Get needed allocation size for the URL string 
        hr = rpAddress[0]->GetURLW( NULL, &dwNumChars ); 
        if( FAILED(hr) && hr != DPNERR_BUFFERTOOSMALL ) 
            goto LCleanReturn; 
 
        // Allocate the URL string 
        strURL = new WCHAR[ dwNumChars ]; 
        if( NULL == strURL ) 
        { 
            hr = E_OUTOFMEMORY; 
            goto LCleanReturn; 
        } 
 
        // Get the URL 
        hr = rpAddress[0]->GetURLW( strURL, &dwNumChars ); 
        if( FAILED(hr) ) 
            goto LCleanReturn; 
    } 
 
    // Locate the stored player data 
    Lock(); 
 
    CSIPlayer* pPlayer; 
    pPlayer = FindPlayer( id );   
    if( pPlayer ) 
    { 
        // Set the player data 
        pPlayer->bIsHost = ( pDpPlayerInfo->dwPlayerFlags & DPNPLAYER_HOST ); 
 
        if( strURL ) 
        { 
            DXUtil_ConvertWideStringToGenericCch( pPlayer->strURL, strURL, 256 ); 
        } 
 
        if( pDpPlayerInfo->pwszName) 
        { 
            DXUtil_ConvertWideStringToGenericCch( pPlayer->strName,  
                                                  pDpPlayerInfo->pwszName, 256 );  
        } 
    } 
 
    Unlock(); 
 
 
LCleanReturn: 
    // Release resources 
    SAFE_DELETE_ARRAY( strURL ); 
  
    for( UINT i=0; i < dwNumAddresses; i++ ) 
        SAFE_RELEASE( rpAddress[i] ); 
 
    SAFE_DELETE_ARRAY( pDpPlayerInfo ); 
 
    
    return hr; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: RefreshGroupInfo() 
// Desc: Use DirectPlay to refresh all info for the group with the given ID 
//----------------------------------------------------------------------------- 
HRESULT CSessionInfo::RefreshGroupInfo( DPNID id ) 
{ 
    HRESULT hr       = S_OK; 
    DPN_GROUP_INFO* pDpGroupInfo    = NULL; 
     
    // Attempt to get the name 
    hr = GetDpGroupInfo( id, &pDpGroupInfo ); 
    if( FAILED(hr) ) 
        goto LCleanReturn; 
 
    // Locate the stored player data 
    Lock(); 
 
    CSIGroup* pGroup; 
    pGroup = FindGroup( id ); 
 
    if( pGroup ) 
    { 
        // Set the group data 
        DXUtil_ConvertWideStringToGenericCch( pGroup->strName, pDpGroupInfo->pwszName, 256 );  
    } 
 
    Unlock(); 
 
 
LCleanReturn: 
    // Release resources 
    SAFE_DELETE_ARRAY( pDpGroupInfo ); 
 
    return hr; 
} 
 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: MessageHandler 
// Desc: Sift the information headed for the application's DirectPlay message 
//       handler, remove any messages used exclusively by this utility class, 
//       and store any useful information before the message is passed off to 
//       the application. 
//----------------------------------------------------------------------------- 
BOOL CSessionInfo::MessageHandler( DWORD dwMessageId, PVOID pMsgBuffer ) 
{ 
    HRESULT hr = S_OK; 
    TCHAR strMessage[ 256 ];  
 
    switch( dwMessageId ) 
    { 
        case DPN_MSGID_ADD_PLAYER_TO_GROUP: 
        { 
            DPNMSG_ADD_PLAYER_TO_GROUP* pMsg = (DPNMSG_ADD_PLAYER_TO_GROUP*) pMsgBuffer; 
         
            Lock(); 
            hr = AddPlayerToGroup( pMsg->dpnidPlayer, pMsg->dpnidGroup ); 
            Unlock(); 
             
            // Invalidate the dialog 
            m_bDlgValid = FALSE; 
     
            // Log the message 
            _sntprintf( strMessage,  
                        200,  
                        TEXT("Add Player To Group: player 0x%x, group 0x%x"),  
                        pMsg->dpnidPlayer,  
                        pMsg->dpnidGroup );  
             
            strMessage[ 200 ] = TEXT('\0'); 
            break; 
        } 
 
        case DPN_MSGID_APPLICATION_DESC: 
        { 
            // Set the window title 
            DPN_APPLICATION_DESC* pAppDesc; 
            if( SUCCEEDED( GetDpAppDesc( &pAppDesc ) ) ) 
            { 
                TCHAR strTitle[ 256 ]; 
                 
                DXUtil_ConvertWideStringToGenericCch( strTitle,  
                                                      pAppDesc->pwszSessionName,  
                                                      256-50 ); 
 
                lstrcat( strTitle, TEXT(" - Session Info") ); 
                SendMessage( m_hDlg, WM_SETTEXT, 0, (LPARAM) strTitle );  
            } 
 
            SAFE_DELETE_ARRAY( pAppDesc ); 
 
            // Log the message 
            _sntprintf( strMessage, 200, TEXT("Application Desc") ); 
            strMessage[ 200 ] = TEXT('\0'); 
            break; 
        } 
 
        case DPN_MSGID_ASYNC_OP_COMPLETE: 
        { 
            DPNMSG_ASYNC_OP_COMPLETE* pMsg = (DPNMSG_ASYNC_OP_COMPLETE*) pMsgBuffer; 
 
            // The messages sent by this helper class always use the same context. 
            // If the completed operation was initiated by this class, we can 
            // safely hide the message from the application. 
            if( SI_ASYNC_CONTEXT == pMsg->pvUserContext ) 
                return TRUE; 
 
            // Log the message 
            _sntprintf( strMessage,  
                        200,  
                        TEXT("Async Op Complete: handle 0x%x, result 0x%x"),  
                        pMsg->hAsyncOp,  
                        pMsg->hResultCode ); 
 
            strMessage[ 200 ] = TEXT('\0'); 
            break; 
        } 
 
        case DPN_MSGID_CLIENT_INFO: 
        { 
            DPNMSG_CLIENT_INFO* pMsg = (DPNMSG_CLIENT_INFO*) pMsgBuffer; 
            OnDpInfoChange( pMsg->dpnidClient ); 
 
            // Log the message 
            _sntprintf( strMessage,  
                        200,  
                        TEXT("Client Info: client 0x%x"),  
                        pMsg->dpnidClient ); 
 
            strMessage[ 200 ] = TEXT('\0'); 
            break; 
        } 
 
        case DPN_MSGID_CONNECT_COMPLETE: 
        { 
            DPNMSG_CONNECT_COMPLETE* pMsg = (DPNMSG_CONNECT_COMPLETE*) pMsgBuffer; 
         
            // Log the message 
            _sntprintf( strMessage,  
                        200,  
                        TEXT("Connect Complete: handle 0x%x, result 0x%x"),  
                        pMsg->hAsyncOp,  
                        pMsg->hResultCode ); 
 
            strMessage[ 200 ] = TEXT('\0'); 
            break; 
        } 
 
        case DPN_MSGID_CREATE_GROUP: 
        { 
            DPNMSG_CREATE_GROUP* pMsg = (DPNMSG_CREATE_GROUP*) pMsgBuffer; 
         
            Lock(); 
            hr = CreateGroup( pMsg->dpnidGroup ); 
            Unlock(); 
             
            RefreshGroupInfo( pMsg->dpnidGroup ); 
             
            if( m_eType == SERVER ) 
                SendGroupInfoToAll( pMsg->dpnidGroup ); 
 
            // Invalidate the dialog 
            m_bDlgValid = FALSE; 
 
            // Log the message 
            _sntprintf( strMessage,  
                        200,  
                        TEXT("Create Group: group 0x%x, owner 0x%x"),  
                        pMsg->dpnidGroup,  
                        pMsg->dpnidOwner ); 
 
            strMessage[ 200 ] = TEXT('\0'); 
            break; 
        } 
 
        case DPN_MSGID_CREATE_PLAYER: 
        { 
            DPNMSG_CREATE_PLAYER* pMsg = (DPNMSG_CREATE_PLAYER*) pMsgBuffer; 
         
            Lock(); 
            hr = CreatePlayer( pMsg->dpnidPlayer ); 
            Unlock();       
            
            // If we don't know our local dpnid yet, try to initialize 
            if( m_dpnidLocal == NULL ) 
                InitializeLocalPlayer( pMsg->dpnidPlayer ); 
 
            // Update information about the new player 
            RefreshPlayerInfo( pMsg->dpnidPlayer ); 
             
            if( m_eType == SERVER ) 
            { 
                // Clients receive all their information from the server, 
                // so bundle up the new player's name and flags, and broadcast 
                // to the session. 
                SendPlayerInfoToAll( pMsg->dpnidPlayer ); 
                SynchronizeWithPlayer( pMsg->dpnidPlayer ); 
            }    
 
            // Invalidate the dialog 
            m_bDlgValid = FALSE; 
 
            // Log the message 
            _sntprintf( strMessage,  
                        200,  
                        TEXT("Create Player: player 0x%x"),  
                        pMsg->dpnidPlayer ); 
 
            strMessage[ 200 ] = TEXT('\0'); 
            break; 
        } 
 
        case DPN_MSGID_DESTROY_GROUP: 
        { 
            DPNMSG_DESTROY_GROUP* pMsg = (DPNMSG_DESTROY_GROUP*) pMsgBuffer; 
         
            Lock(); 
            hr = DestroyGroup( pMsg->dpnidGroup ); 
            Unlock(); 
              
            // Invalidate the dialog 
            m_bDlgValid = FALSE; 
 
            // Log the message 
            _sntprintf( strMessage,  
                        200,  
                        TEXT("Destroy Group: group 0x%x"),  
                        pMsg->dpnidGroup ); 
 
            strMessage[ 200 ] = TEXT('\0'); 
            break; 
        } 
 
        case DPN_MSGID_DESTROY_PLAYER: 
        { 
            DPNMSG_DESTROY_PLAYER* pMsg = (DPNMSG_DESTROY_PLAYER*) pMsgBuffer; 
         
            Lock(); 
            hr = DestroyPlayer( pMsg->dpnidPlayer ); 
            Unlock();    
             
            // Invalidate the dialog 
            m_bDlgValid = FALSE; 
 
            // Log the message 
            _sntprintf( strMessage,  
                        200,  
                        TEXT("Destroy Player: player 0x%x"),  
                        pMsg->dpnidPlayer ); 
 
            strMessage[ 200 ] = TEXT('\0'); 
            break; 
        } 
 
        case DPN_MSGID_ENUM_HOSTS_QUERY: 
        { 
            // Log the message 
            _sntprintf( strMessage,  
                        200,  
                        TEXT("Enum Hosts Query") ); 
 
            strMessage[ 200 ] = TEXT('\0'); 
            break; 
        } 
 
        case DPN_MSGID_ENUM_HOSTS_RESPONSE: 
        { 
            DPNMSG_ENUM_HOSTS_RESPONSE* pMsg = (DPNMSG_ENUM_HOSTS_RESPONSE*) pMsgBuffer; 
         
            // Log the message 
            _sntprintf( strMessage,  
                        200,  
                        TEXT("Enum Hosts Response: latency %d ms, session \"%s\""),  
                        pMsg->dwRoundTripLatencyMS,  
                        pMsg->pApplicationDescription->pwszSessionName ); 
 
            strMessage[ 200 ] = TEXT('\0'); 
            break; 
        } 
 
        case DPN_MSGID_GROUP_INFO: 
        { 
            DPNMSG_GROUP_INFO* pMsg = (DPNMSG_GROUP_INFO*) pMsgBuffer; 
             
            RefreshGroupInfo( pMsg->dpnidGroup ); 
              
            if( m_eType == SERVER ) 
                SendGroupInfoToAll( pMsg->dpnidGroup ); 
 
            // Invalidate the dialog 
            m_bDlgValid = FALSE; 
             
            // Log the message 
            _sntprintf( strMessage,  
                        200,  
                        TEXT("Group Info: group 0x%x"),  
                        pMsg->dpnidGroup ); 
 
            strMessage[ 200 ] = TEXT('\0'); 
            break; 
        } 
 
        case DPN_MSGID_HOST_MIGRATE: 
        { 
            DPNMSG_HOST_MIGRATE* pMsg = (DPNMSG_HOST_MIGRATE*) pMsgBuffer; 
 
            m_dpnidHost = pMsg->dpnidNewHost; 
            RefreshPlayerInfo( pMsg->dpnidNewHost ); 
 
            // Invalidate the dialog 
            m_bDlgValid = FALSE; 
 
            // Log the message 
            _sntprintf( strMessage,  
                        200,  
                        TEXT("Host Migrate: new host 0x%x"),  
                        pMsg->dpnidNewHost ); 
 
            strMessage[ 200 ] = TEXT('\0'); 
            break; 
        } 
 
        case DPN_MSGID_INDICATE_CONNECT: 
        { 
            DPNMSG_INDICATE_CONNECT* pMsg = (DPNMSG_INDICATE_CONNECT*) pMsgBuffer; 
         
            // Log the message 
            _sntprintf( strMessage,  
                        200,  
                    #ifdef _WIN64 
                        TEXT("Indicate Connect: player context 0x%I64x"),  
                    #else 
                        TEXT("Indicate Connect: player context 0x%x"),  
                    #endif // _WIN64 
                        pMsg->pvPlayerContext ); 
 
            strMessage[ 200 ] = TEXT('\0'); 
            break; 
        } 
 
        case DPN_MSGID_INDICATED_CONNECT_ABORTED: 
        { 
            DPNMSG_INDICATED_CONNECT_ABORTED* pMsg = (DPNMSG_INDICATED_CONNECT_ABORTED*) pMsgBuffer; 
         
            // Log the message 
            _sntprintf( strMessage,  
                        200,  
                    #ifdef _WIN64 
                        TEXT("Indicated Connect Aborted: player context 0x%I64x"),  
                    #else 
                        TEXT("Indicated Connect Aborted: player context 0x%x"),  
                    #endif // _WIN64 
                        pMsg->pvPlayerContext ); 
 
            strMessage[ 200 ] = TEXT('\0'); 
            break; 
        } 
 
        case DPN_MSGID_PEER_INFO: 
        { 
            DPNMSG_PEER_INFO* pMsg = (DPNMSG_PEER_INFO*) pMsgBuffer; 
            OnDpInfoChange( pMsg->dpnidPeer ); 
 
            // Log the message 
            _sntprintf( strMessage,  
                        200,  
                        TEXT("Peer Info: peer 0x%x"),  
                        pMsg->dpnidPeer ); 
 
            strMessage[ 200 ] = TEXT('\0'); 
            break; 
        } 
 
        case DPN_MSGID_RECEIVE: 
        { 
            DPNMSG_RECEIVE* pMsg = (DPNMSG_RECEIVE*) pMsgBuffer; 
             
            if( pMsg->dwSize < sizeof(SI_MSG) ) 
                break; 
             
            SI_MSG* pSIMsg = (SI_MSG*) pMsg->pReceiveData; 
 
            switch( pSIMsg->dwMsgID ) 
            { 
                case SI_MSGID_PLAYERINFO: 
                { 
                    if( pMsg->dwReceiveDataSize < sizeof(SI_MSG_PLAYERINFO) ) 
                        break; 
 
                    SI_MSG_PLAYERINFO* pPlayerInfo = (SI_MSG_PLAYERINFO*) pMsg->pReceiveData; 
 
                    // Verify the message is properly sized 
                    if( pMsg->dwReceiveDataSize != sizeof(SI_MSG_PLAYERINFO) + 
                        ( sizeof(WCHAR) * ( pPlayerInfo->dwNameLength + 1 ) ) ) 
                        break; 
                     
                    // Pass the data off to the message handler function 
                    OnPlayerInfoReceive( pPlayerInfo ); 
 
                    // Attempt to get additional information about the player 
                    RefreshPlayerInfo( pPlayerInfo->dpnID ); 
                     
                    // Invalidate the dialog 
                    m_bDlgValid = FALSE; 
 
                    return TRUE; 
                } 
 
                case SI_MSGID_GROUPINFO: 
                { 
                    if( pMsg->dwReceiveDataSize < sizeof(SI_MSG_GROUPINFO) ) 
                        break; 
 
                    SI_MSG_GROUPINFO* pGroupInfo = (SI_MSG_GROUPINFO*) pMsg->pReceiveData; 
 
                    if( pMsg->dwReceiveDataSize != sizeof(SI_MSG_GROUPINFO) + 
                        ( sizeof(WCHAR) * ( pGroupInfo->dwNameLength + 1 ) ) ) 
                    { 
                        break; 
                    } 
 
                    // Pass the data off to the message handler function 
                    OnGroupInfoReceive( pGroupInfo ); 
 
                    // Invalidate the dialog 
                    m_bDlgValid = FALSE; 
 
                    return TRUE; 
                } 
            } 
 
            // Log the message 
            _sntprintf( strMessage,  
                        200,  
                        TEXT("Receive: sender 0x%x, data size %d bytes"),  
                        pMsg->dpnidSender,  
                        pMsg->dwReceiveDataSize ); 
 
            strMessage[ 200 ] = TEXT('\0'); 
            break; 
        } 
 
        case DPN_MSGID_REMOVE_PLAYER_FROM_GROUP: 
        { 
            DPNMSG_REMOVE_PLAYER_FROM_GROUP* pMsg = (DPNMSG_REMOVE_PLAYER_FROM_GROUP*) pMsgBuffer; 
         
            Lock(); 
            hr = RemovePlayerFromGroup( pMsg->dpnidPlayer, pMsg->dpnidGroup ); 
            Unlock(); 
             
            // Invalidate the dialog 
            m_bDlgValid = FALSE; 
 
            // Log the message 
            _sntprintf( strMessage,  
                        200,  
                        TEXT("Remove Player From Group: player 0x%x, group 0x%x"),  
                        pMsg->dpnidPlayer,  
                        pMsg->dpnidGroup ); 
 
            strMessage[ 200 ] = TEXT('\0'); 
            break; 
        } 
 
        case DPN_MSGID_RETURN_BUFFER: 
        { 
            DPNMSG_RETURN_BUFFER* pMsg = (DPNMSG_RETURN_BUFFER*) pMsgBuffer; 
             
            // Log the message 
            _sntprintf( strMessage,  
                        200,  
                    #ifdef _WIN64 
                        TEXT("Return Buffer: user context 0x%I64x, result 0x%x"),  
                    #else 
                        TEXT("Return Buffer: user context 0x%x, result 0x%x"),  
                    #endif // _WIN64 
                        pMsg->pvUserContext,  
                        pMsg->hResultCode ); 
 
            strMessage[ 200 ] = TEXT('\0'); 
            break; 
        } 
 
        case DPN_MSGID_SEND_COMPLETE: 
        { 
            DPNMSG_SEND_COMPLETE* pMsg = (DPNMSG_SEND_COMPLETE*) pMsgBuffer; 
             
            // Log the message 
            _sntprintf( strMessage,  
                        200,  
                        TEXT("Send Complete: handle 0x%x, result 0x%x, send time %d ms"),  
                        pMsg->hAsyncOp,  
                        pMsg->hResultCode,  
                        pMsg->dwSendTime ); 
 
            strMessage[ 200 ] = TEXT('\0'); 
            break; 
        } 
         
        case DPN_MSGID_SERVER_INFO: 
        { 
            DPNMSG_SERVER_INFO* pMsg = (DPNMSG_SERVER_INFO*) pMsgBuffer; 
            OnDpInfoChange( pMsg->dpnidServer ); 
 
            // Log the message 
            _sntprintf( strMessage,  
                        200,  
                        TEXT("Server Info: server 0x%x"),  
                        pMsg->dpnidServer ); 
 
            strMessage[ 200 ] = TEXT('\0'); 
            break; 
        } 
 
        case DPN_MSGID_TERMINATE_SESSION: 
        { 
            DPNMSG_TERMINATE_SESSION* pMsg = (DPNMSG_TERMINATE_SESSION*) pMsgBuffer; 
             
            // Log the message 
            _sntprintf( strMessage,  
                        200,  
                        TEXT("Terminate Session: result 0x%x"),  
                        pMsg->hResultCode ); 
 
            strMessage[ 200 ] = TEXT('\0'); 
            break; 
        } 
 
         
    } 
 
    CMessageList* pMsgList = NULL; 
    int nDlgID = 0; 
 
    // Add the message string to the stored list 
    switch( dwMessageId ) 
    { 
        case DPN_MSGID_RECEIVE: 
        case DPN_MSGID_SEND_COMPLETE: 
        { 
            pMsgList = &m_AppMessages; 
            nDlgID = IDC_SI_APPMSG; 
            break;  
        } 
 
        default: 
        { 
            pMsgList = &m_DPlayMessages; 
            nDlgID = IDC_SI_DPLAYMSG; 
            break;    
        } 
    } 
     
    // Lock the message list 
    pMsgList->Lock(); 
 
    // If the message queue is already full, remove the bottom item from the dialog box  
    if( pMsgList->IsFull() ) 
        SendMessage( GetDlgItem( m_hDlgMessages, nDlgID ), LB_DELETESTRING, SI_MAX_MESSAGES-1, 0 ); 
 
    // Add the message to the stored list 
    TCHAR* strTimeStamped = pMsgList->AddMessage( strMessage ); 
 
    // Unlock the message list 
    pMsgList->Unlock(); 
 
    // Post the new string to the top of the list box 
    if( m_hDlgMessages ) 
        SendMessage( GetDlgItem( m_hDlgMessages, nDlgID ), LB_INSERTSTRING, 0, (LPARAM) strTimeStamped ); 
 
    // Return false to indicate that either the message was not handled, or the 
    // handled message should also be sent to the application message handler. 
    return FALSE; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: OnPlayerInfoReceive() 
// Desc: Handles the extraction and storage of incoming player data 
//----------------------------------------------------------------------------- 
HRESULT CSessionInfo::OnPlayerInfoReceive( SI_MSG_PLAYERINFO* pPlayerInfo ) 
{ 
    HRESULT hr = S_OK; 
 
    // Extract the player name 
    LPWSTR pStrName = (LPWSTR) (pPlayerInfo + 1); 
    pStrName[ pPlayerInfo->dwNameLength ] = 0; 
 
    Lock(); 
       
    // Search for the player with the given ID 
    CSIPlayer* pPlayer = FindPlayer( pPlayerInfo->dpnID );  
 
    // If not found, create a new player 
    if( NULL == pPlayer ) 
    { 
        hr = CreatePlayer( pPlayerInfo->dpnID ); 
        if( FAILED(hr) ) 
            goto LCleanReturn; 
             
        pPlayer = FindPlayer( pPlayerInfo->dpnID ); 
    } 
 
    // Set updated information 
    pPlayer->bIsHost = pPlayerInfo->dwFlags & DPNPLAYER_HOST; 
     
    DXUtil_ConvertWideStringToGenericCch( pPlayer->strName, pStrName, 256 );  
     
    if( pPlayerInfo->dwFlags & DPNPLAYER_LOCAL ) 
        m_dpnidLocal = pPlayerInfo->dpnID; 
    if( pPlayerInfo->dwFlags & DPNPLAYER_HOST ) 
        m_dpnidHost = pPlayerInfo->dpnID; 
 
 
    hr = S_OK; 
 
LCleanReturn: 
    Unlock(); 
    return hr; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: OnGroupInfoReceive() 
// Desc: Handles the extraction and storage of incoming group data 
//----------------------------------------------------------------------------- 
HRESULT CSessionInfo::OnGroupInfoReceive( SI_MSG_GROUPINFO* pGroupInfo ) 
{ 
    HRESULT hr = S_OK; 
 
    // Extract the name 
    LPWSTR pStr = (LPWSTR) (pGroupInfo+1); 
    pStr[ pGroupInfo->dwNameLength ] = 0; 
 
    // Set the data 
    Lock(); 
     
    // Search for the group with the given ID 
    CSIGroup* pGroup = FindGroup( pGroupInfo->dpnID ); 
     
    // If not found, create a new group 
    if( NULL == pGroup ) 
    { 
        hr = CreateGroup( pGroupInfo->dpnID ); 
        if( FAILED(hr) ) 
            goto LCleanReturn; 
 
        pGroup = FindGroup( pGroupInfo->dpnID ); 
    } 
 
    // Set updated information 
    DXUtil_ConvertWideStringToGenericCch( pGroup->strName, pStr, 256 );  
     
    hr = S_OK; 
 
LCleanReturn: 
    Unlock(); 
    return hr; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: OnDpInfoChange() 
// Desc: Handles PEER_INFO, CLIENT_INFO, and SERVER_INFO messages 
//----------------------------------------------------------------------------- 
HRESULT CSessionInfo::OnDpInfoChange( DPNID dpnid ) 
{ 
    RefreshPlayerInfo( dpnid ); 
     
    if( m_eType == SERVER ) 
        SendPlayerInfoToAll( dpnid ); 
 
    // Invalidate the dialog 
    m_bDlgValid = FALSE; 
 
    return S_OK; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: GetDpPlayerInfo() 
// Desc: Get the DirectPlay player info using the stored connection interface 
//----------------------------------------------------------------------------- 
HRESULT CSessionInfo::GetDpPlayerInfo( DPNID dpnid, DPN_PLAYER_INFO** ppPlayerInfo ) 
{ 
    HRESULT hr; 
    DWORD dwSize = 0; 
 
#ifdef _DEBUG 
    if( NULL == ppPlayerInfo || NULL == dpnid ) 
        return E_INVALIDARG; 
#endif // _DEBUG 
     
    switch( m_eType ) 
    { 
        case PEER: 
        { 
            // GetPeerInfo might return DPNERR_CONNECTING when connecting,  
            // so just keep calling it if it does 
            do  
            { 
                hr = m_pPeer->GetPeerInfo( dpnid, *ppPlayerInfo, &dwSize, 0 );  
            } 
            while( hr == DPNERR_CONNECTING ); 
 
            break; 
        } 
 
        case SERVER: 
        { 
            // Special case: Server can't query for local information, so 
            // this should be filled in manually 
            if( m_dpnidLocal == dpnid ) 
            { 
                dwSize = sizeof( DPN_PLAYER_INFO ); 
                hr = DPNERR_BUFFERTOOSMALL; 
            } 
            else 
            { 
                hr = m_pServer->GetClientInfo( dpnid, *ppPlayerInfo, &dwSize, 0 ); 
            } 
 
            break; 
        } 
 
        case CLIENT: 
        { 
            if( m_dpnidHost == dpnid ) 
                hr = m_pClient->GetServerInfo( *ppPlayerInfo, &dwSize, 0 ); 
            else 
                hr = DPNERR_INVALIDPLAYER; 
            break; 
        } 
 
        default: 
            // Unknown type 
            return E_FAIL; 
    } 
       
    // DirectPlay should return BufferTooSmall to give the correct allocation size 
    if( hr != DPNERR_BUFFERTOOSMALL ) 
        return hr; 
 
    // Allocate the memory 
    *ppPlayerInfo = (DPN_PLAYER_INFO*) new BYTE[ dwSize ]; 
    if( NULL == *ppPlayerInfo ) 
        return E_OUTOFMEMORY; 
 
    // Initialize the struct 
    ZeroMemory( *ppPlayerInfo, dwSize ); 
    (*ppPlayerInfo)->dwSize = sizeof(DPN_PLAYER_INFO); 
     
    // Get peer info 
    switch( m_eType ) 
    { 
        case PEER:  
            hr = m_pPeer->GetPeerInfo( dpnid, *ppPlayerInfo, &dwSize, 0 );  
            break; 
 
        case SERVER:  
            // Special case: Server can't query for local information, so 
            // this should be filled in manually 
            if( m_dpnidLocal == dpnid ) 
            {   
                (*ppPlayerInfo)->dwInfoFlags = DPNINFO_NAME | DPNINFO_DATA; 
                (*ppPlayerInfo)->pwszName = L"(Server)"; 
                (*ppPlayerInfo)->dwPlayerFlags = DPNPLAYER_LOCAL | DPNPLAYER_HOST; 
                hr = S_OK; 
            } 
            else 
            { 
                hr = m_pServer->GetClientInfo( dpnid, *ppPlayerInfo, &dwSize, 0 );  
            } 
            break; 
 
        case CLIENT: 
            hr = m_pClient->GetServerInfo( *ppPlayerInfo, &dwSize, 0 ); 
            break; 
 
        default:  
            SAFE_DELETE_ARRAY( *ppPlayerInfo ); 
            return E_FAIL; 
    } 
 
    return hr; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: GetDpGroupInfo 
// Desc: Get the DirectPlay group info using the stored connection interface 
//----------------------------------------------------------------------------- 
HRESULT CSessionInfo::GetDpGroupInfo( DPNID dpnid, DPN_GROUP_INFO** ppGroupInfo ) 
{ 
    HRESULT hr; 
    DWORD dwSize = 0; 
 
#ifdef _DEBUG 
    if( NULL == ppGroupInfo ) 
        return E_INVALIDARG; 
#endif // _DEBUG 
     
    switch( m_eType ) 
    { 
        case PEER: 
            hr = m_pPeer->GetGroupInfo( dpnid, NULL, &dwSize, NULL ); 
            break; 
 
        case SERVER: 
            hr = m_pServer->GetGroupInfo( dpnid, NULL, &dwSize, NULL ); 
            break; 
 
        default: 
            return E_FAIL; 
    } 
 
    // DirectPlay should return BufferTooSmall to give the correct allocation size 
    if( hr != DPNERR_BUFFERTOOSMALL ) 
        return hr; 
 
    // Allocate the memory 
    *ppGroupInfo = (DPN_GROUP_INFO*) new BYTE[ dwSize ]; 
    if( NULL == *ppGroupInfo ) 
        return E_OUTOFMEMORY; 
 
    // Initialize the struct 
    ZeroMemory( *ppGroupInfo, dwSize ); 
    (*ppGroupInfo)->dwSize = sizeof(DPN_GROUP_INFO); 
     
    // Get group info 
    switch( m_eType ) 
    { 
        case PEER: 
            hr = m_pPeer->GetGroupInfo( dpnid, *ppGroupInfo, &dwSize, 0 ); 
            break; 
 
        case SERVER: 
            hr = m_pServer->GetGroupInfo( dpnid, *ppGroupInfo, &dwSize, 0 ); 
            break; 
 
        default: 
            SAFE_DELETE_ARRAY( *ppGroupInfo ); 
            return E_FAIL; 
    } 
 
    return hr; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: GetDpAppDesc 
// Desc: Get the DirectPlay application description using the stored connection  
//       interface 
//----------------------------------------------------------------------------- 
HRESULT CSessionInfo::GetDpAppDesc( DPN_APPLICATION_DESC** ppAppDesc ) 
{ 
    HRESULT hr; 
    DWORD dwSize = 0; 
 
#ifdef _DEBUG 
    if( NULL == ppAppDesc ) 
        return E_INVALIDARG; 
#endif // _DEBUG 
     
    switch( m_eType ) 
    { 
        case PEER: 
            hr = m_pPeer->GetApplicationDesc( NULL, &dwSize, NULL ); 
            break; 
 
        case SERVER: 
            hr = m_pServer->GetApplicationDesc( NULL, &dwSize, NULL ); 
            break; 
 
        case CLIENT: 
            hr = m_pClient->GetApplicationDesc( NULL, &dwSize, NULL ); 
            break; 
 
        default: 
            return E_FAIL; 
    } 
 
    // DirectPlay should return BufferTooSmall to give the correct allocation size 
    if( hr != DPNERR_BUFFERTOOSMALL ) 
        return hr; 
 
    // Allocate the memory 
    *ppAppDesc = (DPN_APPLICATION_DESC*) new BYTE[ dwSize ]; 
    if( NULL == *ppAppDesc ) 
        return E_OUTOFMEMORY; 
 
    // Initialize the struct 
    ZeroMemory( *ppAppDesc, dwSize ); 
    (*ppAppDesc)->dwSize = sizeof(DPN_APPLICATION_DESC); 
     
    // Get group info 
    switch( m_eType ) 
    { 
           case PEER: 
            hr = m_pPeer->GetApplicationDesc( *ppAppDesc, &dwSize, NULL ); 
            break; 
 
        case SERVER: 
            hr = m_pServer->GetApplicationDesc( *ppAppDesc, &dwSize, NULL ); 
            break; 
 
        case CLIENT: 
            hr = m_pClient->GetApplicationDesc( *ppAppDesc, &dwSize, NULL ); 
            break; 
 
        default: 
            return E_FAIL; 
    } 
 
    return hr; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: SendPlayerInfoToPlayer() 
// Desc: Send all stored player information with the given player ID to the 
//       player(s) with the given target ID 
//----------------------------------------------------------------------------- 
HRESULT CSessionInfo::SendPlayerInfoToPlayer( DPNID idPlayer, DPNID idTarget ) 
{ 
    HRESULT hr = S_OK; 
 
    Lock(); 
     
    CSIPlayer* pPlayer = FindPlayer( idPlayer ); 
    if( NULL == pPlayer ) 
    { 
        Unlock(); 
        return E_INVALIDARG; 
    } 
 
    // Package and send the given player info 
    DWORD dwNameLen = lstrlen( pPlayer->strName ); 
     
    DPN_BUFFER_DESC dpBufDesc = {0}; 
    dpBufDesc.dwBufferSize = sizeof(SI_MSG_PLAYERINFO) +  
                           ( sizeof( WCHAR ) * (dwNameLen + 1) ); 
                            
    // Allocate space 
    SI_MSG_PLAYERINFO* pMsg = (SI_MSG_PLAYERINFO*) new BYTE[ dpBufDesc.dwBufferSize ]; 
    if( NULL == pMsg ) 
    { 
        Unlock(); 
        return E_OUTOFMEMORY; 
    } 
 
    // Set the data pointer 
    dpBufDesc.pBufferData = (BYTE*) pMsg; 
 
    // Store values 
    pMsg->dwMsgID       = SI_MSGID_PLAYERINFO; 
    pMsg->dpnID         = pPlayer->id; 
    pMsg->dwFlags       = 0; 
    pMsg->dwNameLength  = dwNameLen; 
     
    // Set flags 
    if( pPlayer->bIsHost ) 
        pMsg->dwFlags |= DPNPLAYER_HOST; 
    if( idPlayer == idTarget ) 
        pMsg->dwFlags |= DPNPLAYER_LOCAL; 
 
    // Pack the string 
    LPWSTR pStr = (LPWSTR) (pMsg+1); 
    DXUtil_ConvertGenericStringToWideCch( pStr, pPlayer->strName, dwNameLen+1 ); 
     
    // Release the lock 
    Unlock(); 
 
    // Send the information 
    DPNHANDLE dpAsync; 
 
    switch( m_eType ) 
    { 
        case PEER: 
            hr = m_pPeer->SendTo( idTarget, &dpBufDesc, 1, 0, SI_ASYNC_CONTEXT, &dpAsync, DPNSEND_NOCOMPLETE | DPNSEND_NOLOOPBACK ); 
            break; 
 
        case SERVER: 
            hr = m_pServer->SendTo( idTarget, &dpBufDesc, 1, 0, SI_ASYNC_CONTEXT, &dpAsync, DPNSEND_NOCOMPLETE | DPNSEND_NOLOOPBACK ); 
            break; 
 
        case CLIENT: 
            hr = m_pClient->Send( &dpBufDesc, 1, 0, SI_ASYNC_CONTEXT, &dpAsync, DPNSEND_NOCOMPLETE | DPNSEND_NOLOOPBACK ); 
            break; 
 
        default: 
            hr = E_FAIL; 
            break; 
    } 
 
    // Release allocated memory and return 
    SAFE_DELETE_ARRAY( pMsg ); 
    return hr; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: SendGroupInfoToPlayer 
// Desc: Send all stored information about the given group ID to the player(s) 
//       with the given target ID 
//----------------------------------------------------------------------------- 
HRESULT CSessionInfo::SendGroupInfoToPlayer( DPNID idGroup, DPNID idTarget ) 
{ 
    HRESULT hr = S_OK; 
 
    Lock(); 
 
    CSIGroup* pGroup = FindGroup( idGroup );  
    if( NULL == pGroup ) 
    { 
        Unlock(); 
        return E_INVALIDARG; 
    } 
 
    // Package and send the given player info 
    DWORD dwNameLen = lstrlen( pGroup->strName ); 
    
    DPN_BUFFER_DESC dpBufDesc = {0}; 
    dpBufDesc.dwBufferSize = sizeof(SI_MSG_GROUPINFO) +  
                           ( sizeof( WCHAR ) * (dwNameLen + 1) );  
 
    // Allocate space 
    SI_MSG_GROUPINFO* pMsg = (SI_MSG_GROUPINFO*) new BYTE[ dpBufDesc.dwBufferSize ]; 
    if( NULL == pMsg ) 
    { 
        Unlock(); 
        return E_OUTOFMEMORY; 
    } 
 
    // Set the data pointer 
    dpBufDesc.pBufferData = (BYTE*) pMsg; 
 
    // Store values 
    pMsg->dwMsgID       = SI_MSGID_GROUPINFO; 
    pMsg->dpnID         = idGroup; 
    pMsg->dwNameLength  = dwNameLen; 
 
    // Pack the strings 
    LPWSTR pStr = (LPWSTR) (pMsg+1); 
    DXUtil_ConvertGenericStringToWideCch( pStr, pGroup->strName, dwNameLen+1 ); 
     
    // Release the lock 
    Unlock(); 
 
    // Send the information 
    DPNHANDLE dpAsync; 
             
    switch( m_eType ) 
    { 
        case PEER: 
            hr = m_pPeer->SendTo( idTarget, &dpBufDesc, 1, 0, SI_ASYNC_CONTEXT, &dpAsync, DPNSEND_NOCOMPLETE | DPNSEND_NOLOOPBACK ); 
            break; 
 
        case SERVER: 
            hr = m_pServer->SendTo( idTarget, &dpBufDesc, 1, 0, SI_ASYNC_CONTEXT, &dpAsync, DPNSEND_NOCOMPLETE | DPNSEND_NOLOOPBACK ); 
            break; 
 
        default: 
            hr = E_FAIL; 
            break; 
    } 
 
    // Release allocated memory and return 
    SAFE_DELETE_ARRAY( pMsg ); 
    return hr; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: SynchronizeWithPlayer() 
// Desc: Send all stored group and player information to the player with the  
//       given ID 
//----------------------------------------------------------------------------- 
HRESULT CSessionInfo::SynchronizeWithPlayer( DPNID id ) 
{ 
    HRESULT hr = S_OK; 
 
    Lock(); 
 
    // Send all player and group information 
    for( UINT i=0; i < m_pPlayers->Count(); i++ ) 
    { 
        CSIPlayer* pPlayer = (CSIPlayer*) m_pPlayers->GetPtr( i ); 
 
        // Attempt to send  
        hr = SendPlayerInfoToPlayer( pPlayer->id, id ); 
        if( FAILED(hr) ) 
            break; 
    } 
 
    for( i=0; i < m_pGroups->Count(); i++ ) 
    { 
        CSIGroup* pGroup = (CSIGroup*) m_pGroups->GetPtr( i ); 
 
        // Attempt to send  
        hr = SendGroupInfoToPlayer( pGroup->id, id ); 
        if( FAILED(hr) ) 
            break; 
    } 
 
    Unlock(); 
 
    return hr; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: ShowDialog() 
// Desc: Show the dialog UI for all stored player, group, and message data. 
//----------------------------------------------------------------------------- 
HRESULT CSessionInfo::ShowDialog( HWND hParent ) 
{ 
    // If a dialog already exists, bring it to the front 
    if( m_hDlg ) 
    { 
        if( FALSE == BringWindowToTop( m_hDlg ) ) 
            return E_FAIL; 
    } 
    else 
    { 
        // If there is an old thread handle, release it now 
        SafeDestroyThread( &m_hDlgThread ); 
 
        // Launch a new dialog thread 
        DWORD dwThreadID; 
 
        m_hDlgParent = hParent; 
        m_hDlgThread = chBEGINTHREADEX( NULL, 0, StaticDialogThread, (void*) this, 0, &dwThreadID ); 
    } 
 
    return S_OK; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: StaticDialogThread() 
// Desc: Message pump thread for modeless dialogs 
//----------------------------------------------------------------------------- 
DWORD WINAPI CSessionInfo::StaticDialogThread( void* pvRef ) 
{ 
    CSessionInfo* pThis = (CSessionInfo*) pvRef; 
 
    pThis->m_hDlgPlayers = CreateDialog( GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_SI_PLAYERS), pThis->m_hDlgParent, StaticDlgProcPlayers ); 
    pThis->m_hDlgMessages = CreateDialog( GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_SI_MESSAGES), pThis->m_hDlgParent, StaticDlgProcMessages ); 
    pThis->m_hDlg = CreateDialog( GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_SI_MAIN), pThis->m_hDlgParent, StaticDlgProcMain );    
     
    ShowWindow( pThis->m_hDlg, SW_SHOW ); 
 
    MSG msg; 
    while( GetMessage( &msg, NULL, 0, 0) ) 
    {   
        // Forward the message to the dialog 
        if( IsDialogMessage( pThis->m_hDlg, &msg ) ) 
            continue; 
 
        if( IsDialogMessage( pThis->m_hDlgPlayers, &msg ) ) 
            continue; 
 
        if( IsDialogMessage( pThis->m_hDlgMessages, &msg ) ) 
            continue; 
    } 
 
    // Release the window resources 
    DestroyWindow( pThis->m_hDlgMessages );  
    pThis->m_hDlgMessages = NULL; 
 
    DestroyWindow( pThis->m_hDlgPlayers );   
    pThis->m_hDlgPlayers = NULL; 
 
    DestroyWindow( pThis->m_hDlg );          
    pThis->m_hDlg = NULL; 
 
    return 0; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: StaticDlgProcMain() 
// Desc: Static dialog procedure sorts incoming messages according to the 
//       window handle and assigns them to the member dialog procedure for the 
//       correct instance 
//----------------------------------------------------------------------------- 
INT_PTR CALLBACK CSessionInfo::StaticDlgProcMain( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) 
{ 
    if( g_pSI ) 
        return g_pSI->DlgProcMain( hDlg, uMsg, wParam, lParam ); 
 
    return FALSE; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: StaticDlgProcPlayers() 
// Desc: Static dialog procedure sorts incoming messages according to the 
//       window handle and assigns them to the member dialog procedure for the 
//       correct instance 
//----------------------------------------------------------------------------- 
INT_PTR CALLBACK CSessionInfo::StaticDlgProcPlayers( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) 
{ 
    if( g_pSI ) 
        return g_pSI->DlgProcPlayers( hDlg, uMsg, wParam, lParam ); 
 
    return FALSE; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: StaticDlgProcMessages() 
// Desc: Static dialog procedure sorts incoming messages according to the 
//       window handle and assigns them to the member dialog procedure for the 
//       correct instance 
//----------------------------------------------------------------------------- 
INT_PTR CALLBACK CSessionInfo::StaticDlgProcMessages( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) 
{ 
    if( g_pSI ) 
        return g_pSI->DlgProcMessages( hDlg, uMsg, wParam, lParam ); 
 
    return FALSE; 
} 
   
 
 
 
//----------------------------------------------------------------------------- 
// Name: DlgProcMain() 
// Desc: Dialog procedure for the UI 
//----------------------------------------------------------------------------- 
INT_PTR CALLBACK CSessionInfo::DlgProcMain( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) 
{ 
    switch( uMsg ) 
    { 
        case WM_INITDIALOG: 
        { 
            SetParent( m_hDlgPlayers, GetDlgItem( hDlg, IDC_SI_TAB ) ); 
            SetParent( m_hDlgMessages, GetDlgItem( hDlg, IDC_SI_TAB ) ); 
 
            // Set the window title 
            DPN_APPLICATION_DESC* pAppDesc; 
            if( SUCCEEDED( GetDpAppDesc( &pAppDesc ) ) ) 
            { 
                TCHAR strTitle[ 256 ]; 
                 
                DXUtil_ConvertWideStringToGenericCch( strTitle, pAppDesc->pwszSessionName, 256-50 ); 
                lstrcat( strTitle, TEXT(" - Session Info") ); 
                SendMessage( hDlg, WM_SETTEXT, 0, (LPARAM) strTitle );  
            } 
 
            // Add tabs 
            TCITEM tcItem = {0}; 
            tcItem.mask = TCIF_TEXT; 
 
            tcItem.pszText = TEXT("Players"); 
            SendMessage( GetDlgItem( hDlg, IDC_SI_TAB ), TCM_INSERTITEM, 0, (LPARAM) &tcItem ); 
            tcItem.pszText = TEXT("Messages"); 
            SendMessage( GetDlgItem( hDlg, IDC_SI_TAB ), TCM_INSERTITEM, 1, (LPARAM) &tcItem ); 
 
 
            RECT rcWindow = {0}; 
            GetWindowRect( GetDlgItem( hDlg, IDC_SI_TAB ), &rcWindow ); 
            SendMessage( GetDlgItem( hDlg, IDC_SI_TAB ), TCM_ADJUSTRECT, FALSE, (LPARAM) &rcWindow ); 
 
            POINT ptWindow = { rcWindow.left, rcWindow.top }; 
            ScreenToClient( GetDlgItem( hDlg, IDC_SI_TAB ), &ptWindow ); 
            MoveWindow( m_hDlgPlayers, ptWindow.x, ptWindow.y, rcWindow.right - rcWindow.left, rcWindow.bottom - rcWindow.top, FALSE ); 
            MoveWindow( m_hDlgMessages, ptWindow.x, ptWindow.y, rcWindow.right - rcWindow.left, rcWindow.bottom - rcWindow.top, FALSE ); 
 
            ShowWindow( m_hDlgPlayers, SW_SHOW ); 
 
            SAFE_DELETE_ARRAY( pAppDesc ); 
            return TRUE; 
        } 
 
        case WM_COMMAND: 
            switch( LOWORD(wParam) ) 
            { 
                case IDCANCEL: 
                    SendMessage( hDlg, WM_CLOSE, 0, 0 ); 
                    return TRUE; 
            } 
 
            break; 
 
        case WM_NOTIFY: 
        { 
            NMHDR* pHdr = (NMHDR*) lParam;  
            switch( pHdr->code ) 
            { 
                case TCN_SELCHANGE: 
                    DWORD dwCurSel = SendMessage( GetDlgItem( hDlg, IDC_SI_TAB ), TCM_GETCURSEL, 0, 0 ); 
 
                    ShowWindow( m_hDlgPlayers,  ( 0 == dwCurSel ) ? SW_SHOW : SW_HIDE ); 
                    ShowWindow( m_hDlgMessages, ( 1 == dwCurSel ) ? SW_SHOW : SW_HIDE );  
                    return TRUE; 
            } 
            break; 
        } 
 
        case WM_CLOSE: 
            if( m_hDlgThread ) 
                PostQuitMessage( 0 ); 
            else 
                EndDialog( hDlg, 0 ); 
             
            return TRUE; 
    } 
 
    return FALSE; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: DlgProcPlayers() 
// Desc: Dialog procedure for the UI 
//----------------------------------------------------------------------------- 
INT_PTR CALLBACK CSessionInfo::DlgProcPlayers( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) 
{ 
    switch( uMsg ) 
    { 
        case WM_INITDIALOG: 
        { 
            // Set custom dialog settings 
            if( m_hNameFont ) 
                SendMessage( GetDlgItem( hDlg, IDC_SI_NAME ), WM_SETFONT, (WPARAM) m_hNameFont, TRUE ); 
 
            if( m_hConnectionFont ) 
                SendMessage( GetDlgItem( hDlg, IDC_SI_DESCRIPTION ), WM_SETFONT, (WPARAM) m_hConnectionFont, TRUE ); 
             
            // Refresh the dialog contents 
            PaintDialog( hDlg ); 
            m_bDlgValid = TRUE; 
             
            // Set the refresh timer 
            SetTimer( hDlg, SI_REFRESH_TIMER, SI_REFRESH_INTERVAL, NULL ); 
            return TRUE; 
        } 
 
        case WM_TIMER: 
        { 
            if( FALSE == m_bDlgValid ) 
                PaintDialog( hDlg ); 
 
            return TRUE; 
        } 
 
        case WM_COMMAND: 
        { 
            switch( LOWORD(wParam) ) 
            { 
                case IDC_SI_PLAYERS: 
                { 
                    if( LBN_SELCHANGE == HIWORD(wParam) ) 
                    { 
                        DWORD dwCurSel; 
                        DWORD dwItemData; 
 
                        if( LB_ERR != ( dwCurSel   = SendMessage( (HWND) lParam, LB_GETCURSEL, 0, 0 ) ) && 
                            LB_ERR != ( dwItemData = SendMessage( (HWND) lParam, LB_GETITEMDATA, dwCurSel, 0 ) ) ) 
                            DisplayPlayer( dwItemData, hDlg ); 
                             
                        return TRUE; 
                    } 
 
                    break; 
                } 
 
                case IDC_SI_GROUPS: 
                { 
                    if( LBN_SELCHANGE == HIWORD(wParam) ) 
                    { 
                        DWORD dwCurSel; 
                        DWORD dwItemData; 
 
                        if( LB_ERR != ( dwCurSel   = SendMessage( (HWND) lParam, LB_GETCURSEL, 0, 0 ) ) && 
                            LB_ERR != ( dwItemData = SendMessage( (HWND) lParam, LB_GETITEMDATA, dwCurSel, 0 ) ) ) 
                            DisplayGroup( dwItemData, hDlg ); 
                             
                        return TRUE; 
                    } 
 
                    break; 
                } 
 
                case IDC_SI_MEMBERSHIP: 
                { 
                    if( LBN_SELCHANGE == HIWORD(wParam) ) 
                    { 
                        DWORD dwCurSel; 
                        DWORD dwItemData; 
 
                        if( LB_ERR != ( dwCurSel   = SendMessage( (HWND) lParam, LB_GETCURSEL, 0, 0 ) ) && 
                            LB_ERR != ( dwItemData = SendMessage( (HWND) lParam, LB_GETITEMDATA, dwCurSel, 0 ) ) ) 
                        { 
                            if( LB_ERR != SendMessage( GetDlgItem( hDlg, IDC_SI_PLAYERS ), LB_GETCURSEL, 0, 0 ) ) 
                                DisplayGroup( dwItemData, hDlg );    
                            else if( LB_ERR != SendMessage( GetDlgItem( hDlg, IDC_SI_GROUPS ), LB_GETCURSEL, 0, 0 ) ) 
                                DisplayPlayer( dwItemData, hDlg ); 
                        } 
 
                        return TRUE; 
                    } 
                    break; 
                } 
            } 
            break; 
        } 
    } 
 
    return FALSE; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: DlgProcMessages() 
// Desc: Dialog procedure for the UI 
//----------------------------------------------------------------------------- 
INT_PTR CALLBACK CSessionInfo::DlgProcMessages( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) 
{ 
    switch( uMsg ) 
    { 
        case WM_INITDIALOG: 
        { 
            // List box settings 
            PostMessage( GetDlgItem( hDlg, IDC_SI_DPLAYMSG ), LB_INITSTORAGE, SI_MAX_MESSAGES, sizeof(TCHAR) * 256 ); 
            PostMessage( GetDlgItem( hDlg, IDC_SI_APPMSG ),   LB_INITSTORAGE, SI_MAX_MESSAGES, sizeof(TCHAR) * 256 ); 
            PostMessage( GetDlgItem( hDlg, IDC_SI_DPLAYMSG ), LB_SETHORIZONTALEXTENT, 400, 0 ); 
            PostMessage( GetDlgItem( hDlg, IDC_SI_APPMSG ),   LB_SETHORIZONTALEXTENT, 400, 0 ); 
 
            // Fill the message windows 
            m_DPlayMessages.Lock(); 
            for( UINT i=0; i < m_DPlayMessages.GetNumOfMessages(); i++ ) 
            { 
                SendMessage( GetDlgItem( hDlg, IDC_SI_DPLAYMSG ), LB_INSERTSTRING, 0, (LPARAM) m_DPlayMessages.GetMessage( i ) ); 
            } 
            m_DPlayMessages.Unlock(); 
             
            m_AppMessages.Lock(); 
            for( i=0; i < m_AppMessages.GetNumOfMessages(); i++ ) 
            { 
                SendMessage( GetDlgItem( hDlg, IDC_SI_APPMSG ), LB_INSERTSTRING, 0, (LPARAM) m_AppMessages.GetMessage( i ) ); 
            } 
            m_AppMessages.Unlock(); 
 
            return TRUE; 
        } 
    } 
 
    return FALSE; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: PaintDialog() 
// Desc: Fill the dialog with the stored player/group data 
//----------------------------------------------------------------------------- 
VOID CSessionInfo::PaintDialog( HWND hDlg ) 
{ 
    m_bDlgValid = TRUE; 
 
    // Store the current selection 
    enum { NONE, PLAYER, GROUP } eSelected = NONE; 
 
    DWORD dwItemData = 0; 
    DWORD dwCurSel = 0; 
 
    if( LB_ERR != ( dwCurSel   = SendMessage( GetDlgItem( hDlg, IDC_SI_PLAYERS ), LB_GETCURSEL, 0, 0 ) ) && 
        LB_ERR != ( dwItemData = SendMessage( GetDlgItem( hDlg, IDC_SI_PLAYERS ), LB_GETITEMDATA, dwCurSel, 0 ) ) ) 
    { 
        eSelected = PLAYER; 
    } 
    else if( LB_ERR != ( dwCurSel   = SendMessage( GetDlgItem( hDlg, IDC_SI_GROUPS ), LB_GETCURSEL, 0, 0 ) ) && 
             LB_ERR != ( dwItemData = SendMessage( GetDlgItem( hDlg, IDC_SI_GROUPS ), LB_GETITEMDATA, dwCurSel, 0 ) ) ) 
    { 
        eSelected = GROUP; 
    } 
 
    // Clear the current contents 
    SendMessage( GetDlgItem( hDlg, IDC_SI_PLAYERS ), LB_RESETCONTENT, 0, 0 ); 
    SendMessage( GetDlgItem( hDlg, IDC_SI_GROUPS ), LB_RESETCONTENT, 0, 0 ); 
 
    Lock(); 
     
    // Add Players 
    for( UINT i=0; i < m_pPlayers->Count(); i++ ) 
    { 
        CSIPlayer* pPlayer = (CSIPlayer*) m_pPlayers->GetPtr( i ); 
 
        DWORD dwIndex = SendMessage( GetDlgItem( hDlg, IDC_SI_PLAYERS ), LB_ADDSTRING, 0, (LPARAM) pPlayer->strName ); 
        SendMessage( GetDlgItem( hDlg, IDC_SI_PLAYERS ), LB_SETITEMDATA, dwIndex, pPlayer->id ); 
    } 
 
    // Add Groups 
    for( i=0; i < m_pGroups->Count(); i++ ) 
    { 
        CSIGroup* pGroup = (CSIGroup*) m_pGroups->GetPtr( i ); 
 
        DWORD dwIndex = SendMessage( GetDlgItem( hDlg, IDC_SI_GROUPS ), LB_ADDSTRING, 0, (LPARAM) pGroup->strName ); 
        SendMessage( GetDlgItem( hDlg, IDC_SI_GROUPS ), LB_SETITEMDATA, dwIndex, pGroup->id ); 
    } 
 
    Unlock(); 
 
    // Restore the selection 
    switch( eSelected ) 
    { 
        case PLAYER:  
            if( FAILED( DisplayPlayer( dwItemData, hDlg ) ) ) 
                DisplayPlayer( m_dpnidLocal, hDlg ); 
            break; 
 
        case GROUP:   
            if( FAILED( DisplayGroup( dwItemData, hDlg ) ) ) 
                DisplayPlayer( m_dpnidLocal, hDlg ); 
            break; 
 
        default:  
            DisplayPlayer( m_dpnidLocal, hDlg ); 
            break; 
    } 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: DisplayPlayer() 
// Desc: Display all stored information about the given player in the dialog UI 
//----------------------------------------------------------------------------- 
HRESULT CSessionInfo::DisplayPlayer( DPNID id, HWND hDlg ) 
{ 
    HRESULT hr; 
 
    // Set the icon 
#ifndef UNDER_CE 
    if( m_hPlayerIcon ) 
        SendMessage( GetDlgItem( hDlg, IDC_SI_NAME_ICON ), STM_SETICON, (LPARAM) m_hPlayerIcon, 0 ); 
#endif // !UNDER_CE 
 
    // Clear the dialog data 
    SendMessage( GetDlgItem( hDlg, IDC_SI_NAME ), WM_SETTEXT, 0, (LPARAM) TEXT("") ); 
    SendMessage( GetDlgItem( hDlg, IDC_SI_GROUPS ), LB_SETCURSEL, (WPARAM) -1, 0 ); 
    SendMessage( GetDlgItem( hDlg, IDC_SI_MEMBERSHIP ), LB_RESETCONTENT, 0, 0 ); 
    SendMessage( GetDlgItem( hDlg, IDC_SI_DESCRIPTION ), WM_SETTEXT, (WPARAM) 0, (LPARAM) TEXT("") ); 
 
    SendMessage( GetDlgItem( hDlg, IDC_SI_MEMBERSHIP_TEXT ), WM_SETTEXT, 0, (LPARAM) TEXT("Member Of") ); 
     
    Lock(); 
 
    // Search for the given player 
    CSIPlayer* pPlayer = FindPlayer( id ); 
    if( NULL == pPlayer ) 
    { 
        hr = E_INVALIDARG; 
        goto LCleanReturn; 
    } 
 
    // Set the selected item 
    SelectListboxItem( GetDlgItem( hDlg, IDC_SI_PLAYERS ), pPlayer->id, pPlayer->strName ); 
 
    // Show the player name 
    SendMessage( GetDlgItem( hDlg, IDC_SI_NAME ), WM_SETTEXT, 0, (LPARAM) pPlayer->strName ); 
 
    // Fill the description box 
    PrintPlayerInfo( GetDlgItem( hDlg, IDC_SI_DESCRIPTION ), pPlayer ); 
 
    // Search for the player id in the group list 
    UINT i; 
    for( i=0; i < m_pGroups->Count(); i++ ) 
    { 
        CSIGroup* pGroup = (CSIGroup*) m_pGroups->GetPtr( i ); 
 
        if( pGroup->IsMember( id ) ) 
        { 
            DWORD dwIndex = SendMessage( GetDlgItem( hDlg, IDC_SI_MEMBERSHIP ), LB_ADDSTRING, 0, (LPARAM) pGroup->strName ); 
            if( LB_ERR != dwIndex ) 
                SendMessage( GetDlgItem( hDlg, IDC_SI_MEMBERSHIP ), LB_SETITEMDATA, dwIndex, pGroup->id ); 
        } 
    } 
 
     
    hr = S_OK; 
 
LCleanReturn: 
    Unlock(); 
    return hr; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: DisplayGroup() 
// Desc: Display all stored information about the given group in the dialog UI 
//----------------------------------------------------------------------------- 
HRESULT CSessionInfo::DisplayGroup( DPNID id, HWND hDlg ) 
{ 
    HRESULT hr; 
 
    // Set the icon 
#ifndef UNDER_CE 
    if( m_hPlayerIcon ) 
        SendMessage( GetDlgItem( hDlg, IDC_SI_NAME_ICON ), STM_SETICON, (LPARAM) m_hGroupIcon, 0 ); 
#endif // !UNDER_CE 
 
    // Clear the dialog data 
    SendMessage( GetDlgItem( hDlg, IDC_SI_NAME ), WM_SETTEXT, 0, (LPARAM) TEXT("") ); 
    SendMessage( GetDlgItem( hDlg, IDC_SI_PLAYERS ), LB_SETCURSEL, (WPARAM) -1, 0 ); 
    SendMessage( GetDlgItem( hDlg, IDC_SI_MEMBERSHIP ), LB_RESETCONTENT, 0, 0 ); 
    SendMessage( GetDlgItem( hDlg, IDC_SI_DESCRIPTION ), WM_SETTEXT, 0, (LPARAM) TEXT("") ); 
 
    SendMessage( GetDlgItem( hDlg, IDC_SI_MEMBERSHIP_TEXT ), WM_SETTEXT, 0, (LPARAM) TEXT("Members") ); 
     
    Lock(); 
 
    // Search for the given group 
    CSIGroup* pGroup = FindGroup( id ); 
    if( NULL == pGroup ) 
    { 
        hr = E_INVALIDARG; 
        goto LCleanReturn; 
    } 
 
    // Set the selected item 
    SelectListboxItem( GetDlgItem( hDlg, IDC_SI_GROUPS ), pGroup->id, pGroup->strName ); 
 
    // Fill the description box 
    PrintGroupInfo( GetDlgItem( hDlg, IDC_SI_DESCRIPTION ), pGroup ); 
 
    // Show the group name 
    SendMessage( GetDlgItem( hDlg, IDC_SI_NAME ), WM_SETTEXT, 0, (LPARAM) pGroup->strName ); 
 
    // List member players 
    UINT i; 
    for( i=0; i < pGroup->pMembers->Count(); i++ ) 
    { 
        DPNID* pID = (DPNID*) pGroup->pMembers->GetPtr( i ); 
        CSIPlayer* pPlayer = FindPlayer( *pID ); 
 
        if( pPlayer ) 
        { 
            DWORD dwIndex; 
 
            dwIndex = SendMessage( GetDlgItem( hDlg, IDC_SI_MEMBERSHIP ), LB_ADDSTRING, 0, (LPARAM) pPlayer->strName ); 
            if( LB_ERR != dwIndex ) 
                SendMessage( GetDlgItem( hDlg, IDC_SI_MEMBERSHIP ), LB_SETITEMDATA, dwIndex, pPlayer->id ); 
        } 
    } 
 
    hr = S_OK; 
 
LCleanReturn: 
    Unlock(); 
    return hr; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: PrintPlayerInfo() 
// Desc: Print the player description to the given edit box 
//----------------------------------------------------------------------------- 
HRESULT CSessionInfo::PrintPlayerInfo( HWND hWndEdit, CSIPlayer* pPlayer) 
{ 
    TCHAR strBuf[51] = {0}; 
 
    // Validate parameters 
#ifdef _DEBUG 
    if( NULL == pPlayer ) 
        return E_INVALIDARG; 
#endif // _DEBUG 
 
 
    // Output DPNID info 
    _sntprintf( strBuf, 50, TEXT("dpnid     0x%x (%u)\015\012"), pPlayer->id, pPlayer->id ); 
    strBuf[ 50 ] = TEXT('\0'); 
    SendMessage( hWndEdit, EM_REPLACESEL, FALSE, (LPARAM) strBuf ); 
 
    // Output flags 
    ZeroMemory( strBuf, sizeof( strBuf ) ); 
    lstrcpy( strBuf, TEXT("flags     ") ); 
 
    if( pPlayer->bIsHost ) 
    { 
        lstrcat( strBuf, TEXT("Host") ); 
    } 
 
    if( m_dpnidLocal == pPlayer->id ) 
    { 
        if( pPlayer->bIsHost ) 
            lstrcat( strBuf, TEXT(", ") ); 
 
        lstrcat( strBuf, TEXT("Local") ); 
    } 
 
    lstrcat( strBuf, TEXT("\015\012") ); 
    SendMessage( hWndEdit, EM_REPLACESEL, FALSE, (LPARAM) strBuf ); 
 
    // If the player has an associated URL address, print the 
    // encoded information 
    if( pPlayer->strURL && lstrlen( pPlayer->strURL ) > lstrlen( TEXT("x-directplay:/") ) ) 
    { 
        // Duplicate input string since it will be modified 
        TCHAR* strCopy = _tcsdup( pPlayer->strURL + lstrlen( TEXT("x-directplay:/") ) ); 
        if( NULL == strCopy ) 
            return E_OUTOFMEMORY; 
 
        TCHAR* strToken = NULL; 
        strToken = _tcstok( strCopy, TEXT("=") ); 
 
        // While more URL information is encoded in input string 
        while( strToken ) 
        { 
            TCHAR strSpacing[] = TEXT("           "); 
 
            // output key 
            SendMessage( hWndEdit, EM_REPLACESEL, FALSE, (LPARAM) strToken ); 
            strSpacing[ 10 - lstrlen( strToken ) ] = L'\0'; 
 
            // insert column spacing 
            SendMessage( hWndEdit, EM_REPLACESEL, FALSE, (LPARAM) strSpacing ); 
 
            // output value 
            strToken = _tcstok( NULL, TEXT(";") ); 
            SendMessage( hWndEdit, EM_REPLACESEL, FALSE, (LPARAM) strToken ); 
            SendMessage( hWndEdit, EM_REPLACESEL, FALSE, (LPARAM) TEXT("\015\012") ); 
 
            // find next key 
            strToken = _tcstok( NULL, TEXT("=") ); 
        } 
 
        // release resources 
        if( strCopy ) 
            free( strCopy ); 
    } 
 
    return S_OK; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: PrintGroupInfo() 
// Desc: Print the group description to the given edit box 
//----------------------------------------------------------------------------- 
HRESULT CSessionInfo::PrintGroupInfo( HWND hWndEdit, CSIGroup* pGroup ) 
{ 
    TCHAR strBuf[51] = {0}; 
 
    // Validate parameters 
#ifdef _DEBUG 
    if( NULL == pGroup ) 
        return E_INVALIDARG; 
#endif // _DEBUG 
 
    // Output DPNID info 
    _sntprintf( strBuf, 50, TEXT("dpnid     0x%x (%u)\015\012"), pGroup->id, pGroup->id ); 
    strBuf[ 50 ] = TEXT('\0'); 
    SendMessage( hWndEdit, EM_REPLACESEL, FALSE, (LPARAM) strBuf ); 
 
    return S_OK; 
} 
 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: CMessageList() 
// Desc: Constructor 
//----------------------------------------------------------------------------- 
CMessageList::CMessageList() : m_dwStartIndex( 0 ), m_dwNumMessages( 0 ) 
{ 
    InitializeCriticalSection( &m_csLock ); 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: ~CMessageList() 
// Desc: Destructor 
//----------------------------------------------------------------------------- 
CMessageList::~CMessageList() 
{ 
    DeleteCriticalSection( &m_csLock ); 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: GetMessage() 
// Desc: Returns the string pointer for the given message number 
//----------------------------------------------------------------------------- 
TCHAR* CMessageList::GetMessage( DWORD dwMessageNum ) 
{ 
    if( dwMessageNum >= m_dwNumMessages ) 
        return NULL; 
 
    return m_rStrMessage[ ( m_dwStartIndex + dwMessageNum ) % SI_MAX_MESSAGES ]; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: AddMessage() 
// Desc: Adds and time-stamp and copies the string to the end of the message 
//       list. If the list is full, the oldest item is removed to make room 
//----------------------------------------------------------------------------- 
TCHAR* CMessageList::AddMessage( TCHAR* strMessage ) 
{ 
    TCHAR* strDest = NULL; 
  
    if( m_dwNumMessages < SI_MAX_MESSAGES ) 
    { 
        // If there is still room in the array, increment the number of messages 
        // and get the newly added string pointer 
        strDest = GetMessage( m_dwNumMessages++ ); 
    } 
    else 
    { 
        // Else, increment the start index and get the last element 
        m_dwStartIndex++; 
        strDest = GetMessage( SI_MAX_MESSAGES - 1 ); 
    } 
 
    // Copy the string, adding a time stamp 
    SYSTEMTIME sysTime; 
    GetLocalTime( &sysTime ); 
    _sntprintf( strDest, 56, TEXT("[%02d:%02d:%02d]  "), 
               sysTime.wHour, sysTime.wMinute, sysTime.wSecond ); 
 
    _tcsncat( strDest, strMessage, 200 ); 
    strDest[ 255 ] = TEXT('\0'); 
 
    return strDest; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: SelectListboxItem 
// Desc: Search for and select the matching item in the list box 
//----------------------------------------------------------------------------- 
HRESULT CSessionInfo::SelectListboxItem( HWND hListBox, DWORD dwData, const TCHAR* strItem ) 
{ 
    int nIndex = -1; 
    int nItemData = LB_ERR;  
 
    do  
    { 
        // Find the string in the list 
        nIndex = SendMessage( hListBox, LB_FINDSTRING, nIndex, (LPARAM) strItem ); 
        if( LB_ERR == nIndex ) 
            break; 
 
        // Confirm the id number matches 
        nItemData = SendMessage( hListBox, LB_GETITEMDATA, nIndex, 0 ); 
        if( LB_ERR == nItemData ) 
            break; 
 
        // Match found, select the item and return 
        if( (DWORD) nItemData == dwData ) 
        { 
            SendMessage( hListBox, LB_SETCURSEL, nIndex, 0 ); 
            return S_OK; 
        } 
    } 
    while( nIndex != LB_ERR ); 
 
    return E_FAIL; 
} 
 
 
 
 
//----------------------------------------------------------------------------- 
// Name: SafeDestroyThread 
// Desc: Signal the window close event and wait for the thread to shut down   
//----------------------------------------------------------------------------- 
VOID CSessionInfo::SafeDestroyThread( LPHANDLE phThread ) 
{ 
    if( *phThread ) 
    { 
        WaitForSingleObject( *phThread, INFINITE ); 
        CloseHandle( *phThread ); 
        *phThread = NULL; 
    } 
}