home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / graphics / directx / duel / wizard.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-07-15  |  29.8 KB  |  1,125 lines

  1. /*==========================================================================
  2.  *
  3.  *  Copyright (C) 1995-1997 Microsoft Corporation. All Rights Reserved.
  4.  *
  5.  *  File:       wizard.c
  6.  *  Content:    User input (setup wizard) related code
  7.  *
  8.  *
  9.  ***************************************************************************/
  10. #include <windows.h>
  11. #include "prsht.h"
  12. #include "wizard.h"
  13. #include "util.h"
  14. #include "comm.h"
  15. #include "gameproc.h"
  16.  
  17. /*
  18.  * Externals
  19.  */
  20. extern HINSTANCE                ghinst;                // program instance
  21. extern HWND                        ghWndMain;            // main window
  22. extern BOOL                        gbIsHost;            // is the user hosting/joining a game
  23. extern DPID                        gOurID;                // player id
  24. extern LPGUID                    glpGuid;            // duel's guid
  25. extern LPDIRECTPLAY3            glpDP3A;                // An IDirectPlay3 interface.
  26.  
  27. /*
  28.  * Globals to this module
  29.  */
  30. static HFONT                    ghTitleFont;        // font for titles on setup wizard
  31. static HFONT                    ghHeadingFont;        // font for headings on setup wizard
  32. static int                        gnSession,gnPlayer; // indexes for tree view images
  33. static HKEY                        ghDuelKey=NULL;        // duel registry key handle
  34. static DWORD                    gdwDuelDisp;        // key created or opened
  35. static HTREEITEM                ghtiSession;        // points to a visible session item in tree control
  36.                                                     //  used for inserting players into tree control
  37. static HWND                        ghWndSPCtl;            // handle to service provider control
  38.  
  39. /*
  40.  * SetupFonts
  41.  *
  42.  * Initializes font structures (used for wizard controls)
  43.  */
  44. void SetupFonts(HDC hDC)
  45. {
  46.     LOGFONT lf;
  47.     TCHAR tszFontName[MAX_FONTNAME];
  48.  
  49.     LoadString(ghinst, IDS_WIZARD_FONTNAME, tszFontName, MAX_FONTNAME);
  50.  
  51.     ZeroMemory(&lf,sizeof(lf));
  52.     lf.lfHeight = -MulDiv(11,GetDeviceCaps(hDC, LOGPIXELSY),72);
  53.     lf.lfWeight = 500;
  54.     lf.lfItalic = TRUE;
  55.     _tcscpy(lf.lfFaceName,tszFontName);
  56.     ghTitleFont = CreateFontIndirect(&lf);
  57.  
  58.     ZeroMemory(&lf,sizeof(lf));
  59.     lf.lfHeight = -MulDiv(11,GetDeviceCaps(hDC, LOGPIXELSY),72);
  60.     lf.lfWeight = 500;
  61.     _tcscpy(lf.lfFaceName,tszFontName);
  62.     ghHeadingFont = CreateFontIndirect(&lf);
  63. }
  64.  
  65. /*
  66.  * CleanupFonts
  67.  *
  68.  * Cleans up font structures
  69.  */
  70. void CleanupFonts(void)
  71. {
  72.     if (ghTitleFont) DeleteObject(ghTitleFont);
  73.     if (ghHeadingFont) DeleteObject(ghHeadingFont);
  74. }
  75.  
  76. /*
  77.  * RegSet
  78.  *
  79.  * Stores a data value in the registry
  80.  */
  81. LONG RegSet(LPCTSTR lptszName, CONST BYTE * lpData, DWORD dwSize)
  82. {
  83. #ifdef UNICODE
  84.     dwSize *= 2; // calc number of bytes
  85. #endif
  86.     return RegSetValueEx(ghDuelKey, lptszName, 0, REG_SZ, lpData, dwSize);
  87. }
  88.  
  89. /*
  90.  * RegSetA
  91.  *
  92.  * Stores data as an ascii string in the registry
  93.  */
  94. LONG RegSetA(LPCTSTR lptszName, CONST BYTE * lpData, DWORD dwSize)
  95. {
  96.     return RegSetValueEx(ghDuelKey, lptszName, 0, REG_SZ, lpData, dwSize);
  97. }
  98.  
  99. /*
  100.  * RegGet
  101.  *
  102.  * Queries a value from the registry
  103.  */
  104. LONG RegGet(LPCTSTR lptszName, LPBYTE lpData, LPDWORD lpdwDataSize)
  105. {
  106.     DWORD dwType;
  107.  
  108.     return RegQueryValueEx(ghDuelKey, lptszName, NULL, &dwType, lpData, lpdwDataSize);
  109. }
  110.  
  111. /*
  112.  * DoWizard
  113.  *
  114.  * Creates and launches a wizard (property sheets) for user input
  115.  */
  116. DWORD WINAPI DoWizard(LPVOID pv)
  117. {
  118.     PROPSHEETPAGE psp[4];
  119.     PROPSHEETHEADER psh;
  120.     TCHAR tszTitle1[MAX_WINDOWTITLE];
  121.     TCHAR tszTitle2[MAX_WINDOWTITLE];
  122.     TCHAR tszTitle3[MAX_WINDOWTITLE];
  123.     TCHAR tszTitle4[MAX_WINDOWTITLE];
  124.     TCHAR tszTitle5[MAX_WINDOWTITLE];
  125.     HDC hDC;
  126.  
  127.     CoInitialize( NULL );
  128.     
  129.     LoadString(ghinst, IDS_WIZARD_TITLE_SP, tszTitle1, MAX_WINDOWTITLE); 
  130.     LoadString(ghinst, IDS_WIZARD_TITLE_GS, tszTitle2, MAX_WINDOWTITLE); 
  131.     LoadString(ghinst, IDS_WIZARD_TITLE_JS, tszTitle3, MAX_WINDOWTITLE); 
  132.     LoadString(ghinst, IDS_WIZARD_TITLE_HS, tszTitle4, MAX_WINDOWTITLE); 
  133.     LoadString(ghinst, IDS_WIZARD_TITLE, tszTitle5, MAX_WINDOWTITLE); 
  134.  
  135.     psp[0].dwSize = sizeof(PROPSHEETPAGE);
  136.     psp[0].dwFlags = PSP_USETITLE;
  137.     psp[0].hInstance = ghinst;
  138.     psp[0].pszTemplate = MAKEINTRESOURCE(IDD_CHOOSEPROVIDER);
  139.     psp[0].pszIcon = NULL; 
  140.     psp[0].pfnDlgProc = DlgProcChooseProvider;
  141.     psp[0].pszTitle = tszTitle1;
  142.     psp[0].lParam = 0;
  143.     psp[0].pfnCallback = NULL;
  144.  
  145.     psp[1].dwSize = sizeof(PROPSHEETPAGE);
  146.     psp[1].dwFlags = PSP_USETITLE;
  147.     psp[1].hInstance = ghinst;
  148.     psp[1].pszTemplate = MAKEINTRESOURCE(IDD_GAMESETUP);
  149.     psp[1].pszIcon = NULL; 
  150.     psp[1].pfnDlgProc = DlgProcGameSetup;
  151.     psp[1].pszTitle = tszTitle2;
  152.     psp[1].lParam = 0;
  153.     psp[1].pfnCallback = NULL;
  154.  
  155.     psp[2].dwSize = sizeof(PROPSHEETPAGE);
  156.     psp[2].dwFlags = PSP_USETITLE;
  157.     psp[2].hInstance = ghinst;
  158.     psp[2].pszTemplate = MAKEINTRESOURCE(IDD_JOINSESSION);
  159.     psp[2].pszIcon = NULL; 
  160.     psp[2].pfnDlgProc = DlgProcJoinSession;
  161.     psp[2].pszTitle = tszTitle3;
  162.     psp[2].lParam = 0;
  163.     psp[2].pfnCallback = NULL;
  164.  
  165.     psp[3].dwSize = sizeof(PROPSHEETPAGE);
  166.     psp[3].dwFlags = PSP_USETITLE;
  167.     psp[3].hInstance = ghinst;
  168.     psp[3].pszTemplate = MAKEINTRESOURCE(IDD_HOSTSESSION);
  169.     psp[3].pszIcon = NULL; 
  170.     psp[3].pfnDlgProc = DlgProcHostSession;
  171.     psp[3].pszTitle = tszTitle4;
  172.     psp[3].lParam = 0;
  173.     psp[3].pfnCallback = NULL;
  174.     
  175.     psh.dwSize = sizeof(PROPSHEETHEADER);
  176.     psh.dwFlags = PSH_PROPSHEETPAGE | PSH_WIZARD;
  177.     psh.hwndParent = ghWndMain;
  178.     psh.hInstance = ghinst;
  179.     psh.pszIcon = NULL;
  180.     psh.pszCaption = tszTitle5;
  181.     psh.nPages = sizeof(psp) / sizeof(PROPSHEETPAGE);
  182.     psh.nStartPage = 0;
  183.     psh.ppsp = (LPCPROPSHEETPAGE) &psp;
  184.     psh.pfnCallback = NULL;
  185.  
  186.     // open/create duel registry key 
  187.     if (ERROR_SUCCESS != RegCreateKeyEx(HKEY_CURRENT_USER,
  188.                                         DUEL_KEY,
  189.                                         0,
  190.                                         NULL,                    
  191.                                         REG_OPTION_NON_VOLATILE,
  192.                                         KEY_ALL_ACCESS,
  193.                                         NULL,
  194.                                         &ghDuelKey,
  195.                                         &gdwDuelDisp
  196.                                         ))
  197.     {
  198.         goto ABORT;
  199.     }
  200.  
  201.     hDC = GetDC(ghWndMain);
  202.     if (!hDC)
  203.     {
  204.         goto ABORT;
  205.     }
  206.  
  207.     // setup fonts
  208.     SetupFonts(hDC);
  209.  
  210.     // launch the wizard
  211.     PropertySheet(&psh);
  212.  
  213.     // was communication initialized correctly ?
  214.     if (!IsDPlay())
  215.     {
  216.         goto ABORT;
  217.     }
  218.  
  219.     // cleanup
  220.     CleanupFonts();
  221.     ReleaseDC(ghWndMain, hDC);
  222.  
  223.     CoUninitialize();
  224.     // success
  225.     PostMessage( ghWndMain, UM_LAUNCH, 0, 0 );
  226.     return 0;
  227.  
  228. ABORT:
  229.     // failure
  230.     CleanupFonts();
  231.     if (hDC) 
  232.     {
  233.         ReleaseDC(ghWndMain, hDC);
  234.     }
  235.     PostMessage( ghWndMain, UM_ABORT, 0, 0 );
  236.     return 1;
  237. }
  238.  
  239. /*
  240.  * DPEnumConnectionsCallback
  241.  *
  242.  * creates SP list box entries and associates connection data with them
  243.  */
  244. BOOL FAR PASCAL DPEnumConnectionsCallback(
  245.                         LPCGUID            lpguidSP,
  246.                         LPVOID            lpConnection,
  247.                         DWORD            dwSize,
  248.                         LPCDPNAME        lpName,
  249.                         DWORD            dwFlags,
  250.                         LPVOID            lpContext)
  251. {
  252.  
  253.     HWND            hWnd = (HWND) lpContext;
  254.     LRESULT            iIndex;
  255.     LPVOID            lpConnectionBuffer;
  256.  
  257.     // store service provider name in combo box
  258.     iIndex = SendMessage(hWnd, LB_ADDSTRING, 0, (LPARAM) lpName->lpszShortNameA);
  259.  
  260.     if (iIndex == CB_ERR)
  261.         goto FAILURE;
  262.  
  263.     // make space for Connection Shortcut
  264.     lpConnectionBuffer = GlobalAllocPtr(GHND, dwSize);
  265.     if (lpConnectionBuffer == NULL)
  266.         goto FAILURE;
  267.  
  268.     // store pointer to GUID in combo box
  269.     memcpy(lpConnectionBuffer, lpConnection, dwSize);
  270.  
  271.     if (iIndex != LB_ERR)
  272.         SendMessage(hWnd, LB_SETITEMDATA, iIndex, (LPARAM) lpConnectionBuffer);
  273.  
  274. FAILURE:
  275.     return (TRUE);
  276. }
  277.  
  278. /*
  279.  * DlgProcChooseProvider
  280.  *
  281.  * Dialog procedure for the choose service provider dialog
  282.  */
  283. BOOL CALLBACK DlgProcChooseProvider(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
  284. {
  285.     LPVOID    lpCon = NULL;
  286.     static  LONG    iIndex;
  287.     static  HWND hWndCtl;
  288.     HRESULT hr;
  289.  
  290.     switch (msg)
  291.     {
  292.     case WM_NOTIFY:
  293.         switch (((NMHDR FAR *) lParam)->code) 
  294.         {
  295.         case PSN_SETACTIVE:
  296.             PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_NEXT);
  297.             return(TRUE);
  298.  
  299.         case PSN_WIZNEXT: 
  300.             // Release previously selected DPlay object, if any.
  301.             DPlayRelease();
  302.             if (iIndex != LB_ERR)
  303.             {
  304.                 lpCon = (LPVOID) SendMessage(hWndCtl, LB_GETITEMDATA, iIndex, 0);
  305.                 
  306.                 if (lpCon)
  307.                 {
  308.                     // create directplay object
  309.                     if ((hr = DPlayCreate(lpCon)) == DP_OK)
  310.                     {
  311.                         return(TRUE);
  312.                     }
  313.                     else
  314.                     {
  315.                         ShowError(IDS_DPLAY_ERROR_IDC);
  316.                     }
  317.                 }
  318.             }
  319.             SetWindowLong(hDlg, DWL_MSGRESULT, -1);
  320.             return(TRUE);
  321.  
  322.         case PSN_QUERYCANCEL:
  323.             ReleaseSPData();
  324.             DPlayRelease();
  325.             return(TRUE);
  326.         }
  327.         break;
  328.  
  329.     case WM_INITDIALOG:
  330.         SendDlgItemMessage(hDlg, IDC_SP_TITLE, WM_SETFONT, (WPARAM)ghTitleFont, MAKELPARAM(TRUE,0));
  331.  
  332.         hWndCtl = GetDlgItem(hDlg, IDC_SERVICEPROVIDERS);
  333.         if (hWndCtl == NULL) return(TRUE);
  334.  
  335.         // remember the service provider control. used later in freeing sp information.
  336.         ghWndSPCtl = hWndCtl;
  337.  
  338.         if ((hr = DPlayCreate(lpCon)) != DP_OK)
  339.         {
  340.             // class not available, so tell them to get DirectX 5 or later
  341.             if ((hr == REGDB_E_CLASSNOTREG) || (hr == REGDB_E_IIDNOTREG))
  342.                 ShowError(IDS_DPLAY_ERROR_CLSID);
  343.             else
  344.                 ShowError(IDS_DPLAY_ERROR_IDC);
  345.             SetWindowLong(hDlg, DWL_MSGRESULT, -1);
  346.             PostQuitMessage(-1);
  347.             return(TRUE);
  348.         }
  349.  
  350.         //Insert all available connections into the connection listbox.
  351.         IDirectPlay3_EnumConnections( glpDP3A, glpGuid, DPEnumConnectionsCallback, hWndCtl, 0);
  352.         SetFocus(hWndCtl);
  353.  
  354.         SendMessage(hWndCtl, LB_SETCURSEL, iIndex, 0);
  355.         iIndex = SendMessage(hWndCtl, LB_GETCURSEL, 0, 0);
  356.         return(TRUE);
  357.  
  358.     case WM_COMMAND:
  359.         if ( HIWORD(wParam) == LBN_SELCHANGE)
  360.         {
  361.             iIndex = SendMessage(hWndCtl, LB_GETCURSEL, 0, 0);
  362.             
  363.             return(TRUE);
  364.         }
  365.         break;
  366.     }
  367.     return (FALSE);
  368. }
  369.  
  370.  
  371. /*
  372.  * DlgProcGameSetup
  373.  *
  374.  * Dialog procedure for the Game Setup Dialog.
  375.  */
  376. BOOL CALLBACK DlgProcGameSetup (HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
  377. {
  378.     switch (msg)
  379.     {
  380.     case WM_NOTIFY:
  381.         switch (((NMHDR FAR *) lParam)->code) 
  382.         {
  383.             case PSN_SETACTIVE:
  384.                 PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_BACK | PSWIZB_NEXT);
  385.                 return(TRUE);
  386.             
  387.             case PSN_WIZNEXT: 
  388.                 if (gbIsHost)
  389.                     SetWindowLong(hDlg, DWL_MSGRESULT, IDD_HOSTSESSION);
  390.                 return(TRUE);
  391.  
  392.             case PSN_QUERYCANCEL:
  393.                 ReleaseSPData();
  394.                 DPlayRelease();
  395.                 return(TRUE);
  396.         }
  397.         break;
  398.  
  399.     case WM_INITDIALOG:
  400.         // setup title fonts
  401.         SendDlgItemMessage(hDlg, IDC_GAMESETUP_TITLE, WM_SETFONT, (WPARAM)ghTitleFont, MAKELPARAM(TRUE,0));
  402.         SendDlgItemMessage(hDlg, IDC_JOINSESSION, WM_SETFONT, (WPARAM)ghHeadingFont, MAKELPARAM(TRUE,0));
  403.         SendDlgItemMessage(hDlg, IDC_HOSTSESSION, WM_SETFONT, (WPARAM)ghHeadingFont, MAKELPARAM(TRUE,0));
  404.         // host by default
  405.         SendDlgItemMessage(hDlg, IDC_HOSTSESSION, BM_SETCHECK, 1, 0);
  406.         SendDlgItemMessage(hDlg, IDC_JOINSESSION, BM_SETCHECK, 0, 0);
  407.         gbIsHost = TRUE;
  408.         return(TRUE);
  409.  
  410.     case WM_COMMAND:
  411.         if (HIWORD(wParam) == BN_CLICKED)
  412.             switch (LOWORD(wParam))
  413.             {
  414.             case IDC_HOSTSESSION:
  415.                 if (SendDlgItemMessage(hDlg, IDC_HOSTSESSION, BM_GETCHECK, 1, 0) == 1)
  416.                 {
  417.                     gbIsHost = TRUE;
  418.                 }
  419.                 return(TRUE);
  420.             case IDC_JOINSESSION:
  421.                 if (SendDlgItemMessage(hDlg, IDC_JOINSESSION, BM_GETCHECK, 1, 0) == 1)
  422.                 {
  423.                     gbIsHost = FALSE;
  424.                 }
  425.                 return(TRUE);
  426.             }
  427.         break;
  428.     }
  429.     return(FALSE);
  430. }
  431.  
  432. /*
  433.  * EnumPlayer
  434.  *
  435.  * EnumeratePlayer callback. Inserts player information into the passed in tree view control.
  436.  */
  437. BOOL WINAPI EnumPlayer(DPID pidID, DWORD dwPlayerType, LPCDPNAME lpName,
  438.     DWORD dwFlags, LPVOID lpContext)
  439. {
  440.     HWND hWnd = (HWND) lpContext;
  441.     HTREEITEM hItem;
  442.  
  443. #ifdef UNICODE
  444.     hItem = AddItemToTree(hWnd, lpName->lpszShortName, 0, -1);
  445. #else
  446.     hItem = AddItemToTree(hWnd, lpName->lpszShortNameA, 0, -1);
  447. #endif
  448.  
  449.     return(TRUE);
  450. }
  451.  
  452. /*
  453.  * EnumSession
  454.  *
  455.  * EnumSessions callback. Inserts session description information in the passed in 
  456.  * tree view control.
  457.  */
  458. BOOL WINAPI EnumSession(LPCDPSESSIONDESC2 lpDPSessionDesc, LPDWORD lpdwTimeOut, DWORD dwFlags, 
  459.                         LPVOID lpContext)
  460. {
  461.     HWND hWnd = (HWND) lpContext;
  462.     HTREEITEM hItem;
  463.     LPTREEDATA lpdata;
  464.  
  465.     if(dwFlags & DPESC_TIMEDOUT) return FALSE;       // don't try again
  466.  
  467.     if (hWnd == NULL) return FALSE;
  468.  
  469.     // allocate memory to remember the guid
  470.     lpdata = (LPTREEDATA) malloc(sizeof(TREEDATA));
  471.     if (!lpdata) return FALSE;
  472.  
  473.     lpdata->guid = lpDPSessionDesc->guidInstance;
  474.     lpdata->dwRefresh = 1;
  475.     
  476. #ifdef UNICODE
  477.     hItem = AddItemToTree(hWnd, lpDPSessionDesc->lpszSessionName, (DWORD)lpdata, 1);
  478. #else
  479.     hItem = AddItemToTree(hWnd, lpDPSessionDesc->lpszSessionNameA, (DWORD)lpdata, 1);
  480. #endif
  481.  
  482.     if (hItem)
  483.     {
  484.         // it was new
  485.         TreeView_SelectItem(hWnd, hItem);        
  486.         // enumerate players for new session
  487.         DPlayEnumPlayers((LPGUID)&lpDPSessionDesc->guidInstance, EnumPlayer, (LPVOID) hWnd, DPENUMPLAYERS_SESSION);
  488.     }
  489.     else 
  490.     {
  491.         // it was already in the tree
  492.         free(lpdata);    
  493.     }
  494.  
  495.  
  496.     return(TRUE);
  497. }
  498.  
  499.  
  500. void ResetTreeRefresh(HWND hwndTV)
  501. {
  502.     TV_ITEM item;
  503.     HTREEITEM hItem;
  504.     LPTREEDATA psearchdata;
  505.  
  506.     memset(&item,0,sizeof(item));
  507.     
  508.     // get the first app item (child of the running apps item)
  509.     hItem = (HTREEITEM)SendMessage( hwndTV, TVM_GETNEXTITEM, TVGN_ROOT,(LPARAM) NULL);    
  510.     
  511.     while (hItem)
  512.     {
  513.         // gets its data
  514.         item.mask = TVIF_PARAM;
  515.         item.hItem = hItem;
  516.         SendMessage( hwndTV, TVM_GETITEM, 0, (LPARAM)&item );
  517.         // set the dwRefresh to 0
  518.         psearchdata = (LPTREEDATA)item.lParam;
  519.         if (psearchdata) psearchdata->dwRefresh = 0;
  520.         SendMessage( hwndTV, TVM_SETITEM, 0, (LPARAM)&item );
  521.  
  522.         // get the next one
  523.         hItem = (HTREEITEM)SendMessage( hwndTV, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)hItem );    
  524.     }    
  525.     
  526.     return ;
  527. }  // ResetTreeRefresh      
  528.  
  529. void RemoveStaleSessions(HWND hwndTV)
  530. {
  531.     TV_ITEM item;
  532.     HTREEITEM hItem,hItemNext;
  533.     LPTREEDATA psearchdata;
  534.  
  535.     memset(&item,0,sizeof(item));
  536.     
  537.     // get the first app item (child of the running apps item)
  538.     hItem = (HTREEITEM)SendMessage( hwndTV, TVM_GETNEXTITEM, TVGN_ROOT,(LPARAM) NULL);    
  539.     
  540.     while (hItem)
  541.     {
  542.         // get the next one
  543.         hItemNext = (HTREEITEM)SendMessage( hwndTV, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)hItem );    
  544.         
  545.         // gets its data
  546.         item.mask = TVIF_PARAM;
  547.         item.hItem = hItem;
  548.         SendMessage( hwndTV, TVM_GETITEM, 0, (LPARAM)&item );
  549.  
  550.         // if dwRefresh is still 0, it wasn't enum'ed
  551.         psearchdata = (LPTREEDATA)item.lParam;
  552.         if (psearchdata && (0 == psearchdata->dwRefresh))
  553.         {
  554.             free(psearchdata);
  555.             SendMessage( hwndTV, TVM_DELETEITEM, 0, (LPARAM)hItem );
  556.         }
  557.  
  558.         hItem = hItemNext;
  559.     }    
  560.     
  561.     return ;
  562.     
  563. }  // RemoveStaleSessions      
  564.  
  565. /*
  566.  * DlgProcJoinSession
  567.  *
  568.  * Dialog procedure for Join Session Dialog
  569.  */
  570. BOOL CALLBACK DlgProcJoinSession (HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
  571. {
  572.     static  NM_TREEVIEW nmtv;
  573.     static  HWND hWndCtl;
  574.     static  TCHAR tszPlayerName[MAX_PLAYERNAME+1];
  575.     static  HANDLE    dphEvent = NULL;
  576.     TV_ITEM tvItem;
  577.     TCHAR    tszSessionName[MAX_SESSIONNAME+1];
  578.     DWORD   dwPNameSize;
  579.     HRESULT hr;
  580.     HTREEITEM htiCur, htiNext;
  581.     LPGUID  lpGuid;
  582.  
  583.     switch (msg)
  584.     {
  585.     case WM_NOTIFY:
  586.         switch (((NMHDR FAR *) lParam)->code) 
  587.         {
  588.             case PSN_SETACTIVE:
  589.                 if (hWndCtl) TreeView_DeleteAllItems(hWndCtl);
  590.  
  591.                 // enum sessions and let dplay decide the timeout
  592.                 hr = DPlayEnumSessions(0, EnumSession, (LPVOID) hWndCtl, DPENUMSESSIONS_ASYNC);
  593.  
  594.                 if (FAILED(hr))
  595.                 {
  596.                     return (FALSE);
  597.                 }
  598.  
  599.                 // enumerate players for all sessions
  600.                 ghtiSession = TreeView_GetFirstVisible(hWndCtl);
  601.         
  602.                 while (ghtiSession)
  603.                 {
  604.                     // delete previous players from display
  605.                     if ((htiNext = htiCur = TreeView_GetChild(hWndCtl, ghtiSession)) 
  606.                         != (HTREEITEM)0)
  607.                     {
  608.                         do
  609.                         {
  610.                             htiNext = TreeView_GetNextSibling(hWndCtl, htiCur);
  611.                             TreeView_DeleteItem(hWndCtl, htiCur);
  612.                             htiCur = htiNext;
  613.                         } while (htiNext);
  614.                     }
  615.  
  616.                     tvItem.hItem = ghtiSession;
  617.                     tvItem.pszText = tszSessionName;
  618.                     tvItem.cchTextMax = MAX_SESSIONNAME;
  619.                     TreeView_GetItem(hWndCtl, &tvItem);
  620.                     // enumerate players for selected session
  621.                     DPlayEnumPlayers((LPGUID)tvItem.lParam, EnumPlayer, (LPVOID) hWndCtl, DPENUMPLAYERS_SESSION);
  622.                     ghtiSession = TreeView_GetNextItem(hWndCtl, ghtiSession, TVGN_NEXTVISIBLE);
  623.                 }
  624.  
  625.                 // set Finish button highlite
  626.                 if (GetDlgItemText(hDlg, IDC_JOIN_PLAYERNAME, tszPlayerName, MAX_PLAYERNAME) && 
  627.                     (htiCur = TreeView_GetSelection(hWndCtl)))
  628.                 {
  629.                     PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_BACK|PSWIZB_FINISH);
  630.                 }
  631.                 else
  632.                 {
  633.                     PropSheet_SetWizButtons(GetParent(hDlg),PSWIZB_BACK|PSWIZB_DISABLEDFINISH);
  634.                 }
  635.  
  636.                 SetFocus(hWndCtl);
  637.                 
  638.                 // start up timer
  639.                 SetTimer(hDlg, ENUM_TIMER_ID, ENUM_TIMEOUT, NULL);
  640.         
  641.                 return(TRUE);
  642.  
  643.             case PSN_WIZFINISH:
  644.                 KillTimer(hDlg, ENUM_TIMER_ID);
  645.                             
  646.                 // add user selections to registry
  647.                 if (ghDuelKey) 
  648.                 {
  649.                     RegSet(TEXT("PlayerName"), (CONST BYTE *)tszPlayerName, 
  650.                             sizeof(tszPlayerName));
  651.                 }
  652.     
  653.                 // get the session guid
  654.                 if (nmtv.itemNew.lParam)
  655.                 {
  656.                     // user selected a session item, so just grab its lParam
  657.                     lpGuid = (LPGUID)nmtv.itemNew.lParam;
  658.                 }
  659.                 else
  660.                 {
  661.                     // user selected a player item, so grab its parent's (session) lParam
  662.                     htiCur = TreeView_GetParent(hWndCtl, nmtv.itemNew.hItem);
  663.                     if (!htiCur)
  664.                     {
  665.                         // fail finish
  666.                         ShowError(IDS_WIZARD_ERROR_GSG);
  667.                         SetWindowLong(hDlg, DWL_MSGRESULT, -1);
  668.                         return (TRUE);
  669.                     }
  670.  
  671.                     tvItem.hItem = htiCur;
  672.                     tvItem.pszText = tszSessionName;
  673.                     tvItem.cchTextMax = MAX_SESSIONNAME;
  674.                     TreeView_GetItem(hWndCtl, &tvItem);                    
  675.                     lpGuid = (LPGUID)tvItem.lParam;
  676.                 }
  677.  
  678.                 // open session
  679.                 if ((hr = DPlayOpenSession(lpGuid)) != DP_OK)
  680.                 {
  681.                     // fail finish
  682.                     ShowError(IDS_DPLAY_ERROR_JS);
  683.                     SetWindowLong(hDlg, DWL_MSGRESULT, -1);
  684.                     return (TRUE);
  685.                 }
  686.  
  687.                 // create player
  688.                 if ((hr = DPlayCreatePlayer(&gOurID, tszPlayerName, NULL, NULL, 0)) != DP_OK)
  689.                 {
  690.                     // fail finish
  691.                     ShowError(IDS_DPLAY_ERROR_CP);
  692.                     SetWindowLong(hDlg, DWL_MSGRESULT, -1);
  693.                     return (TRUE);
  694.                 }
  695.  
  696.                 // everything went well, release allocated memory and finish
  697.                 ReleaseSessionData(hWndCtl);
  698.                 ReleaseSPData();                
  699.                 return(TRUE);
  700.  
  701.             case PSN_QUERYCANCEL:
  702.                 KillTimer(hDlg, ENUM_TIMER_ID);
  703.                 ReleaseSPData();
  704.                 ReleaseSessionData(hWndCtl);
  705.                 DPlayRelease();
  706.                 return(TRUE);
  707.  
  708.             case PSN_WIZBACK:
  709.                 KillTimer(hDlg, ENUM_TIMER_ID);
  710.                 ReleaseSessionData(hWndCtl);
  711.                 return(TRUE);
  712.  
  713.             case TVN_SELCHANGING:
  714.                 nmtv = *((NM_TREEVIEW *) lParam);
  715.  
  716.                 // set Finish button status 
  717.                 if (GetDlgItemText(hDlg, IDC_JOIN_PLAYERNAME, tszPlayerName, MAX_PLAYERNAME) && 
  718.                     (htiCur = TreeView_GetSelection(hWndCtl)))
  719.                 {
  720.                     PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_BACK|PSWIZB_FINISH);
  721.                 }
  722.                 else
  723.                 {
  724.                     PropSheet_SetWizButtons(GetParent(hDlg),PSWIZB_BACK|PSWIZB_DISABLEDFINISH);
  725.                 }
  726.                 return(FALSE);
  727.  
  728.             case NM_CLICK:
  729.                 return(FALSE);
  730.         }
  731.         break;
  732.     case WM_INITDIALOG:
  733.         // setup title fonts
  734.         SendDlgItemMessage(hDlg, IDC_JOIN_SESSION_TITLE, WM_SETFONT, (WPARAM)ghHeadingFont, MAKELPARAM(TRUE,0));
  735.         SendDlgItemMessage(hDlg, IDC_JOIN_PLAYER_TITLE, WM_SETFONT, (WPARAM)ghHeadingFont, MAKELPARAM(TRUE,0));
  736.  
  737.         // setup user's previous data
  738.         dwPNameSize = MAX_PLAYERNAME+1;
  739.         tszPlayerName[0]=0;
  740.         if (ghDuelKey && (RegGet(TEXT("PlayerName"),(LPBYTE)tszPlayerName,&dwPNameSize) == ERROR_SUCCESS))    
  741.             SetDlgItemText(hDlg, IDC_JOIN_PLAYERNAME, tszPlayerName);
  742.  
  743.         hWndCtl = GetDlgItem(hDlg, IDC_JOIN_SESSION);
  744.         if (hWndCtl == NULL) return(TRUE);
  745.         InitTreeViewImageLists(hWndCtl);
  746.         return(TRUE);
  747.         
  748.     case WM_TIMER:
  749.  
  750.         ResetTreeRefresh(hWndCtl);
  751.         // enum sessions and let dplay decide the timeout
  752.         DPlayEnumSessions(0, EnumSession, (LPVOID) hWndCtl, DPENUMSESSIONS_ASYNC);
  753.  
  754.         RemoveStaleSessions(hWndCtl);
  755.         
  756.             // set Finish button highlite
  757.         if (GetDlgItemText(hDlg, IDC_JOIN_PLAYERNAME, tszPlayerName, MAX_PLAYERNAME) && 
  758.             (htiCur = TreeView_GetSelection(hWndCtl)))
  759.         {
  760.             PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_BACK|PSWIZB_FINISH);
  761.         }
  762.         else
  763.         {
  764.             PropSheet_SetWizButtons(GetParent(hDlg),PSWIZB_BACK|PSWIZB_DISABLEDFINISH);
  765.         }
  766.         
  767.         break;
  768.         
  769.     case WM_COMMAND:
  770.  
  771.         switch (LOWORD(wParam))
  772.         {
  773.         case IDC_JOIN_PLAYERNAME:
  774.             if (HIWORD(wParam) == EN_CHANGE)
  775.             {
  776.                 // set Finish button status 
  777.                 if (GetDlgItemText(hDlg, IDC_JOIN_PLAYERNAME, tszPlayerName, MAX_PLAYERNAME) && 
  778.                     (htiCur = TreeView_GetSelection(hWndCtl)))
  779.                 {
  780.                     PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_BACK|PSWIZB_FINISH);
  781.                 }
  782.                 else
  783.                 {
  784.                     PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_BACK|PSWIZB_DISABLEDFINISH);
  785.                 }
  786.             }
  787.             break;
  788.         }
  789.         break;
  790.     }
  791.     return (FALSE);
  792. }
  793.  
  794. /*
  795.  * DlgProcHostSession
  796.  *
  797.  * Dialog proc for Host Session Dialog
  798.  */
  799. BOOL CALLBACK DlgProcHostSession(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
  800. {
  801.     static    TCHAR tszSessionName[MAX_SESSIONNAME+1], tszPlayerName[MAX_PLAYERNAME+1];
  802.     HRESULT hr;
  803.     DWORD    dwPNameSize, dwSNameSize;
  804.  
  805.     switch (msg) {
  806.     case WM_NOTIFY:
  807.         switch (((NMHDR FAR *) lParam)->code) 
  808.         {
  809.             case PSN_SETACTIVE:
  810.                 if (GetDlgItemText(hDlg, IDC_HOST_SESSIONNAME, tszSessionName, MAX_SESSIONNAME) && 
  811.                     GetDlgItemText(hDlg, IDC_HOST_PLAYERNAME, tszPlayerName, MAX_PLAYERNAME))
  812.                 {
  813.                     PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_BACK|PSWIZB_FINISH);
  814.                 }
  815.                 else
  816.                 {
  817.                     PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_BACK|PSWIZB_DISABLEDFINISH);
  818.                 }
  819.                 return(TRUE);
  820.     
  821.             case PSN_WIZFINISH:
  822.                 // add user selections to registry
  823.                 if (ghDuelKey) 
  824.                 {
  825.                     RegSet(TEXT("PlayerName"), (CONST BYTE *)tszPlayerName, sizeof(tszPlayerName));
  826.                     RegSet(TEXT("SessionName"), (CONST BYTE *)tszSessionName, sizeof(tszSessionName));
  827.                 }
  828.  
  829.                 // create session
  830.                 if ((hr = DPlayCreateSession(tszSessionName)) != DP_OK)
  831.                 {
  832.                     // fail finish
  833.                     ShowError(IDS_DPLAY_ERROR_CS);
  834.                     SetWindowLong(hDlg, DWL_MSGRESULT, -1);
  835.                     return (TRUE);
  836.                 }
  837.  
  838.                 // create player
  839.                 if ((hr = DPlayCreatePlayer(&gOurID, tszPlayerName, NULL, NULL, 0)) != DP_OK)
  840.                 {
  841.                     ShowError(IDS_DPLAY_ERROR_CP);
  842.                     SetWindowLong(hDlg, DWL_MSGRESULT, -1);
  843.                     return (TRUE);
  844.                 }
  845.  
  846.                 // everything went well, release allocated memory and finish
  847.                 ReleaseSPData();
  848.                 return(TRUE);
  849.             
  850.             case PSN_WIZBACK:
  851.                 SetWindowLong(hDlg, DWL_MSGRESULT, IDD_GAMESETUP);
  852.                 return(TRUE);
  853.  
  854.             case PSN_QUERYCANCEL:
  855.                 // release allocated memory
  856.                 ReleaseSPData();
  857.                 // release dplay
  858.                 DPlayRelease();
  859.                 return(TRUE);
  860.         }
  861.         break;
  862.     case WM_INITDIALOG:
  863.         // setup title font
  864.         SendDlgItemMessage(hDlg, IDC_HOST_TITLE, WM_SETFONT, (WPARAM)ghTitleFont, MAKELPARAM(TRUE,0));
  865.         SendDlgItemMessage(hDlg, IDC_HOST_SESSION_TITLE, WM_SETFONT, (WPARAM)ghHeadingFont, MAKELPARAM(TRUE,0));
  866.         SendDlgItemMessage(hDlg, IDC_HOST_PLAYER_TITLE, WM_SETFONT, (WPARAM)ghHeadingFont, MAKELPARAM(TRUE,0));
  867.  
  868.         dwPNameSize = MAX_PLAYERNAME+1;
  869.         dwSNameSize = MAX_SESSIONNAME+1;
  870.         tszPlayerName[0]=0;
  871.         tszSessionName[0]=0;
  872.         if (ghDuelKey)
  873.         {
  874.             if (RegGet(TEXT("PlayerName"), (LPBYTE)tszPlayerName, &dwPNameSize) == ERROR_SUCCESS)    
  875.                 SetDlgItemText(hDlg, IDC_HOST_PLAYERNAME, tszPlayerName);
  876.             if (RegGet(TEXT("SessionName"), (LPBYTE)tszSessionName, &dwSNameSize) == ERROR_SUCCESS)    
  877.                 SetDlgItemText(hDlg, IDC_HOST_SESSIONNAME, tszSessionName);
  878.         }
  879.         
  880.         return(TRUE);
  881.  
  882.     case WM_COMMAND:
  883.         switch (LOWORD(wParam))
  884.         {
  885.         case IDC_HOST_SESSIONNAME:
  886.             if (HIWORD(wParam) == EN_CHANGE)
  887.             {
  888.                 if (GetDlgItemText(hDlg, IDC_HOST_SESSIONNAME, tszSessionName, MAX_SESSIONNAME) && 
  889.                     GetDlgItemText(hDlg, IDC_HOST_PLAYERNAME, tszPlayerName, MAX_PLAYERNAME))
  890.                 {
  891.                     PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_BACK|PSWIZB_FINISH);
  892.                 }
  893.                 else
  894.                 {
  895.                     PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_BACK|PSWIZB_DISABLEDFINISH);
  896.                 }
  897.  
  898.                 return TRUE;
  899.             }
  900.             break;
  901.  
  902.         case IDC_HOST_PLAYERNAME:
  903.             if (HIWORD(wParam) == EN_CHANGE)
  904.             {
  905.                 if (GetDlgItemText(hDlg, IDC_HOST_SESSIONNAME, tszSessionName, MAX_SESSIONNAME) && 
  906.                     GetDlgItemText(hDlg, IDC_HOST_PLAYERNAME, tszPlayerName, MAX_PLAYERNAME))
  907.                 {
  908.                     PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_BACK|PSWIZB_FINISH);
  909.                 }
  910.                 else
  911.                 {
  912.                     PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_BACK|PSWIZB_DISABLEDFINISH);
  913.                 }
  914.             }
  915.             break;
  916.         }
  917.         break;
  918.     }
  919.  
  920.     return(FALSE);
  921. }
  922.  
  923. /*
  924.  * InitTreeViewImageLists - creates an image list, adds three bitmaps to 
  925.  *     it, and associates the image list with a tree-view control. 
  926.  * Returns TRUE if successful or FALSE otherwise. 
  927.  * hwndTV - handle of the tree-view control 
  928.  *
  929.  * Global variables and constants 
  930.  *     gnSession, and gnPlayer - integer variables for 
  931.  *         indexes of the images 
  932.  *     CX_BITMAP and CY_BITMAP - width and height of an icon 
  933.  *     NUM_BITMAPS - number of bitmaps to add to the image list 
  934.  */
  935. BOOL InitTreeViewImageLists(HWND hwndTV) 
  936.     HIMAGELIST himl;  // handle of image list 
  937.     HBITMAP hbmp;     // handle of bitmap 
  938.  
  939.     // Create the image list. 
  940.     if ((himl = ImageList_Create(CX_BITMAP, CY_BITMAP, 
  941.             FALSE, NUM_BITMAPS, 0)) == NULL) 
  942.         return FALSE; 
  943.  
  944.     // Add the session and player bitmaps. 
  945.     hbmp = LoadBitmap(ghinst, MAKEINTRESOURCE(IDB_CLOSED_SESSION)); 
  946.     gnSession = ImageList_Add(himl, hbmp, (HBITMAP) NULL); 
  947.     DeleteObject(hbmp); 
  948.  
  949.     hbmp = LoadBitmap(ghinst, MAKEINTRESOURCE(IDB_PLAYER)); 
  950.     gnPlayer = ImageList_Add(himl, hbmp, (HBITMAP) NULL); 
  951.     DeleteObject(hbmp); 
  952.  
  953.     // Fail if not all of the images were added. 
  954.     if (ImageList_GetImageCount(himl) < 2) 
  955.         return FALSE; 
  956.  
  957.     // Associate the image list with the tree-view control. 
  958.     TreeView_SetImageList(hwndTV, himl, TVSIL_NORMAL); 
  959.  
  960.     return TRUE; 
  961.    
  962. HTREEITEM FindItemInTree(HWND hwndTV,LPTREEDATA pdata)
  963. {
  964.     TV_ITEM item;
  965.     HTREEITEM hItem;
  966.     LPTREEDATA psearchdata;
  967.        
  968.     memset(&item,0,sizeof(item));
  969.     
  970.     // get the first app item (child of the running apps item)
  971.     hItem = (HTREEITEM)SendMessage( hwndTV, TVM_GETNEXTITEM, TVGN_ROOT,(LPARAM) NULL);    
  972.     
  973.     while (hItem)
  974.     {
  975.         // gets its data
  976.         item.mask = TVIF_PARAM;
  977.         item.hItem = hItem;
  978.         SendMessage( hwndTV, TVM_GETITEM, 0, (LPARAM)&item );
  979.         // is this the one?
  980.         psearchdata = (LPTREEDATA)item.lParam;
  981.         if (IsEqualGUID(&(psearchdata->guid),&(pdata->guid))) 
  982.         {
  983.             // mark the item as seen
  984.             psearchdata->dwRefresh = 1;
  985.             SendMessage( hwndTV, TVM_SETITEM, 0, (LPARAM)&item );
  986.             return hItem;
  987.         }
  988.         // try the next one
  989.         hItem = (HTREEITEM)SendMessage( hwndTV, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)hItem );    
  990.     }    
  991.     
  992.     return NULL;
  993.         
  994.  
  995.     
  996. } // FindItemInTree
  997.  
  998. /*
  999.  * AddItemToTree - adds items to a tree-view control. 
  1000.  * Returns the handle of the newly added item. 
  1001.  * hwndTV - handle of the tree-view control 
  1002.  * lpszItem - text of the item to add 
  1003.  * nLevel - level at which to add the item 
  1004.  */
  1005. HTREEITEM AddItemToTree(HWND hwndTV, LPTSTR lptszItem, DWORD dwData, int nLevel) 
  1006.     TV_ITEM tvi; 
  1007.     TV_INSERTSTRUCT tvins; 
  1008.     static HTREEITEM hPrev = (HTREEITEM) TVI_FIRST; 
  1009.     static HTREEITEM hPrevRootItem = NULL; 
  1010.     static HTREEITEM hPrevLev2Item = NULL; 
  1011.     HTREEITEM hItem;
  1012.     
  1013.     if (1 == nLevel)
  1014.     {
  1015.         hItem = FindItemInTree(hwndTV,(LPTREEDATA)dwData);    
  1016.         if (hItem) return NULL;
  1017.     }
  1018.     memset(&tvi,0,sizeof(tvi));
  1019.     memset(&tvins,0,sizeof(tvins));
  1020.  
  1021.     tvi.mask = TVIF_TEXT | TVIF_IMAGE 
  1022.         | TVIF_SELECTEDIMAGE | TVIF_PARAM;
  1023.     
  1024.      // Set the state
  1025.     if (nLevel == 1) 
  1026.     {
  1027.         tvi.mask |= TVIF_STATE; 
  1028.         tvi.state = TVIS_SELECTED;
  1029.     }
  1030.  
  1031.     // Set the text of the item. 
  1032.     tvi.pszText = lptszItem; 
  1033.  
  1034.    // Set the image
  1035.     if (nLevel == 1)
  1036.     {
  1037.         tvi.iImage = gnSession; 
  1038.         tvi.iSelectedImage = gnSession; 
  1039.     }
  1040.     else
  1041.     {
  1042.         tvi.iImage = gnPlayer; 
  1043.         tvi.iSelectedImage = gnPlayer; 
  1044.     }
  1045.  
  1046.  
  1047.     // Save the heading level in the item's application-defined 
  1048.     // data area. 
  1049.     tvi.lParam = (LPARAM) dwData; 
  1050.  
  1051.     tvins.item = tvi; 
  1052.     tvins.hInsertAfter = hPrev; 
  1053.  
  1054.     // Set the parent item based on the specified level. 
  1055.     if (nLevel == -1)
  1056.         tvins.hParent = hPrevRootItem;
  1057.     else if (nLevel == 1) 
  1058.         tvins.hParent = TVI_ROOT; 
  1059.     else if (nLevel == 2) 
  1060.         tvins.hParent = hPrevRootItem; 
  1061.     else
  1062.         tvins.hParent = hPrevLev2Item; 
  1063.  
  1064.     // Add the item to the tree-view control. 
  1065.     hPrev = (HTREEITEM) SendMessage(hwndTV, TVM_INSERTITEM, 0, 
  1066.          (LPARAM) (LPTV_INSERTSTRUCT) &tvins); 
  1067.  
  1068.     // Save the handle of the item. 
  1069.     if (nLevel == 1) 
  1070.         hPrevRootItem = hPrev; 
  1071.     else if (nLevel == 2) 
  1072.         hPrevLev2Item = hPrev; 
  1073.  
  1074.     return hPrev; 
  1075.  
  1076. /*
  1077.  * ReleaseSessionData
  1078.  *
  1079.  * Releases the memory allocated for session guids
  1080.  */
  1081. void ReleaseSessionData(HWND hWndCtl)
  1082. {
  1083.     HTREEITEM htiSession;
  1084.     TV_ITEM tvItem;
  1085.     TCHAR tszSessionName[MAX_SESSIONNAME+1];
  1086.  
  1087.  
  1088.     htiSession = TreeView_GetRoot(hWndCtl);
  1089.     while (htiSession)
  1090.     {
  1091.         tvItem.hItem = htiSession;
  1092.         tvItem.pszText = tszSessionName;
  1093.         tvItem.cchTextMax = MAX_SESSIONNAME;
  1094.         TreeView_GetItem(hWndCtl, &tvItem);
  1095.         if (tvItem.lParam) free((LPVOID)tvItem.lParam);
  1096.         htiSession = TreeView_GetNextSibling(hWndCtl, htiSession);
  1097.     }
  1098. }
  1099.  
  1100. /*
  1101.  * ReleaseSPData
  1102.  *
  1103.  * Releases the memory allocated for service provider guids
  1104.  * depends on global variable ghWndSPControl
  1105.  */
  1106. void ReleaseSPData(void)
  1107. {
  1108.     LPVOID lpCon = NULL;
  1109.     int count,index;
  1110.  
  1111.     if (ghWndSPCtl)
  1112.     {
  1113.         count = SendMessage(ghWndSPCtl, LB_GETCOUNT, 0, 0);
  1114.         for (index = 0; index < count; index++)
  1115.         {
  1116.             lpCon = (LPVOID) SendMessage(ghWndSPCtl, LB_GETITEMDATA, index, 0);
  1117.             if (lpCon) GlobalFreePtr(lpCon);
  1118.         }
  1119.     }
  1120. }
  1121.