//----------------------------------------------------------------------------- // File: Wizard.cpp // // Desc: UI routines // // Copyright (C) 1995-2001 Microsoft Corporation. All Rights Reserved. //----------------------------------------------------------------------------- #include "duel.h" #include "prsht.h" #include "wizard.h" #include "lobby.h" #include "DPUtil.h" #include "gameproc.h" //----------------------------------------------------------------------------- // Globals //----------------------------------------------------------------------------- extern HINSTANCE g_hInst; // Program instance extern HWND g_hwndMain; // Main window extern LPDIRECTPLAY4 g_pDP; // DPlay object pointer extern BOOL g_bHostPlayer; // Is the user hosting/joining a game extern DPID g_LocalPlayerDPID;// Player id extern LPGUID g_pAppGUID; // Duel's guid extern BOOL g_bUseProtocol; // DPlay Protocol messaging static BOOL gbWaitForLobby; // We will Wait for Settings from Lobby static HFONT ghTitleFont; // Font for titles on setup wizard static HFONT ghHeadingFont; // Font for headings on setup wizard static int gnSession; // Index for tree view images static int gnPlayer; // Index for tree view images static HKEY ghDuelKey = NULL; // Duel registry key handle static DWORD gdwDuelDisp; // Key created or opened static HTREEITEM ghtiSession; // Points to a visible session item in // tree ctl used for inserting players static HWND ghWndSPCtl; // Handle to service provider control //----------------------------------------------------------------------------- // Name: SetupFonts() // Desc: Initializes font structures (used for wizard controls) //----------------------------------------------------------------------------- BOOL SetupFonts() { LOGFONT lf; TCHAR strFontName[MAX_FONTNAME]; HDC hDC = GetDC(g_hwndMain); if (!hDC) return FALSE; LoadString(g_hInst, IDS_WIZARD_FONTNAME, strFontName, MAX_FONTNAME); ZeroMemory(&lf,sizeof(lf)); lf.lfHeight = -MulDiv(11,GetDeviceCaps(hDC, LOGPIXELSY),72); lf.lfWeight = 500; lf.lfItalic = TRUE; _tcscpy( lf.lfFaceName, strFontName ); ghTitleFont = CreateFontIndirect(&lf); ZeroMemory(&lf,sizeof(lf)); lf.lfHeight = -MulDiv(11,GetDeviceCaps(hDC, LOGPIXELSY),72); lf.lfWeight = 500; _tcscpy( lf.lfFaceName, strFontName ); ghHeadingFont = CreateFontIndirect(&lf); ReleaseDC(g_hwndMain, hDC); if (!ghTitleFont || !ghHeadingFont) { CleanupFonts(); return FALSE; } return TRUE; } //----------------------------------------------------------------------------- // Name: CleanupFonts() // Desc: Cleans up font structures //----------------------------------------------------------------------------- VOID CleanupFonts() { if (ghTitleFont) DeleteObject(ghTitleFont); if (ghHeadingFont) DeleteObject(ghHeadingFont); ghTitleFont = NULL; ghHeadingFont = NULL; } //----------------------------------------------------------------------------- // Name: RegSet() // Desc: Stores a data value in the registry //----------------------------------------------------------------------------- LONG RegSet( const TCHAR* strName, const BYTE* pData, DWORD dwSize ) { #ifdef UNICODE dwSize *= 2; // calc number of bytes #endif return RegSetValueEx( ghDuelKey, strName, 0, REG_SZ, pData, dwSize ); } //----------------------------------------------------------------------------- // Name: RegGet() // Desc: Queries a value from the registry //----------------------------------------------------------------------------- LONG RegGet( const TCHAR* strName, BYTE* pData, DWORD* pdwDataSize ) { DWORD dwType; return RegQueryValueEx( ghDuelKey, strName, NULL, &dwType, pData, pdwDataSize ); } //----------------------------------------------------------------------------- // Name: DoWizard() // Desc: Creates and launches a wizard (property sheets) for user input //----------------------------------------------------------------------------- DWORD WINAPI DoWizard(LPVOID pv) { PROPSHEETPAGE psp[4]; PROPSHEETHEADER psh; TCHAR strTitle1[MAX_WINDOWTITLE]; TCHAR strTitle2[MAX_WINDOWTITLE]; TCHAR strTitle3[MAX_WINDOWTITLE]; TCHAR strTitle4[MAX_WINDOWTITLE]; TCHAR strTitle5[MAX_WINDOWTITLE]; CoInitialize(NULL); // Setup the property pages LoadString(g_hInst, IDS_WIZARD_TITLE_GS, strTitle1, MAX_WINDOWTITLE); LoadString(g_hInst, IDS_WIZARD_TITLE_SP, strTitle2, MAX_WINDOWTITLE); LoadString(g_hInst, IDS_WIZARD_TITLE_JS, strTitle3, MAX_WINDOWTITLE); LoadString(g_hInst, IDS_WIZARD_TITLE_HS, strTitle4, MAX_WINDOWTITLE); LoadString(g_hInst, IDS_WIZARD_TITLE, strTitle5, MAX_WINDOWTITLE); psp[0].dwSize = sizeof(PROPSHEETPAGE); psp[0].dwFlags = PSP_USETITLE; psp[0].hInstance = g_hInst; #ifdef NONAMELESSUNION psp[0].u.pszTemplate = MAKEINTRESOURCE(IDD_GAMESETUP); psp[0].u2.pszIcon = NULL; #else psp[0].pszTemplate = MAKEINTRESOURCE(IDD_GAMESETUP); psp[0].pszIcon = NULL; #endif psp[0].pfnDlgProc = (DLGPROC)DlgProcGameSetup; psp[0].pszTitle = strTitle1; psp[0].lParam = 0; psp[0].pfnCallback = NULL; psp[1].dwSize = sizeof(PROPSHEETPAGE); psp[1].dwFlags = PSP_USETITLE; psp[1].hInstance = g_hInst; #ifdef NONAMELESSUNION psp[1].u.pszTemplate = MAKEINTRESOURCE(IDD_CHOOSEPROVIDER); psp[1].u2.pszIcon = NULL; #else psp[1].pszTemplate = MAKEINTRESOURCE(IDD_CHOOSEPROVIDER); psp[1].pszIcon = NULL; #endif psp[1].pfnDlgProc = (DLGPROC)DlgProcChooseProvider; psp[1].pszTitle = strTitle2; psp[1].lParam = 0; psp[1].pfnCallback = NULL; psp[2].dwSize = sizeof(PROPSHEETPAGE); psp[2].dwFlags = PSP_USETITLE; psp[2].hInstance = g_hInst; #ifdef NONAMELESSUNION psp[2].u.pszTemplate = MAKEINTRESOURCE(IDD_JOINSESSION); psp[2].u2.pszIcon = NULL; #else psp[2].pszTemplate = MAKEINTRESOURCE(IDD_JOINSESSION); psp[2].pszIcon = NULL; #endif psp[2].pfnDlgProc = (DLGPROC)DlgProcJoinSession; psp[2].pszTitle = strTitle3; psp[2].lParam = 0; psp[2].pfnCallback = NULL; psp[3].dwSize = sizeof(PROPSHEETPAGE); psp[3].dwFlags = PSP_USETITLE; psp[3].hInstance = g_hInst; #ifdef NONAMELESSUNION psp[3].u.pszTemplate = MAKEINTRESOURCE(IDD_HOSTSESSION); psp[3].u2.pszIcon = NULL; #else psp[3].pszTemplate = MAKEINTRESOURCE(IDD_HOSTSESSION); psp[3].pszIcon = NULL; #endif psp[3].pfnDlgProc = (DLGPROC)DlgProcHostSession; psp[3].pszTitle = strTitle4; psp[3].lParam = 0; psp[3].pfnCallback = NULL; psh.dwSize = sizeof(PROPSHEETHEADER); psh.dwFlags = PSH_PROPSHEETPAGE | PSH_WIZARD; psh.hwndParent = g_hwndMain; psh.hInstance = g_hInst; #ifdef NONAMELESSUNION psh.u.pszIcon = NULL; psh.u2.nStartPage = 0; psh.u3.ppsp = (LPCPROPSHEETPAGE) &psp; #else psh.pszIcon = NULL; psh.nStartPage = 0; psh.ppsp = (LPCPROPSHEETPAGE) &psp; #endif psh.pszCaption = strTitle5; psh.nPages = sizeof(psp) / sizeof(PROPSHEETPAGE); psh.pfnCallback = NULL; // open/create duel registry key if( ERROR_SUCCESS != RegCreateKeyEx( HKEY_CURRENT_USER, DUEL_KEY, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &ghDuelKey, &gdwDuelDisp ) ) { TRACE(_T("Failed to create/open registry key\n")); ShowError(IDS_WIN_ERROR); goto ABORT; } if (!SetupFonts()) { TRACE(_T("Failed to create fonts\n")); ShowError(IDS_WIN_ERROR); goto ABORT; } // launch the wizard if (PropertySheet(&psh) < 0) { TRACE(_T("PropertySheet failed (comctl32.dll)\n")); ShowError(IDS_WIN_ERROR); goto ABORT; } CleanupFonts(); // Are we going to wait for a lobby to connect to us? if (gbWaitForLobby) gbWaitForLobby = DPLobbyWait(); // gbWaitForLobby now means we successfully got our connection settings // Do we have our settings or a game session? if( gbWaitForLobby || DPUtil_IsDPlayInitialized() ) { // success CoUninitialize(); PostMessage(g_hwndMain, UM_LAUNCH, (WPARAM)gbWaitForLobby, 0); gbWaitForLobby = FALSE; // done with this flag return 0; } ABORT: // failure CoUninitialize(); PostMessage(g_hwndMain, UM_ABORT, 0, 0); return 1; } //----------------------------------------------------------------------------- // Name: DPEnumConnectionsCallback() // Desc: Creates SP list box entries and associates connection data with them //----------------------------------------------------------------------------- BOOL FAR PASCAL DPEnumConnectionsCallback( const GUID* pguidSP, VOID* pConnection, DWORD dwSize, const DPNAME* pName, DWORD dwFlags, VOID* pContext ) { HWND hWnd = (HWND)pContext; LRESULT iIndex; VOID* pConnectionBuffer; // Store service provider name in combo box #ifdef UNICODE iIndex = SendMessage( hWnd, LB_ADDSTRING, 0, (LPARAM)pName->lpszShortName ); #else iIndex = SendMessage( hWnd, LB_ADDSTRING, 0, (LPARAM)pName->lpszShortNameA ); #endif if( iIndex == CB_ERR ) return TRUE; // Make space for Connection Shortcut pConnectionBuffer = GlobalAllocPtr( GHND, dwSize ); if( pConnectionBuffer == NULL ) return TRUE; // Store pointer to GUID in combo box memcpy( pConnectionBuffer, pConnection, dwSize ); if( iIndex != LB_ERR ) SendMessage( hWnd, LB_SETITEMDATA, iIndex, (LPARAM)pConnectionBuffer ); return TRUE; } //----------------------------------------------------------------------------- // Name: DlgProcChooseProvider() // Desc: Dialog procedure for the choose service provider dialog //----------------------------------------------------------------------------- BOOL CALLBACK DlgProcChooseProvider( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam ) { VOID* pCon = NULL; static LONG iIndex; static HWND hWndCtl; HRESULT hr; switch( msg ) { case WM_NOTIFY: switch( ((NMHDR FAR *)lParam)->code ) { case PSN_SETACTIVE: PropSheet_SetWizButtons( GetParent(hDlg), PSWIZB_BACK | PSWIZB_NEXT ); return TRUE; case PSN_WIZNEXT: // Release previously selected DPlay object, if any. DPUtil_Release(); if( iIndex != LB_ERR ) { pCon = (VOID*)SendMessage( hWndCtl, LB_GETITEMDATA, iIndex, 0 ); if( pCon ) { // Create directplay object if( SUCCEEDED( DPUtil_InitDirectPlay( pCon ) ) ) { if( g_bHostPlayer ) // Skip to host page SetWindowLong( hDlg, DWL_MSGRESULT, IDD_HOSTSESSION ); return TRUE; } else { ShowError(IDS_DPLAY_ERROR_IDC); } } } SetWindowLong( hDlg, DWL_MSGRESULT, -1 ); return TRUE; case PSN_QUERYCANCEL: ReleaseSPData(); DPUtil_Release(); // Indicates no game session return TRUE; } break; case WM_INITDIALOG: SendDlgItemMessage( hDlg, IDC_SP_TITLE, WM_SETFONT, (WPARAM)ghTitleFont, MAKELPARAM(TRUE,0) ); hWndCtl = GetDlgItem( hDlg, IDC_SERVICEPROVIDERS ); if( hWndCtl == NULL ) return TRUE; // Remember the service provider control. used later in freeing // sp information. ghWndSPCtl = hWndCtl; if( FAILED( hr = DPUtil_InitDirectPlay( pCon ) ) ) { // Class not available, so tell them to get DPlay 6 or later if( (hr == REGDB_E_CLASSNOTREG) || (hr == REGDB_E_IIDNOTREG) || (hr == E_NOINTERFACE)) ShowError(IDS_DPLAY_ERROR_CLSID); else ShowError(IDS_DPLAY_ERROR_IDC); SetWindowLong( hDlg, DWL_MSGRESULT, -1 ); PostQuitMessage( -1 ); return TRUE; } //Insert all available connections into the connection listbox. g_pDP->EnumConnections( g_pAppGUID, DPEnumConnectionsCallback, hWndCtl, 0 ); SetFocus( hWndCtl ); SendMessage( hWndCtl, LB_SETCURSEL, iIndex, 0 ); iIndex = SendMessage( hWndCtl, LB_GETCURSEL, 0, 0 ); return TRUE; case WM_COMMAND: if( HIWORD(wParam) == LBN_SELCHANGE ) { iIndex = SendMessage( hWndCtl, LB_GETCURSEL, 0, 0 ); return TRUE; } break; } return FALSE; } //----------------------------------------------------------------------------- // Name: DlgProcGameSetup() // Desc: Dialog procedure for the Game Setup Dialog. //----------------------------------------------------------------------------- BOOL CALLBACK DlgProcGameSetup (HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) { static BOOL bWaitForLobby; switch (msg) { case WM_NOTIFY: switch (((NMHDR FAR *) lParam)->code) { case PSN_SETACTIVE: if (bWaitForLobby) PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_FINISH); else PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_NEXT); return(TRUE); case PSN_WIZNEXT: if (g_bHostPlayer) g_bUseProtocol = (SendDlgItemMessage(hDlg, IDC_USEPROTOCOL, BM_GETCHECK, 0, 0) == BST_CHECKED); else g_bUseProtocol = FALSE; return(TRUE); case PSN_WIZFINISH: // only if WaitForLobby selected gbWaitForLobby = bWaitForLobby; // fall thru case PSN_QUERYCANCEL: ReleaseSPData(); DPUtil_Release(); // Indicates no game session return(TRUE); } break; case WM_INITDIALOG: // setup title fonts SendDlgItemMessage(hDlg, IDC_GAMESETUP_TITLE, WM_SETFONT, (WPARAM)ghTitleFont, MAKELPARAM(TRUE,0)); SendDlgItemMessage(hDlg, IDC_JOINSESSION, WM_SETFONT, (WPARAM)ghHeadingFont, MAKELPARAM(TRUE,0)); SendDlgItemMessage(hDlg, IDC_HOSTSESSION, WM_SETFONT, (WPARAM)ghHeadingFont, MAKELPARAM(TRUE,0)); SendDlgItemMessage(hDlg, IDC_WAITFORLOBBY, WM_SETFONT, (WPARAM)ghHeadingFont, MAKELPARAM(TRUE,0)); SendDlgItemMessage(hDlg, IDC_USEPROTOCOL, WM_SETFONT, (WPARAM)ghHeadingFont, MAKELPARAM(TRUE,0)); // host by default g_bHostPlayer = TRUE; gbWaitForLobby = bWaitForLobby = FALSE; SendDlgItemMessage(hDlg, IDC_HOSTSESSION, BM_SETCHECK, 1, 0); SendDlgItemMessage(hDlg, IDC_JOINSESSION, BM_SETCHECK, 0, 0); SendDlgItemMessage(hDlg, IDC_WAITFORLOBBY, BM_SETCHECK, 0, 0); SendDlgItemMessage(hDlg, IDC_USEPROTOCOL, BM_SETCHECK, g_bUseProtocol, 0); return(TRUE); case WM_COMMAND: if (HIWORD(wParam) == BN_CLICKED) switch (LOWORD(wParam)) { // Enable Use Protocol check box only if Hosting is selected // Enable Finish Button only if WaitForLobby is selected case IDC_HOSTSESSION: g_bHostPlayer = TRUE; EnableWindow(GetDlgItem(hDlg, IDC_USEPROTOCOL), TRUE); if (bWaitForLobby) { bWaitForLobby = FALSE; PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_NEXT); } return(TRUE); case IDC_JOINSESSION: if (g_bHostPlayer) { g_bHostPlayer = FALSE; EnableWindow(GetDlgItem(hDlg, IDC_USEPROTOCOL), FALSE); } if (bWaitForLobby) { bWaitForLobby = FALSE; PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_NEXT); } return(TRUE); case IDC_WAITFORLOBBY: bWaitForLobby = TRUE; PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_FINISH); if (g_bHostPlayer) { g_bHostPlayer = FALSE; EnableWindow(GetDlgItem(hDlg, IDC_USEPROTOCOL), FALSE); } return(TRUE); } break; } return(FALSE); } //----------------------------------------------------------------------------- // Name: EnumPlayer() // Desc: EnumeratePlayer callback. Inserts player information into the passed // in tree view control. //----------------------------------------------------------------------------- BOOL WINAPI EnumPlayer( DPID pidID, DWORD dwPlayerType, const DPNAME* pName, DWORD dwFlags, VOID* pContext ) { HWND hWnd = (HWND)pContext; HTREEITEM hItem; #ifdef UNICODE hItem = AddItemToTree( hWnd, pName->lpszShortName, 0, -1 ); #else hItem = AddItemToTree( hWnd, pName->lpszShortNameA, 0, -1 ); #endif return TRUE; } //----------------------------------------------------------------------------- // Name: EnumSession() // Desc: EnumSessions callback. Inserts session description information in the // passed in tree view control. //----------------------------------------------------------------------------- BOOL WINAPI EnumSession( const DPSESSIONDESC2* pDPSessionDesc, DWORD* pdwTimeOut, DWORD dwFlags, VOID* pContext ) { HWND hWnd = (HWND)pContext; HTREEITEM hItem; TREEDATA* pdata; if( dwFlags & DPESC_TIMEDOUT ) return FALSE; // don't try again if( hWnd == NULL ) return FALSE; // allocate memory to remember the guid pdata = (TREEDATA*)malloc(sizeof(TREEDATA)); if( NULL == pdata ) return FALSE; pdata->guid = pDPSessionDesc->guidInstance; pdata->dwRefresh = 1; #ifdef UNICODE hItem = AddItemToTree( hWnd, pDPSessionDesc->lpszSessionName, (DWORD)pdata, 1); #else hItem = AddItemToTree( hWnd, pDPSessionDesc->lpszSessionNameA, (DWORD)pdata, 1); #endif if( hItem ) { // It was new TreeView_SelectItem( hWnd, hItem ); // Enumerate players for new session DPUtil_EnumPlayers( (GUID*)&pDPSessionDesc->guidInstance, EnumPlayer, (VOID*)hWnd, DPENUMPLAYERS_SESSION ); } else { // It was already in the tree free( pdata ); } return TRUE; } //----------------------------------------------------------------------------- // Name: ResetTreeRefresh() // Desc: //----------------------------------------------------------------------------- VOID ResetTreeRefresh( HWND hwndTV ) { TV_ITEM item; HTREEITEM hItem; TREEDATA* psearchdata; memset( &item, 0, sizeof(item) ); // Get the first app item (child of the running apps item) hItem = (HTREEITEM)SendMessage( hwndTV, TVM_GETNEXTITEM, TVGN_ROOT,(LPARAM) NULL); while (hItem) { // gets its data item.mask = TVIF_PARAM; item.hItem = hItem; SendMessage( hwndTV, TVM_GETITEM, 0, (LPARAM)&item ); // set the dwRefresh to 0 psearchdata = (TREEDATA*)item.lParam; if (psearchdata) psearchdata->dwRefresh = 0; SendMessage( hwndTV, TVM_SETITEM, 0, (LPARAM)&item ); // get the next one hItem = (HTREEITEM)SendMessage( hwndTV, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)hItem ); } return ; } //----------------------------------------------------------------------------- // Name: RemoveStaleSessions() // Desc: //----------------------------------------------------------------------------- VOID RemoveStaleSessions( HWND hwndTV ) { TV_ITEM item; HTREEITEM hItem,hItemNext; TREEDATA* psearchdata; memset(&item,0,sizeof(item)); // get the first app item (child of the running apps item) hItem = (HTREEITEM)SendMessage( hwndTV, TVM_GETNEXTITEM, TVGN_ROOT,(LPARAM) NULL); while (hItem) { // get the next one hItemNext = (HTREEITEM)SendMessage( hwndTV, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)hItem ); // gets its data item.mask = TVIF_PARAM; item.hItem = hItem; SendMessage( hwndTV, TVM_GETITEM, 0, (LPARAM)&item ); // if dwRefresh is still 0, it wasn't enum'ed psearchdata = (TREEDATA*)item.lParam; if (psearchdata && (0 == psearchdata->dwRefresh)) { free(psearchdata); SendMessage( hwndTV, TVM_DELETEITEM, 0, (LPARAM)hItem ); } hItem = hItemNext; } return; } //----------------------------------------------------------------------------- // Name: DlgProcJoinSession() // Desc: Dialog procedure for Join Session Dialog //----------------------------------------------------------------------------- BOOL CALLBACK DlgProcJoinSession( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam ) { static NM_TREEVIEW nmtv; static HWND hWndCtl; static TCHAR strPlayerName[MAX_PLAYERNAME+1]; static HANDLE dphEvent = NULL; static UINT idTimer = 0; TV_ITEM tvItem; TCHAR strSessionName[MAX_SESSIONNAME+1]; DWORD dwPNameSize; HRESULT hr = DP_OK; HTREEITEM htiCur, htiNext; GUID* pGuid; switch( msg ) { case WM_NOTIFY: switch (((NMHDR FAR *) lParam)->code) { case PSN_SETACTIVE: // Only the back button is valid at this point PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_BACK); if( hWndCtl ) TreeView_DeleteAllItems( hWndCtl ); // Enum sessions and let dplay decide the timeout hr = DPUtil_EnumSessions( 0, EnumSession, (VOID*)hWndCtl, DPENUMSESSIONS_ASYNC ); if( FAILED(hr) ) return FALSE; // Enumerate players for all sessions ghtiSession = TreeView_GetFirstVisible( hWndCtl ); while( ghtiSession ) { // delete previous players from display if ((htiNext = htiCur = TreeView_GetChild(hWndCtl, ghtiSession)) != (HTREEITEM)0) { do { htiNext = TreeView_GetNextSibling(hWndCtl, htiCur); TreeView_DeleteItem(hWndCtl, htiCur); htiCur = htiNext; } while (htiNext); } tvItem.hItem = ghtiSession; tvItem.pszText = strSessionName; tvItem.cchTextMax = MAX_SESSIONNAME; TreeView_GetItem(hWndCtl, &tvItem); // enumerate players for selected session DPUtil_EnumPlayers( (GUID*)tvItem.lParam, EnumPlayer, (LPVOID) hWndCtl, DPENUMPLAYERS_SESSION); ghtiSession = TreeView_GetNextItem(hWndCtl, ghtiSession, TVGN_NEXTVISIBLE); } // set Finish button highlite if (GetDlgItemText(hDlg, IDC_JOIN_PLAYERNAME, strPlayerName, MAX_PLAYERNAME) && (htiCur = TreeView_GetSelection(hWndCtl))) { PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_BACK|PSWIZB_FINISH); } else { PropSheet_SetWizButtons(GetParent(hDlg),PSWIZB_BACK|PSWIZB_DISABLEDFINISH); } SetFocus(hWndCtl); // start up timer idTimer = SetTimer(hDlg, ENUM_TIMER_ID, ENUM_TIMEOUT, NULL); return(TRUE); case PSN_WIZFINISH: if (idTimer) { KillTimer(hDlg, idTimer); idTimer = 0; } // add user selections to registry if (ghDuelKey) { RegSet(TEXT("PlayerName"), (CONST BYTE *)strPlayerName, sizeof(strPlayerName)); } // get the session guid if (nmtv.itemNew.lParam) { // user selected a session item, so just grab its lParam pGuid = (GUID*)nmtv.itemNew.lParam; } else { // user selected a player item, so grab its parent's (session) lParam htiCur = TreeView_GetParent(hWndCtl, nmtv.itemNew.hItem); if (!htiCur) { // fail finish ShowError(IDS_WIZARD_ERROR_GSG); SetWindowLong(hDlg, DWL_MSGRESULT, -1); return (TRUE); } tvItem.hItem = htiCur; tvItem.pszText = strSessionName; tvItem.cchTextMax = MAX_SESSIONNAME; TreeView_GetItem(hWndCtl, &tvItem); pGuid = (GUID*)tvItem.lParam; } // Open session if( FAILED( hr = DPUtil_OpenSession( pGuid ) ) ) { // Fail finish ShowError(IDS_DPLAY_ERROR_JS); SetWindowLong(hDlg, DWL_MSGRESULT, -1); return TRUE; } // create player if( FAILED( hr = DPUtil_CreatePlayer( &g_LocalPlayerDPID, strPlayerName, NULL, NULL, 0 ) ) ) { // fail finish ShowError(IDS_DPLAY_ERROR_CP); SetWindowLong(hDlg, DWL_MSGRESULT, -1); return (TRUE); } // everything went well, release allocated memory and finish ReleaseSessionData(hWndCtl); ReleaseSPData(); return(TRUE); case PSN_QUERYCANCEL: if (idTimer) { KillTimer(hDlg, idTimer); idTimer = 0; } DPUtil_EnumSessions(0, EnumSession, (LPVOID)hWndCtl, DPENUMSESSIONS_STOPASYNC); ReleaseSessionData(hWndCtl); ReleaseSPData(); DPUtil_Release(); // Indicates no game session return(TRUE); case PSN_WIZBACK: if (idTimer) { KillTimer(hDlg, idTimer); idTimer = 0; } DPUtil_EnumSessions(0, EnumSession, (LPVOID)hWndCtl, DPENUMSESSIONS_STOPASYNC); ReleaseSessionData(hWndCtl); return(TRUE); case TVN_SELCHANGING: nmtv = *((NM_TREEVIEW *) lParam); // set Finish button status if (GetDlgItemText(hDlg, IDC_JOIN_PLAYERNAME, strPlayerName, MAX_PLAYERNAME) && (htiCur = TreeView_GetSelection(hWndCtl))) { PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_BACK|PSWIZB_FINISH); } else { PropSheet_SetWizButtons(GetParent(hDlg),PSWIZB_BACK|PSWIZB_DISABLEDFINISH); } return(FALSE); case NM_CLICK: return(FALSE); } break; case WM_INITDIALOG: // setup title fonts SendDlgItemMessage(hDlg, IDC_JOIN_SESSION_TITLE, WM_SETFONT, (WPARAM)ghHeadingFont, MAKELPARAM(TRUE,0)); SendDlgItemMessage(hDlg, IDC_JOIN_PLAYER_TITLE, WM_SETFONT, (WPARAM)ghHeadingFont, MAKELPARAM(TRUE,0)); // setup user's previous data dwPNameSize = MAX_PLAYERNAME+1; strPlayerName[0]=0; if (ghDuelKey && (RegGet(TEXT("PlayerName"),(LPBYTE)strPlayerName,&dwPNameSize) == ERROR_SUCCESS)) SetDlgItemText(hDlg, IDC_JOIN_PLAYERNAME, strPlayerName); hWndCtl = GetDlgItem(hDlg, IDC_JOIN_SESSION); if (hWndCtl == NULL) return(TRUE); InitTreeViewImageLists(hWndCtl); return(TRUE); case WM_TIMER: // guard against leftover timer messages after timer has been killed if (!idTimer) break; ResetTreeRefresh(hWndCtl); // enum sessions and let dplay decide the timeout hr = DPUtil_EnumSessions(0, EnumSession, (LPVOID) hWndCtl, DPENUMSESSIONS_ASYNC); if (FAILED(hr) && hr != DPERR_CONNECTING) { KillTimer(hDlg, idTimer); idTimer = 0; if (hr != DPERR_USERCANCEL) ShowError(IDS_DPLAY_ERROR_ES); } RemoveStaleSessions(hWndCtl); // set Finish button highlite if (GetDlgItemText(hDlg, IDC_JOIN_PLAYERNAME, strPlayerName, MAX_PLAYERNAME) && (htiCur = TreeView_GetSelection(hWndCtl))) { PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_BACK|PSWIZB_FINISH); } else { PropSheet_SetWizButtons(GetParent(hDlg),PSWIZB_BACK|PSWIZB_DISABLEDFINISH); } break; case WM_COMMAND: switch (LOWORD(wParam)) { case IDC_JOIN_PLAYERNAME: if (HIWORD(wParam) == EN_CHANGE) { // set Finish button status if (GetDlgItemText(hDlg, IDC_JOIN_PLAYERNAME, strPlayerName, MAX_PLAYERNAME) && (htiCur = TreeView_GetSelection(hWndCtl))) { PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_BACK|PSWIZB_FINISH); } else { PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_BACK|PSWIZB_DISABLEDFINISH); } } break; } break; } return FALSE; } //----------------------------------------------------------------------------- // Name: DlgProcHostSession() // Desc: Dialog proc for Host Session Dialog //----------------------------------------------------------------------------- BOOL CALLBACK DlgProcHostSession(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) { static TCHAR strSessionName[MAX_SESSIONNAME+1]; static TCHAR strPlayerName[MAX_PLAYERNAME+1]; HRESULT hr; DWORD dwPNameSize, dwSNameSize; switch( msg ) { case WM_NOTIFY: switch (((NMHDR FAR *) lParam)->code) { case PSN_SETACTIVE: if (GetDlgItemText(hDlg, IDC_HOST_SESSIONNAME, strSessionName, MAX_SESSIONNAME) && GetDlgItemText(hDlg, IDC_HOST_PLAYERNAME, strPlayerName, MAX_PLAYERNAME)) { PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_BACK|PSWIZB_FINISH); } else { PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_BACK|PSWIZB_DISABLEDFINISH); } return(TRUE); case PSN_WIZFINISH: // add user selections to registry if (ghDuelKey) { RegSet(TEXT("PlayerName"), (CONST BYTE *)strPlayerName, sizeof(strPlayerName)); RegSet(TEXT("SessionName"), (CONST BYTE *)strSessionName, sizeof(strSessionName)); } // create session if ((hr = DPUtil_CreateSession(strSessionName)) != DP_OK) { // fail finish ShowError(IDS_DPLAY_ERROR_CS); SetWindowLong(hDlg, DWL_MSGRESULT, -1); return (TRUE); } // create player if( FAILED( hr = DPUtil_CreatePlayer( &g_LocalPlayerDPID, strPlayerName, NULL, NULL, 0 ) ) ) { ShowError(IDS_DPLAY_ERROR_CP); SetWindowLong(hDlg, DWL_MSGRESULT, -1); return (TRUE); } // everything went well, release allocated memory and finish ReleaseSPData(); return(TRUE); case PSN_WIZBACK: SetWindowLong(hDlg, DWL_MSGRESULT, IDD_CHOOSEPROVIDER); return(TRUE); case PSN_QUERYCANCEL: // release allocated memory ReleaseSPData(); // release dplay DPUtil_Release(); // Indicates no game session return(TRUE); } break; case WM_INITDIALOG: // setup title font SendDlgItemMessage(hDlg, IDC_HOST_TITLE, WM_SETFONT, (WPARAM)ghTitleFont, MAKELPARAM(TRUE,0)); SendDlgItemMessage(hDlg, IDC_HOST_SESSION_TITLE, WM_SETFONT, (WPARAM)ghHeadingFont, MAKELPARAM(TRUE,0)); SendDlgItemMessage(hDlg, IDC_HOST_PLAYER_TITLE, WM_SETFONT, (WPARAM)ghHeadingFont, MAKELPARAM(TRUE,0)); dwPNameSize = MAX_PLAYERNAME+1; dwSNameSize = MAX_SESSIONNAME+1; strPlayerName[0]=0; strSessionName[0]=0; if (ghDuelKey) { if (RegGet(TEXT("PlayerName"), (LPBYTE)strPlayerName, &dwPNameSize) == ERROR_SUCCESS) SetDlgItemText(hDlg, IDC_HOST_PLAYERNAME, strPlayerName); if (RegGet(TEXT("SessionName"), (LPBYTE)strSessionName, &dwSNameSize) == ERROR_SUCCESS) SetDlgItemText(hDlg, IDC_HOST_SESSIONNAME, strSessionName); } return(TRUE); case WM_COMMAND: switch (LOWORD(wParam)) { case IDC_HOST_SESSIONNAME: if (HIWORD(wParam) == EN_CHANGE) { if (GetDlgItemText(hDlg, IDC_HOST_SESSIONNAME, strSessionName, MAX_SESSIONNAME) && GetDlgItemText(hDlg, IDC_HOST_PLAYERNAME, strPlayerName, MAX_PLAYERNAME)) { PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_BACK|PSWIZB_FINISH); } else { PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_BACK|PSWIZB_DISABLEDFINISH); } return TRUE; } break; case IDC_HOST_PLAYERNAME: if (HIWORD(wParam) == EN_CHANGE) { if (GetDlgItemText(hDlg, IDC_HOST_SESSIONNAME, strSessionName, MAX_SESSIONNAME) && GetDlgItemText(hDlg, IDC_HOST_PLAYERNAME, strPlayerName, MAX_PLAYERNAME)) { PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_BACK|PSWIZB_FINISH); } else { PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_BACK|PSWIZB_DISABLEDFINISH); } } break; } break; } return FALSE; } //----------------------------------------------------------------------------- // Name: InitTreeViewImageLists() // Desc: Creates an image list, adds bitmaps to it, and associates the image // list with a tree-view control. //----------------------------------------------------------------------------- BOOL InitTreeViewImageLists(HWND hwndTV) { HIMAGELIST himl; // handle of image list HBITMAP hbmp; // handle of bitmap // Create the image list. if ((himl = ImageList_Create(CX_BITMAP, CY_BITMAP, FALSE, NUM_BITMAPS, 0)) == NULL) return FALSE; // Add the session and player bitmaps. hbmp = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_CLOSED_SESSION)); gnSession = ImageList_Add(himl, hbmp, (HBITMAP) NULL); DeleteObject(hbmp); hbmp = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_PLAYER)); gnPlayer = ImageList_Add(himl, hbmp, (HBITMAP) NULL); DeleteObject(hbmp); // Fail if not all of the images were added. if (ImageList_GetImageCount(himl) < 2) return FALSE; // Associate the image list with the tree-view control. TreeView_SetImageList(hwndTV, himl, TVSIL_NORMAL); return TRUE; } //----------------------------------------------------------------------------- // Name: FindItemInTree() // Desc: //----------------------------------------------------------------------------- HTREEITEM FindItemInTree( HWND hwndTV, TREEDATA* pdata ) { TV_ITEM item; HTREEITEM hItem; TREEDATA* psearchdata; memset(&item,0,sizeof(item)); // get the first app item (child of the running apps item) hItem = (HTREEITEM)SendMessage( hwndTV, TVM_GETNEXTITEM, TVGN_ROOT,(LPARAM) NULL); while (hItem) { // gets its data item.mask = TVIF_PARAM; item.hItem = hItem; SendMessage( hwndTV, TVM_GETITEM, 0, (LPARAM)&item ); // is this the one? psearchdata = (TREEDATA*)item.lParam; if( IsEqualGUID( psearchdata->guid, pdata->guid ) ) { // mark the item as seen psearchdata->dwRefresh = 1; SendMessage( hwndTV, TVM_SETITEM, 0, (LPARAM)&item ); return hItem; } // try the next one hItem = (HTREEITEM)SendMessage( hwndTV, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)hItem ); } return NULL; } //----------------------------------------------------------------------------- // Name: AddItemToTree() // Desc: Adds items to a tree-view control. //----------------------------------------------------------------------------- HTREEITEM AddItemToTree( HWND hwndTV, TCHAR* strItem, DWORD dwData, int nLevel ) { static HTREEITEM hPrev = (HTREEITEM) TVI_FIRST; static HTREEITEM hPrevRootItem = NULL; static HTREEITEM hPrevLev2Item = NULL; HTREEITEM hItem; TV_ITEM tvi; TV_INSERTSTRUCT tvins; if( 1 == nLevel ) { hItem = FindItemInTree( hwndTV, (TREEDATA*)dwData ); if (hItem) return NULL; } memset( &tvi,0,sizeof(tvi) ); memset( &tvins,0,sizeof(tvins) ); tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM; // Set the state if (nLevel == 1) { tvi.mask |= TVIF_STATE; tvi.state = TVIS_SELECTED; } // Set the text of the item. tvi.pszText = strItem; // Set the image if (nLevel == 1) { tvi.iImage = gnSession; tvi.iSelectedImage = gnSession; } else { tvi.iImage = gnPlayer; tvi.iSelectedImage = gnPlayer; } // Save the heading level in the item's application-defined // data area. tvi.lParam = (LPARAM) dwData; #ifdef NONAMELESSUNION tvins.u.item = tvi; #else tvins.item = tvi; #endif tvins.hInsertAfter = hPrev; // Set the parent item based on the specified level. if (nLevel == -1) tvins.hParent = hPrevRootItem; else if (nLevel == 1) tvins.hParent = TVI_ROOT; else if (nLevel == 2) tvins.hParent = hPrevRootItem; else tvins.hParent = hPrevLev2Item; // Add the item to the tree-view control. hPrev = (HTREEITEM) SendMessage(hwndTV, TVM_INSERTITEM, 0, (LPARAM) (LPTV_INSERTSTRUCT) &tvins); // Save the handle of the item. if (nLevel == 1) hPrevRootItem = hPrev; else if (nLevel == 2) hPrevLev2Item = hPrev; return hPrev; } //----------------------------------------------------------------------------- // Name: ReleaseSessionData() // Desc: //----------------------------------------------------------------------------- VOID ReleaseSessionData(HWND hWndCtl) { HTREEITEM htiSession = TreeView_GetRoot( hWndCtl ); while( htiSession ) { TCHAR strSessionName[MAX_SESSIONNAME+1]; TV_ITEM tvItem; tvItem.hItem = htiSession; tvItem.pszText = strSessionName; tvItem.cchTextMax = MAX_SESSIONNAME; TreeView_GetItem( hWndCtl, &tvItem ); if( tvItem.lParam ) free( (VOID*)tvItem.lParam ); htiSession = TreeView_GetNextSibling( hWndCtl, htiSession ); } } //----------------------------------------------------------------------------- // Name: ReleaseSPData() // Desc: Releases the memory allocated for service provider guids // depends on global variable ghWndSPControl //----------------------------------------------------------------------------- VOID ReleaseSPData() { LPVOID lpCon = NULL; int count,index; if (ghWndSPCtl) { count = SendMessage(ghWndSPCtl, LB_GETCOUNT, 0, 0); for (index = 0; index < count; index++) { lpCon = (LPVOID) SendMessage(ghWndSPCtl, LB_GETITEMDATA, index, 0); if (lpCon) GlobalFreePtr(lpCon); } } }