SETDISP.C
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:37k
源码类别:

Windows编程

开发平台:

Visual C++

  1. /******************************************************************************
  2. *       This is a part of the Microsoft Source Code Samples.
  3. *       Copyright (C) 1993-1997 Microsoft Corporation.
  4. *       All rights reserved.
  5. *       This source code is only intended as a supplement to
  6. *       Microsoft Development Tools and/or WinHelp documentation.
  7. *       See these sources for detailed information regarding the
  8. *       Microsoft samples programs.
  9. ******************************************************************************/
  10. /******************************************************************************
  11. *                                                              
  12. *   Implementation file for SetDisp                           
  13. *                                                              
  14. *   SetDisp creates a tray Notification Icon                  
  15. *   that you can use to dynamically change your srceen         
  16. *   resolution. The core APIs used are:                        
  17. *                                                              
  18. *   Shell_NotifyIcon -- to create and maintain the tray icon   
  19. *   TrackPopupMenu   -- to implement the icon resolution       
  20. *                       context menu                           
  21. *   ChangeDisplaySettings -- to test and set the display       
  22. *                       settings dynamically and to update     
  23. *                       the registry with these setttings      
  24. *   EnumDisplaySettings -- to get the display settings that are
  25. *                       supported by the video driver          
  26. *
  27. *   KNOWN ISSUES:  On Windows 95 one cannot use the EnunDisplaySettings
  28. *   to get the current settings.  This causes the check mark to not
  29. *   be displayed and the current setting to not be bolded.  This 
  30. *   information could be obtained from the registry. Also on Windows 95
  31. *   color depth changes require a reboot.  None of the possible returns
  32. *   from ChangeDisplaySettings are currently handled execpt for 
  33. *   success and a message is displayed indicated the sample does not
  34. *   support a reboot. If your driver has many resolutions, the menu
  35. *   may not fit on some resolutions.  This could be resolved with 
  36. *   the use of sub menus for different settings such as color depth
  37. *   or frequencies.
  38. *                                                                 
  39. ******************************************************************************/
  40. #if DBG
  41. #define TRC(_x) OutputDebugString(TEXT(_x))
  42. #else
  43. #define TRC(_x)
  44. #endif
  45. #include "setdisp.h"
  46. #include <windowsx.h>
  47. #include <winuser.h>
  48. #include "resource.h"
  49. /**************************************************************
  50. *                                                              * 
  51. *  Tray Icon Structure taken from Win32TrayNot sample         *
  52. *                                                              * 
  53. **************************************************************/
  54. struct _DLGITEMS
  55. {
  56. DWORD dwStart;
  57. UINT uNotify;
  58. UINT uDelayID;
  59. UINT uState1;
  60. UINT uTip1;
  61. UINT uState2;
  62. UINT uTip2;
  63. } g_sDlgItems [] =
  64. //
  65. // Data for one tray icon
  66. //
  67. {
  68. {
  69. 0, IDC_NOTIFY1, IDC_DELAY1, IDC_STATE11, IDC_TIP11, IDC_STATE12, IDC_TIP12,
  70. },
  71. } ;
  72.  /****************************************************************************
  73.  *                                                                          
  74.  *  FUNCTION   : WinMain (HINSTANCE hInstance,  HINSTANCE hPrevInstance, 
  75.  *                        LPSTR lpCmdLine, int nCmdShow )
  76.  *                                                                          
  77.  *  PURPOSE    : Creates a dialog box which implements the tray icon
  78.  *               and which handles the resolution changes
  79.  *                                                                          
  80.  ****************************************************************************/
  81. int WINAPI WinMain (
  82.   HINSTANCE hInstance, 
  83.   HINSTANCE hPrevInstance, 
  84.   LPSTR lpCmdLine, 
  85.   int nCmdShow )
  86. {
  87.     g_hinst = hInstance;
  88.     DialogBox(hInstance, MAKEINTRESOURCE(IDD_SETDISP), NULL, SetDispDlgProc);
  89.     if (gpDevModes)
  90.     {
  91.         free(gpDevModes);
  92.     }
  93.     return(FALSE);
  94. }
  95.  /****************************************************************************
  96.  *                                                                          
  97.  *  FUNCTION   : SetDispDlgProc (HWND hDlg, UINT uMsg,
  98.  *                                WPARAM wParam, LPARAM lParam)
  99.  *                                                                          
  100.  *  PURPOSE    : Dialog box callback which manages the tray icon
  101.  *               and which handles the resolution changes
  102.  *                                                                          
  103.  ****************************************************************************/
  104. BOOL CALLBACK SetDispDlgProc (
  105.   
  106.    HWND hDlg, 
  107.    UINT uMsg, 
  108.    WPARAM wParam, 
  109.    LPARAM lParam)
  110. {
  111.     switch (uMsg)
  112.     {
  113.     case WM_INITDIALOG:
  114.     {
  115. BOOL bRet;
  116. //
  117. //Get DEVMODES
  118. //
  119. bRet = GetDevModes (hDlg);
  120.         if (bRet == FALSE)
  121.         {
  122.             //
  123.             // Failed to get DEVMODES so end the dialog
  124.             //
  125.         
  126.     EndDialog(hDlg, TRUE);
  127.     break;
  128.         }
  129.         
  130. //
  131. // Initialize Menu with Resolutions
  132. //
  133. SetResolutionMenu (hDlg);
  134.         
  135. //
  136. // Add Tray Notify Icon
  137. //
  138. NotifyAdd(hDlg, 0);
  139.         
  140. //
  141. // Update Tip to current settings
  142. //
  143. {
  144.         
  145.     char szTip[50];
  146.     //
  147.     // Set the menu text
  148.     //
  149.     if (IsNT())
  150.             {
  151.                 wsprintf(szTip,"%d x %d, %d bit color at %d hz",
  152.                          gpCurrentMode->dmPelsWidth,
  153.                          gpCurrentMode->dmPelsHeight,
  154.                          gpCurrentMode->dmBitsPerPel,
  155.                          gpCurrentMode->dmDisplayFrequency);
  156.             }
  157.             else
  158.             {
  159.                 szTip[0] = '';
  160.             }
  161.         
  162.     //
  163.     // For the current mode, set the tool tip
  164.             //
  165.     TrayMessage(hDlg, NIM_MODIFY, g_sDlgItems[0].uNotify,
  166.                         LoadImage(g_hinst, MAKEINTRESOURCE(IDI_SETDISP),
  167.                         IMAGE_ICON, 16, 16, 0), szTip);
  168. }
  169.  
  170. SetForegroundWindow (hDlg);
  171. break;
  172.     
  173.     } // WM_INITDIALOG:
  174.     case WM_DESTROY: NotifyDelete(hDlg, 0);
  175. break;
  176.     case WM_COMMAND:
  177.     {
  178. switch (GET_WM_COMMAND_ID(wParam, lParam))
  179. {
  180.     case IDCANCEL:   EndDialog(hDlg, TRUE);
  181.      break;
  182.     case IDABORT:    ShowWindow(hDlg, SW_HIDE);
  183.      break;
  184.         
  185.     case IDM_ABOUT:
  186.   DialogBox(g_hinst, MAKEINTRESOURCE(IDD_ABOUT),
  187.     hDlg, (DLGPROC)About);
  188.   break;
  189.     default:
  190.         
  191.     //
  192.     // handle MYWM_NOTIFYICON + 10 + i for diffent devmodes
  193.     //
  194.             if ( MYWM_NOTIFYICON + 10  <= GET_WM_COMMAND_ID(wParam, lParam) &&
  195.  MYWM_NOTIFYICON + 11 + gnModes >= GET_WM_COMMAND_ID(wParam, lParam))
  196.     {
  197. UINT nNewMode;
  198.                 
  199.                 nNewMode = GET_WM_COMMAND_ID(wParam, lParam) - (MYWM_NOTIFYICON + 10);
  200.                 
  201. //
  202. // Only update if the chose a new setting
  203. //
  204.                 if (gnCurrentMode != nNewMode)
  205.                 {            
  206.    ChangeResolution (nNewMode, hDlg);
  207. }
  208. break;
  209.             
  210.             }
  211. } //WM_COMMAN switch
  212.     } //WM_COMMAND
  213.     case MYWM_NOTIFYICON:
  214.     {
  215. switch (lParam)
  216. {
  217.     case WM_LBUTTONDOWN:
  218.         
  219.  //
  220.  // Handle popup here when user right mouse clicks tray icon
  221.  //
  222.  {
  223.             
  224.      //
  225.      // Handle popup here
  226.      //
  227.      POINT point;
  228.  
  229.      GetCursorPos (&point);
  230.      HandlePopupMenu (hDlg, point);
  231.             
  232.      break;
  233.     
  234.  }
  235.     
  236.     case WM_RBUTTONDOWN:
  237.  switch (wParam)
  238.  {
  239.             
  240.     //
  241.     // Diplay the dialog box on Left mouse click
  242.     // this is how the user gets the dialog back if it is hidden
  243.     //
  244.     case IDC_NOTIFY1:  ShowWindow (hDlg, SW_RESTORE);
  245.        SetForegroundWindow (hDlg);
  246.        break;
  247.     default:
  248.  break;
  249.   }
  250.        
  251.     default:
  252.  break;
  253. }  // lParam switch
  254.     }// MYWM_NOTIFYICON
  255.     default:
  256.  return(FALSE);
  257.     
  258.     }  // switch (uMsg)
  259.     return(TRUE);
  260. }
  261. /****************************************************************************
  262. *                                                                          
  263. *  FUNCTION   : GetDevModes (hwnd)
  264. *                                                                          
  265. *  PURPOSE    : Get the display modes supported by the current driver
  266. *
  267. ****************************************************************************/
  268. BOOL GetDevModes (
  269.                   
  270.   HWND hwnd)
  271. {
  272.     BOOL bRet = TRUE;
  273.     BOOL bAddDM;
  274.     UINT iModeNum = 0;
  275.     DEVMODE CurrentDM, WorkingDM;
  276.     INT i;
  277.     UINT nlist;
  278.     LPDEVMODE pDM;
  279.     //
  280.     // setting param 1 to NULL (lpszDeviceName) uses current device on NT
  281.     // and NULL is required on Win95
  282.     //
  283.     if (IsNT())
  284.     {
  285. //
  286. // NT specific; returns current devmode when param 2 set to ENUM_CURRENT_SETTINGS,
  287. //
  288. EnumDisplaySettings (NULL, ENUM_CURRENT_SETTINGS, &CurrentDM);
  289.     }
  290.     //
  291.     // Determine how much memory we need
  292.     //
  293.     for (iModeNum = 0, gnModes = -1;
  294.          bRet && (gnModes <= MAX_MODES); 
  295.          iModeNum++)
  296.     {
  297.         bRet = EnumDisplaySettings 
  298.                      (NULL,            // use default device
  299.                      iModeNum,         // DEVMODEs start at 0
  300.                      &WorkingDM);      // if successful the system
  301.                                        // fills in the DEVMODE
  302.                                        // structure
  303.         //
  304.         // For some reason my driver supports 1 hz display frequency
  305.         // So filter out settings we do not want to display
  306.         //
  307.         
  308.         if ( bRet & 
  309.      WorkingDM.dmPelsWidth   >= MIN_WIDTH &&
  310.             (WorkingDM.dmDisplayFrequency >= MIN_FREQUENCY  ||
  311.      WorkingDM.dmDisplayFrequency == 0) )
  312.     //
  313.     // Accept 0 frequency since windows 95 does return frequency
  314.     //
  315.         {
  316.             //
  317.             // Increment to use next DEVMODE
  318.             //
  319.             gnModes++;
  320. }
  321.     } // for loop
  322.     
  323.     if(-1 == gnModes)
  324.     {
  325.         MessageBox(hwnd, "No Display modes available.", "SetDisp Error", IDOK);
  326.         return (FALSE);
  327.     }
  328.     //
  329.     // We need gnModes worth of DEVMODE Structures
  330.     //
  331.     
  332.     gpDevModes = (PDEVMODE) malloc ((size_t) (gnModes + 1) * sizeof(DEVMODE));
  333.     if (gpDevModes == NULL)
  334.     {
  335.         return (FALSE);
  336.     }
  337.     //
  338.     // Loop through EnumDisplaySetting to get each supported DEVMODE
  339.     // When returns false we have them all
  340.     //
  341.     
  342.     for (iModeNum = 0, i = 0, bRet = TRUE; 
  343.          bRet && i <= gnModes; 
  344.          iModeNum++)
  345.     {
  346.         bRet = EnumDisplaySettings 
  347.                      (NULL,            // use default device
  348.                      iModeNum,         // DEVMODEs start at 0
  349.                      &WorkingDM);      // if successful the system
  350.                                        // fills in the DEVMODE
  351.                                        // structure
  352.         //
  353.         // Miniport drivers support 1 hz display frequency representing the default
  354.         // and older drivers support 0 hz display frequency representing the default
  355.         // So filter out settings we do not want to display
  356.         //
  357.         
  358.         if ( bRet && 
  359.              WorkingDM.dmPelsWidth        >= MIN_WIDTH      && 
  360.             (WorkingDM.dmDisplayFrequency >= MIN_FREQUENCY  ||
  361.              WorkingDM.dmDisplayFrequency == 0              ||
  362.              WorkingDM.dmDisplayFrequency == 1))
  363.              // Accept 0 or 1 frequency which represent default frequencies
  364.         {
  365.             bAddDM = TRUE;
  366.             //
  367.             // We will take this one unless another default has already been 
  368.             // added at these settings
  369.             //
  370.             if (WorkingDM.dmDisplayFrequency == 0 || 
  371.                 WorkingDM.dmDisplayFrequency == 1)
  372.             {
  373.                 for (nlist = 0; nlist <= gnCurrentMode; nlist++)
  374.                 {
  375.                       pDM = gpDevModes + nlist;
  376.                       if (pDM->dmPelsWidth  == WorkingDM.dmPelsWidth   &&
  377.                           pDM->dmPelsHeight == WorkingDM.dmPelsHeight  &&
  378.                           pDM->dmBitsPerPel == WorkingDM.dmBitsPerPel)
  379.                       {
  380.                           //
  381.                           // If everything else is the same do not add another default
  382.                           //
  383.                           bAddDM = FALSE;
  384.                       }
  385.                 } // end for nlist
  386.             } // end if default
  387.             if (bAddDM)
  388.             {
  389.                 //
  390.                 // Increment to use next DEVMODE
  391.                 //
  392.                 if (i == 0)
  393.                 {
  394.                     gpCurrentMode = gpDevModes;
  395.                 }
  396.                 else
  397.                 {
  398.                     gpCurrentMode += 1;
  399.                 }
  400.                 EnumDisplaySettings (NULL, iModeNum, gpCurrentMode);
  401.                 i++;
  402.             } // end if bAddDM
  403.         } // end bRet and hz if
  404.     } // end for EnumDisplaySettings
  405.     //
  406.     // Sort the array for display; default sort
  407.     // A different compare routine could sort by different attributes
  408.     //
  409.     
  410.     qsort((void *) gpDevModes, 
  411.           (size_t) gnModes + 1,  // add one since gnModes is zero based 
  412.           (size_t) sizeof(DEVMODE), 
  413.           ( int (_cdecl*)(const void*,const void*) )CompareDevModes);
  414.      
  415.     //
  416.     // Set gCurrentMode to CurrentDM
  417.     //
  418.     if (IsNT())
  419.     {
  420.         for (i =0; i <= gnModes; i++)
  421.         {
  422.             
  423.             if (i == 0)
  424.             {
  425.                 gpCurrentMode = gpDevModes;
  426.             }
  427.             else
  428.             {
  429.                 gpCurrentMode += 1;
  430.             }
  431.             if (0 == CompareDevModes (&CurrentDM, gpCurrentMode))
  432.             {
  433.                 gnCurrentMode = i;
  434.                 break;
  435.             }
  436.         }
  437.     }
  438.     else
  439.     {
  440.         //
  441.         // Default to the first one
  442.         // since we error out if there is not even this one
  443.         //
  444.         gnCurrentMode = 0;
  445.     }
  446.     //
  447.     // TODO: What if some of the DEVMODES are not supported on 
  448.     //       the physical Device?
  449.     //
  450.     
  451.     return (TRUE);
  452. }  //GetDevModes scope
  453. /****************************************************************************
  454. *                                                
  455. *  FUNCTION   : ChangeResolution (UINT nNewMode, HWND hwnd)
  456. *                                                
  457. *  PURPOSE    : Change the resolution to the nNewMode index DEVMODE               
  458. *                                                                          
  459. ****************************************************************************/
  460. BOOL ChangeResolution (
  461.     UINT nNewMode, 
  462.     HWND hwnd )
  463. {
  464.   
  465.     LONG lResult;       //Result of ChangeDisplaySettings
  466.     INT nUserResponse; //Return from VerifyRes Dlg
  467.     UINT nOldMode;
  468.    /*
  469.     ChangeDisplaySettings can set
  470. dmBitsPerPel     Bits per pixel
  471. dmPelsWidth     Pixel width
  472. dmPelsHeight     Pixel height
  473. dmDisplayFlags     Mode flags
  474. dmDisplayFrequency  Mode frequency
  475.         
  476.         but one of thes must be set in the DEVMODE dmFields
  477.         DM_BITSPERPEL     Use the dmBitsPerPel value.
  478.         DM_PELSWIDTH     Use the dmPelsWidth value.
  479.         DM_PELSHEIGHT     Use the dmPelsHeight value.
  480.         DM_DISPLAYFLAGS     Use the dmDisplayFlags value.
  481. DM_DISPLAYFREQENCY  Use the dmDisplayFrequency value.
  482.     */
  483.     //
  484.     // First select a DevMode to use
  485.     // to start, just cycling through them
  486.     //
  487.     nOldMode = gnCurrentMode;
  488.     gnCurrentMode = nNewMode;
  489.     
  490.     //
  491.     // Increment ptr to the new mode
  492.     //
  493.     gpCurrentMode = gpDevModes;
  494.     gpCurrentMode += nNewMode;
  495.     
  496.     //
  497.     // Verify that the DEVMODE select will change something
  498.     //
  499.     if (!(DM_BITSPERPEL      & gpCurrentMode->dmFields ||
  500.           DM_PELSWIDTH      & gpCurrentMode->dmFields ||
  501.           DM_PELSHEIGHT      & gpCurrentMode->dmFields ||
  502.           DM_DISPLAYFLAGS  & gpCurrentMode->dmFields ||
  503.           DM_DISPLAYFREQUENCY & gpCurrentMode->dmFields ) )
  504.     
  505.     {
  506.     
  507. //
  508.         // Note our DEVMODE returned from EnumDisplaySettings
  509. // So we should never enter here
  510. //
  511.         gnCurrentMode = nOldMode;
  512.         
  513.         gpCurrentMode = gpDevModes;
  514.         gpCurrentMode += gnCurrentMode;
  515.         return (FALSE);
  516.     }
  517.     /************************************************************
  518.     *                                                            *
  519.     *   Possible Flags for param 2 of ChangeDisplay Settings     *
  520.     *    0         The graphics mode for the current            *
  521.     *                screen will be changed dynamically.         *
  522.     *    CDS_UPDATEREGISTRY The graphics mode for the            *
  523.     *                current screen will be changed dynamically  *
  524.     *                and the graphics mode will be updated in    *
  525.     *                the registry. The mode information is       *
  526.     *                stored in the USER profile.                 *
  527.     *    CDS_TEST The system tests if the requested            *
  528.     *                graphics mode could be set.                 *
  529.     *                                                            *
  530.     ************************************************************/
  531.     
  532.     //
  533.     // First have the system test if we can set it
  534.     //
  535.     //
  536.     // Param 1 is the new DEVMODE
  537.     // Param 2 is Test Flag, 
  538.     // which asks the operating system if this is a valid setting 
  539.     // to change to -- wedon't want to make the system unusable
  540.     //
  541.     lResult = ChangeDisplaySettings
  542.                 ( gpCurrentMode, 
  543.                   CDS_TEST );               
  544.                                       
  545.     
  546.     if (lResult == DISP_CHANGE_RESTART)
  547.     {
  548.         MessageBox(hwnd, "This mode requires a reboot whichn"  
  549.                          "is not supported in this sample.", 
  550.  "SetDisp Message", IDOK);
  551.         
  552.         gnCurrentMode = nOldMode;
  553.         gpCurrentMode = gpDevModes;
  554.         gpCurrentMode += gnCurrentMode;
  555.         return FALSE;
  556.     }
  557.     if (DISP_CHANGE_SUCCESSFUL ==lResult)
  558.     {
  559.     
  560.  //
  561.  // Param 2 is 0 to change dynamically
  562.  // hence the app name SetDisp!
  563.  //
  564.          lResult = ChangeDisplaySettings
  565.                      ( gpCurrentMode,
  566.                        0 );  
  567.     }
  568.     
  569.     switch (lResult)
  570.     {
  571.     case DISP_CHANGE_SUCCESSFUL:  //The settings change was successful.
  572.  TRC("DISP_CHANGE_SUCCESSFULn");
  573.  break;
  574.     case DISP_CHANGE_RESTART:   //The computer must be restarted in order 
  575.                                   //  for the graphics mode to work.
  576.          TRC("DISP_CHANGE_RESTARTn");
  577.          break;
  578.     case DISP_CHANGE_BADFLAGS:   //An invalid set of flags was passed in.
  579.          TRC("DISP_CHANGE_BADFLAGSn");
  580.          break;
  581.     case DISP_CHANGE_FAILED:   //The display driver failed the specified 
  582.                                   //  graphics mode.
  583.          TRC("DISP_CHANGE_FAILEDn");
  584.          break;
  585.     case DISP_CHANGE_BADMODE:   //The graphics mode is not supported.
  586.          TRC("DISP_CHANGE_BADMODEn");
  587.          break;
  588.     case DISP_CHANGE_NOTUPDATED:  //Windows NT only: Unable to write settings 
  589.                                   //  to the registry.                        
  590.          TRC("DISP_CHANGE_NOTUPDATEDn");
  591.          break;
  592.     default:
  593.  TRC("Undocumented return value!!!!n");
  594.  break;
  595.     }
  596.     //
  597.     // Set a timer to only give the user so much
  598.     // time to dedice incase the screen is garbled
  599.     //
  600.     SetTimer(hwnd, VERIFY_RESCHANGE, VERIFY_TIMEOUT, VerifyTimerProc);
  601.     //
  602.     // Have the user verify the new resolution and depth
  603.     //
  604.     nUserResponse = DialogBox(g_hinst,// handle this application 
  605.                               MAKEINTRESOURCE(IDD_VERIFYRES),// identifies dialog box template
  606.                               NULL,               // handle to owner window
  607.                               VerifyDlgProc);  // the dialog box procedure
  608.     //
  609.     // The verification is over, kill the timer
  610.     //
  611.     KillTimer(hwnd, VERIFY_RESCHANGE);
  612.     
  613.     if (IDYES == nUserResponse)
  614.     {
  615.     
  616. //
  617. // Keep resolution and update registry
  618. //
  619.         char szTip[50];
  620. //
  621. // TODO: give them three choices:
  622.         //       Dynamic only, Registry too, or Abort
  623. //
  624.         lResult = ChangeDisplaySettings
  625.                     ( gpCurrentMode,
  626.                       CDS_UPDATEREGISTRY );  //  Flag to update registry
  627.         
  628.         switch (lResult)
  629.         
  630.         {
  631.         
  632.         case DISP_CHANGE_SUCCESSFUL: 
  633.             
  634.     //
  635.     // The settings change was successful.
  636.     //
  637.             TRC("DISP_CHANGE_SUCCESSFUL - regn");
  638.             
  639.             wsprintf(szTip,"%d x %d, %d bit color at %d hz", 
  640.                  gpCurrentMode->dmPelsWidth,
  641.                  gpCurrentMode->dmPelsHeight,
  642.                  gpCurrentMode->dmBitsPerPel,
  643.                  gpCurrentMode->dmDisplayFrequency);
  644.              {
  645.  //
  646.  // Update the Menu informtion, just a check mark for now
  647.  //
  648.                  HMENU hMenu, hMenu2;
  649.                  
  650.                  hMenu = GetMenu(hwnd);
  651.                  
  652.                  hMenu2 = GetSubMenu (hMenu, 0);
  653.  //
  654.  // Uncheck the old
  655.  //
  656.                  CheckMenuItem (hMenu2, nOldMode, MF_BYPOSITION | MF_UNCHECKED);
  657.                  
  658.  //
  659.  // Check the new
  660.  //
  661.  CheckMenuItem (hMenu2, gnCurrentMode, MF_BYPOSITION | MF_CHECKED);
  662.              
  663.              }
  664.      //
  665.      // Update the Notify_Icon quick tip
  666.      //
  667.              TrayMessage(hwnd, NIM_MODIFY, g_sDlgItems[0].uNotify,
  668.                     LoadImage(g_hinst, MAKEINTRESOURCE(IDI_SETDISP),
  669.                     IMAGE_ICON, 16, 16, 0), szTip);
  670.              break;
  671.         case DISP_CHANGE_RESTART:   //The computer must be restarted in order 
  672.                                       //  for the graphics mode to work.
  673.              TRC("DISP_CHANGE_RESTART - regn");
  674.              break;
  675.         case DISP_CHANGE_BADFLAGS:   //An invalid set of flags was passed in.
  676.              TRC("DISP_CHANGE_BADFLAGS - regn");
  677.              break;
  678.         case DISP_CHANGE_FAILED:   //The display driver failed the specified 
  679.                                       //  graphics mode.
  680.              TRC("DISP_CHANGE_FAILED - regn");
  681.              break;
  682.         case DISP_CHANGE_BADMODE:   //The graphics mode is not supported.
  683.              TRC("DISP_CHANGE_BADMODE - regn");
  684.              break;
  685.         case DISP_CHANGE_NOTUPDATED:  //Windows NT only: Unable to write settings 
  686.                                       //  to the registry.                        
  687.              TRC("DISP_CHANGE_NOTUPDATED - regn");
  688.              break;
  689.         default:
  690.               TRC("Undocumented return value!!!! - regn");
  691.               break;
  692.         }
  693.     
  694.     }
  695.     
  696.     else
  697.     
  698.     {
  699. //
  700. // The user chose to not keep the setting or we timed out
  701.         // so they may not have seen the dialog box to choose
  702.         //
  703.         // Change resolution back by calling
  704.         // ChangeDisplaySettings 
  705.         // with a NULL DEVMODE which returns us to the current
  706.         // registry settings
  707. //
  708.         lResult = ChangeDisplaySettings
  709.                     ( NULL, 
  710.                       0 );  // 0 to change dynamically
  711.         
  712.         gnCurrentMode = nOldMode;
  713.         
  714.         gpCurrentMode = gpDevModes;
  715.         gpCurrentMode += gnCurrentMode;
  716.     
  717.     }
  718.     return (TRUE);
  719. }
  720.  /****************************************************************************
  721.  *                                                                          
  722.  *  FUNCTION   : SetResolutionMenu (hwnd)                              
  723.  *                                                                          
  724.  *  PURPOSE    : Create a context menu with all of the resolutions
  725.  *               that are in the devmodes we obtained from the drive
  726.  *                                                                          
  727.  ****************************************************************************/
  728. BOOL SetResolutionMenu ( 
  729.     HWND hwnd)
  730. {
  731.   
  732.     HMENU hMenu, hWndMenu; //hMenuTrackPopup;
  733.     INT i;
  734.     BOOL bRet;
  735.     PDEVMODE pDevMode;
  736.     
  737.     //
  738.     // Create new Resolutions menu from the DevModes collected
  739.     //
  740.     hMenu = CreateMenu(); 
  741.     
  742.     if (!hMenu)
  743.     {
  744.        return (FALSE);
  745.     }
  746.     //
  747.     // For each devmode add an menu item
  748.     // TODO: make the description on BitPerPel better
  749.     //
  750.     for(i=0; i<= gnModes; i++)
  751.     {
  752.     
  753.         char szRes[50];
  754.     
  755.         if (i == 0)
  756.         {
  757.             pDevMode = gpDevModes;
  758.         }
  759.         else
  760.         {
  761.             pDevMode += 1;
  762. }
  763.         //
  764. // Set the menu text
  765. //
  766.         if (pDevMode->dmDisplayFrequency == 0 ||
  767.             pDevMode->dmDisplayFrequency == 1)
  768. {
  769.     wsprintf(szRes,"%d x %d, %d bit color, default frequency",
  770.                  pDevMode->dmPelsWidth,
  771.                  pDevMode->dmPelsHeight,
  772.                  pDevMode->dmBitsPerPel);
  773. }
  774. else
  775. {
  776.     wsprintf(szRes,"%d x %d, %d bit color at %d hz",
  777.                  pDevMode->dmPelsWidth,
  778.                  pDevMode->dmPelsHeight,
  779.                  pDevMode->dmBitsPerPel,
  780.                  pDevMode->dmDisplayFrequency);
  781. }
  782.         
  783.         bRet = AppendMenu (hMenu, MF_STRING, MYWM_NOTIFYICON + 10 + i, szRes);
  784.         
  785.         SetMenuItemBitmaps(
  786.   hMenu,  // handle of menu
  787.   gnCurrentMode, // menu item to receive new bitmaps
  788.                   MF_BYPOSITION, // menu item flags
  789.                   NULL,          // handle of unchecked bitmap
  790.   NULL);  // handle of checked bitmap
  791.                                  // if last two are NULL the default 
  792.                                  // bitmap is used
  793.     
  794.     } // for loop to set menu
  795.     
  796.     if (!bRet)
  797.     {
  798.    
  799.         //
  800.         // if the last one worked they all likely worked
  801.         // otherwise let's clean up
  802.         //
  803.         DestroyMenu(hMenu);
  804.         
  805.         return(FALSE);
  806.     
  807.     }
  808.     else
  809.     {
  810.         if (IsNT())
  811.         {
  812.             //
  813.             // Check the Current one
  814.             //
  815.             CheckMenuItem (hMenu, gnCurrentMode, MF_BYPOSITION | MF_CHECKED);
  816.     
  817.             //
  818.             // Set it as the default so it is easy to go back to
  819.             //
  820.     SetMenuDefaultItem(hMenu, gnCurrentMode, TRUE ); //TRUE if for by Position
  821.         }
  822.         hWndMenu = GetMenu (hwnd);
  823.         
  824.         InsertMenu (hWndMenu, 0, MF_POPUP|MF_BYPOSITION, (DWORD)hMenu, "&Resolutions");
  825.         
  826.         return(TRUE);
  827.     
  828.     }
  829.     
  830. }
  831.  /****************************************************************************
  832.  *                                                                          
  833.  *  FUNCTION   : TrayMessage (HWND hDlg, DWORD dwMessage, UINT uID, 
  834.  *                            HICON hIcon, PSTR pszTip )
  835.  *                                                                          
  836.  *  PURPOSE    : Creates, Modifies or deletes the tray icon 
  837.  *               If pszTip is not null, it uses that for the tip
  838.  *               otherwise it sets a default tip
  839.  *                                                                          
  840.  ****************************************************************************/
  841. BOOL TrayMessage (
  842.                   
  843.   HWND hDlg, 
  844.   DWORD dwMessage, 
  845.   UINT uID, 
  846.   HICON hIcon, 
  847.   PSTR pszTip )
  848. {
  849.     BOOL res;
  850.     NOTIFYICONDATA tnd;
  851.     //
  852.     // Get the Tray Icon
  853.     //
  854.     hIcon = (HICON)LoadImage(g_hinst, MAKEINTRESOURCE(IDI_SETDISP), IMAGE_ICON,
  855.      16, 16, 0);
  856.     
  857.     tnd.cbSize = sizeof(NOTIFYICONDATA);
  858.     tnd.hWnd = hDlg;
  859.     tnd.uID = uID;
  860.     tnd.uFlags = NIF_MESSAGE|NIF_ICON|NIF_TIP;
  861.     tnd.uCallbackMessage= MYWM_NOTIFYICON;
  862.     tnd.hIcon = hIcon;
  863.     //
  864.     // If there is a specific tip, set it
  865.     // otherwise use SetDisp as the default
  866.     //
  867.     if (pszTip)
  868.     {
  869. lstrcpyn(tnd.szTip, pszTip, sizeof(tnd.szTip));
  870.     }
  871.     else
  872.     {
  873. lstrcpyn(tnd.szTip, "SetDisp", sizeof("SetDisp"));;
  874.     }
  875.     //
  876.     // Use the Shell_NotifyIcon API to setup the tray icon
  877.     //
  878.     res = Shell_NotifyIcon(dwMessage, &tnd);
  879.     if (hIcon)
  880.     {
  881.  DestroyIcon(hIcon);
  882.     }
  883.     return res;
  884. }
  885.  /****************************************************************************
  886.  *                                                                          
  887.  *  FUNCTION   : NotifyDelete (HWND hDlg, UINT uIndex)
  888.  *                                                                          
  889.  *  PURPOSE    : Deletes a tray icon based on the uIndex .
  890.  *               In this SetDisp sample, it is only used for a single icon
  891.  *                                                                          
  892.  ****************************************************************************/
  893. void NotifyDelete (
  894.                    
  895.   HWND hDlg, 
  896.   UINT uIndex )
  897. {
  898.     TrayMessage(hDlg, NIM_DELETE, g_sDlgItems[uIndex].uNotify, NULL, NULL);
  899. }
  900.  /****************************************************************************
  901.  *                                                                          
  902.  *  FUNCTION   : NotifyAdd (HWND hDlg, UINT uIndex)
  903.  *                                                                          
  904.  *  PURPOSE    : Creates tray icons based on the uIndex .
  905.  *               In this SetDisp sample, it is only used for a single icon
  906.  *                                                                          
  907.  ****************************************************************************/
  908. void NotifyAdd (
  909.                
  910.   HWND hDlg, 
  911.   UINT uIndex )
  912. {
  913.     TrayMessage(hDlg, NIM_ADD, g_sDlgItems[uIndex].uNotify, NULL, NULL);
  914. }
  915.  /****************************************************************************
  916.  *                                                                          
  917.  *  FUNCTION   : VerifyTimerProc (HWND hwnd, UINT uMsg, 
  918.  *                                UINT idEvent, DWORD dwTime )
  919.  *                                                                          
  920.  *  PURPOSE    : Simply sends an IDNO to the Verify Dialog
  921.  *               This is setup to give the user a limited amount of time
  922.  *               to approve
  923.  *                                                                          
  924.  ****************************************************************************/
  925. VOID CALLBACK VerifyTimerProc (
  926.                                
  927.   HWND hwnd, 
  928.   UINT uMsg, 
  929.   UINT idEvent,
  930.   DWORD dwTime )
  931. {
  932.     SendMessage(ghVerifyDlg, WM_COMMAND, IDNO, 0);
  933. }
  934.  /****************************************************************************
  935.  *                                                                          
  936.  *  FUNCTION   : VerifyTimerProc (HWND hDlg, UINT uMsg,
  937.  *                                WPARAM wParam, LPARAM lParam)
  938.  *
  939.  *                                                                          
  940.  *  PURPOSE    : Ask the user if they want to keep the new resolution
  941.  *                                                                          
  942.  ****************************************************************************/
  943. BOOL CALLBACK VerifyDlgProc(
  944.     HWND hDlg,
  945.     UINT uMsg,
  946.     WPARAM wParam,
  947.     LPARAM lParam)
  948. {
  949.     switch(uMsg)
  950.     {
  951. case WM_INITDIALOG: ghVerifyDlg = hDlg;
  952. return(TRUE);
  953.     
  954. case WM_COMMAND:
  955.     
  956.      //
  957.      // LOWORD added for portability
  958.      //
  959.         
  960.      switch (LOWORD(wParam))
  961.      {
  962.         
  963.  case IDNO:
  964.  case IDCANCEL: ghVerifyDlg = NULL;
  965. EndDialog(hDlg, IDNO);
  966. return 0;
  967.         
  968.  case IDYES: ghVerifyDlg = NULL;
  969. EndDialog(hDlg, IDYES);
  970. return 0;
  971.       }
  972.        
  973. default:
  974.      break;
  975.    
  976.     }
  977.    
  978.     return(FALSE);
  979.     UNREFERENCED_PARAMETER(lParam);
  980. }
  981. /****************************************************************************
  982.  *                                                                          *
  983.  *  FUNCTION   : HandlePopupMenu (hwnd, point)                              *
  984.  *                                                                          *
  985.  *  PURPOSE    : Handles the display of the "floating" popup that appears   *
  986.  *               on a mouse click in the app's client area.                 *
  987.  *                                                                          *
  988.  ****************************************************************************/
  989. VOID APIENTRY HandlePopupMenu (
  990.     HWND  hwnd,
  991.     POINT point)
  992. {
  993.     
  994.     HMENU hMenu;
  995.     HMENU hMenuTrackPopup;
  996.     //
  997.     // Get the menu for the windows
  998.     //
  999.     hMenu = GetMenu(hwnd);
  1000.     if (!hMenu)
  1001.     {
  1002. return;
  1003.     }
  1004.     //
  1005.     // Get the first menu in it which we will use for the call to
  1006.     // TrackPopup(). This could also have been created on the fly using
  1007.     // CreatePopupMenu and then we could have used InsertMenu() or
  1008.     // AppendMenu.
  1009.     //
  1010.     
  1011.     hMenuTrackPopup = GetSubMenu (hMenu, 0);
  1012.     //
  1013.     // Draw and track the "floating" popup
  1014.     //
  1015.     if (point.x < (long) (gpCurrentMode->dmPelsWidth - 50))
  1016.         point.x = (long) (gpCurrentMode->dmPelsWidth - 50);
  1017.     if (point.y < (long) (gpCurrentMode->dmPelsHeight - 50))
  1018.         point.y = (long) (gpCurrentMode->dmPelsHeight - 50);
  1019.     
  1020.     
  1021.     //
  1022.     // This is required when using a notify icon -- see KB article
  1023.     // PRB: Menus for Notification Icons Don't Work Correctly
  1024.     //
  1025.     SetForegroundWindow (hwnd);
  1026.     
  1027.     TrackPopupMenu (hMenuTrackPopup, TPM_LEFTBUTTON | TPM_RIGHTBUTTON,
  1028.                     point.x, point.y, 0, hwnd, NULL);
  1029.     //
  1030.     // This is required when using a notify icon -- see KB article
  1031.     // PRB: Menus for Notification Icons Don't Work Correctly
  1032.     //
  1033.     PostMessage (hwnd, WM_USER, 0, 0);
  1034. }
  1035. /****************************************************************************
  1036. *                                                                          
  1037. *  FUNCTION   : CompareDevModes (DEVMODE *leftDM, DEVMODE *rightDM)                              
  1038. *                                                                          
  1039. *  PURPOSE    : Handles the display of the "floating" popup that appears   
  1040. *               on a mouse click in the app's client area.                 
  1041. *                                                                          
  1042. ****************************************************************************/
  1043. int CompareDevModes (
  1044.     DEVMODE *leftDM,
  1045.     DEVMODE *rightDM)
  1046. {
  1047.     //
  1048.     // Return 1  if left is bigger
  1049.     //        0  if equal
  1050.     //        -1 if right is bigger
  1051.     //
  1052.     // Comparison order is Width then Height then Bits then Frequency
  1053.     // This is an arbitrary order of importance. Often BitPerPels is
  1054.     // on considered the most important measure of a display setting
  1055.     //
  1056.     // return on the first one that is larger between left and right
  1057.     //
  1058.     if (leftDM->dmPelsWidth  > rightDM->dmPelsWidth)
  1059.     {
  1060.         return 1;
  1061.     }
  1062.     else if (leftDM->dmPelsWidth  < rightDM->dmPelsWidth)
  1063.     {
  1064.         return -1;
  1065.     }
  1066.     if (leftDM->dmPelsHeight > rightDM->dmPelsHeight)
  1067.     {
  1068.         return 1;
  1069.     }
  1070.     else if (leftDM->dmPelsHeight < rightDM->dmPelsHeight)
  1071.     {
  1072.         return -1;
  1073.     }
  1074.     if (leftDM->dmBitsPerPel > rightDM->dmBitsPerPel)
  1075.     {
  1076.         return 1;
  1077.     }
  1078.     else if (leftDM->dmBitsPerPel < rightDM->dmBitsPerPel)
  1079.     {
  1080.         return -1;
  1081.     }
  1082.     
  1083.     if (leftDM->dmDisplayFrequency > rightDM->dmDisplayFrequency)
  1084.     {
  1085.         return 1;
  1086.     }
  1087.     else if (leftDM->dmDisplayFrequency < rightDM->dmDisplayFrequency)
  1088.     {
  1089.         return -1;
  1090.     }
  1091.     //
  1092.     // All are equal
  1093.     //
  1094.     return 0;
  1095. }
  1096. /****************************************************************************
  1097. *                                                                          
  1098. *  FUNCTION: About(HWND, UINT, UINT, LONG)
  1099. *
  1100. *  PURPOSE:  Processes messages for the "About" dialog box 
  1101. *
  1102. ****************************************************************************/
  1103. BOOL APIENTRY About(
  1104.     HWND hDlg,
  1105.     UINT message,
  1106.     UINT wParam,
  1107.     LONG lParam)
  1108. {
  1109. switch (message)
  1110. {
  1111.    case WM_INITDIALOG:
  1112.   return TRUE;
  1113.    case WM_COMMAND:              
  1114.  if (LOWORD(wParam) == IDOK)
  1115.  {
  1116.   EndDialog(hDlg, TRUE);
  1117.   return TRUE;
  1118.  }
  1119.  break;
  1120.    default:
  1121. return FALSE;
  1122. }
  1123. }
  1124. BOOL IsNT()
  1125. {
  1126.     OSVERSIONINFO          osvi;
  1127.     
  1128.     osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  1129.     
  1130.     GetVersionEx(&osvi);
  1131.     if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) 
  1132.     {
  1133.         return TRUE;
  1134.     }
  1135.     else
  1136.     {
  1137.         return FALSE;
  1138.     }
  1139. }