main.cpp
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 76k
Category:

Windows Kernel

Development Platform:

Visual C++

  1. //+-------------------------------------------------------------------------
  2. //
  3. //  TaskMan - NT TaskManager
  4. //  Copyright (C) Microsoft
  5. //
  6. //  File:       Main.CPP
  7. //
  8. //  History:    Nov-10-95   DavePl  Created
  9. //
  10. //--------------------------------------------------------------------------
  11. #include "precomp.h"
  12. #include <htmlhelp.h>
  13. static UINT g_msgTaskbarCreated = 0;
  14. //
  15. // Control IDs
  16. //
  17. #define IDC_STATUSWND   100
  18. //
  19. // Globals - this app is (effectively) single threaded and these values
  20. //           are used by all pages
  21. //
  22. const TCHAR cszStartupMutex[] = TEXT("NTShell Taskman Startup Mutex");
  23. #define FINDME_TIMEOUT 10000                // Wait to to 10 seconds for a response
  24. void MainWnd_OnSize(HWND hwnd, UINT state, int cx, int cy);
  25. HANDLE      g_hStartupMutex = NULL;
  26. BOOL        g_fMenuTracking = FALSE;
  27. HWND        g_hMainWnd      = NULL;
  28. HDESK       g_hMainDesktop  = NULL;
  29. HWND        g_hStatusWnd    = NULL;
  30. HINSTANCE   g_hInstance     = NULL;
  31. HACCEL      g_hAccel        = NULL;
  32. BYTE        g_cProcessors   = (BYTE) 0;
  33. HBITMAP     g_hbmpBack      = NULL;
  34. HBITMAP     g_hbmpForward   = NULL;
  35. HMENU       g_hMenu         = NULL;
  36. BOOL        g_fCantHide     = FALSE;
  37. BOOL        g_fInPopup      = FALSE;
  38. DWORD       g_idTrayThread  = 0;
  39. HANDLE      g_hTrayThread   = NULL;
  40. LONG        g_minWidth      = 0;
  41. LONG        g_minHeight     = 0;
  42. LONG        g_DefSpacing    = 0;
  43. LONG        g_InnerSpacing  = 0;
  44. LONG        g_TopSpacing    = 0;
  45. LONG        g_cxEdge        = 0;
  46. HRGN        g_hrgnView      = NULL;
  47. HRGN        g_hrgnClip      = NULL;
  48. HBRUSH      g_hbrWindow     = NULL;
  49. COptions    g_Options;
  50. static BOOL fAlreadySetPos  = FALSE;
  51. BOOL g_bMirroredOS = FALSE;
  52. #define SHORTSTRLEN         32
  53. //
  54. // Global strings - short strings used too often to be LoadString'd
  55. //                  every time
  56. //
  57. TCHAR       g_szRealtime    [SHORTSTRLEN];
  58. TCHAR       g_szNormal      [SHORTSTRLEN];
  59. TCHAR       g_szHigh        [SHORTSTRLEN];
  60. TCHAR       g_szLow         [SHORTSTRLEN];
  61. TCHAR       g_szUnknown     [SHORTSTRLEN];
  62. TCHAR       g_szAboveNormal [SHORTSTRLEN];
  63. TCHAR       g_szBelowNormal [SHORTSTRLEN];
  64. TCHAR       g_szHung        [SHORTSTRLEN];
  65. TCHAR       g_szRunning     [SHORTSTRLEN];
  66. TCHAR       g_szfmtTasks    [SHORTSTRLEN];
  67. TCHAR       g_szfmtProcs    [SHORTSTRLEN];
  68. TCHAR       g_szfmtCPU      [SHORTSTRLEN];  
  69. TCHAR       g_szfmtMEM      [SHORTSTRLEN];  
  70. TCHAR       g_szfmtCPUNum   [SHORTSTRLEN];
  71. TCHAR       g_szTotalCPU    [SHORTSTRLEN];
  72. TCHAR       g_szKernelCPU   [SHORTSTRLEN];
  73. TCHAR       g_szMemUsage    [SHORTSTRLEN];
  74. TCHAR       g_szK[10];                     // Localized "K"ilobyte symbol
  75. // Page Array
  76. // 
  77. // Each of the page objects is delcared here, and g_pPages is an array
  78. // of pointers to those instantiated objects (at global scope).  The main
  79. // window code can call through the base members of the CPage class to
  80. // do things like sizing, etc., without worrying about whatever specific
  81. // stuff each page might do
  82. CPage * g_pPages[NUM_PAGES] = { NULL };
  83. // to hold pointer to the winsta.dll function
  84. // this function is used to get the username for the given process.
  85. pfnWinStationGetProcessSid gpfnWinStationGetProcessSid = 0;
  86. // to hold the pointer to the utildll function.
  87. // this function hold gets the username from sid.
  88. pfnCachedGetUserFromSid gpfnCachedGetUserFromSid = 0;
  89. // to hold pointer to the winsta.dll function
  90. // this function is used to terminate cross session processes
  91. // NtOpenProcess on cross session processes fails unless you enable
  92. // the debug privilige. This is done by termsrv.exe
  93. pfnWinStationTerminateProcess gpfnWinStationTerminateProcess = 0;
  94. /*
  95.    Superclass of GROUPBOX
  96.  
  97.    We need to turn on clipchildren for our dialog which contains the
  98.    history graphs, so they don't get erased during the repaint cycle.
  99.    Unfortunately, group boxes don't erase their backgrounds, so we
  100.    have to superclass them and provide a control that does.
  101.    This is a lot of extra crap, but the painting is several orders of
  102.    magnitude nicer with it...
  103. */
  104. /*++ DavesFrameWndProc
  105. Routine Description:
  106.     WndProc for the custom group box class.  Primary difference from
  107.     standard group box is that this one knows how to erase its own
  108.     background, and doesn't rely on the parent to do it for it.
  109.     These controls also have CLIPSIBLINGS turn on so as not to stomp
  110.     on the ownderdraw graphs they surround.
  111.     
  112. Arguments:
  113.     standard wndproc fare
  114. Revision History:
  115.       Nov-29-95 Davepl  Created
  116. --*/
  117. WNDPROC oldButtonWndProc = NULL;
  118.                                
  119. LRESULT DavesFrameWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
  120. {
  121.     if (msg == WM_CREATE)
  122.     {
  123.         //
  124.         // Turn on clipsiblings for the frame
  125.         //
  126.         DWORD dwStyle = GetWindowLong(hWnd, GWL_STYLE);
  127.         dwStyle |= WS_CLIPSIBLINGS;
  128.         SetWindowLong(hWnd, GWL_STYLE, dwStyle);
  129.     }
  130.     else if (msg == WM_ERASEBKGND)
  131.     {
  132.         //
  133.         // Erase our background so that we don't get ugly in a dialog that has
  134.         // clip children on (as our parents will in this app)
  135.         //
  136.         HDC hdc = (HDC) wParam;
  137.         // 
  138.         // If no dc is supplied by the caller, we create our own by building the
  139.         // update region and using GetDCEx to get a DC that corresponds to only
  140.         // the actual area that needs to be erased.
  141.         //
  142.                 
  143.         HRGN hRegion;
  144.         if (0 == wParam)
  145.         {
  146.             // Isn't Windows painting self-evident?
  147.             hRegion = CreateRectRgn(0, 0, 0, 0);
  148.             if (hRegion)
  149.             {
  150.                 GetUpdateRgn(hWnd, hRegion, TRUE);
  151.                 hdc = GetDCEx(hWnd, hRegion, DCX_CACHE | DCX_CLIPSIBLINGS | DCX_INTERSECTRGN);
  152.             }
  153.         }
  154.         RECT rcClient;
  155.         GetClientRect(hWnd, &rcClient);
  156.         FillRect(hdc, &rcClient, (HBRUSH)(COLOR_3DFACE + 1));
  157.         if (0 == wParam)
  158.         {
  159.             // If your compiler warns about hRegion not being initialized, it's lying
  160.             ReleaseDC(hWnd, hdc);
  161.             if (hRegion)
  162.             {
  163.                 DeleteObject(hRegion);
  164.             }
  165.         }
  166.         return TRUE;
  167.     }
  168.     // For anything else, we defer to the standard button class code
  169.     return oldButtonWndProc(hWnd, msg, wParam, lParam);
  170. }
  171. /*++ COptions::Save 
  172. Routine Description:
  173.    Saves current options to the registy
  174.  
  175. Arguments:
  176. Returns:
  177.     
  178.     HRESULT
  179. Revision History:
  180.       Jan-01-95 Davepl  Created
  181. --*/
  182. const TCHAR szTaskmanKey[] = TEXT("Software\Microsoft\Windows NT\CurrentVersion\TaskManager");
  183. const TCHAR szOptionsKey[] = TEXT("Preferences");
  184. HRESULT COptions::Save()
  185. {
  186.     DWORD dwDisposition;
  187.     HKEY  hkSave;
  188.     if (ERROR_SUCCESS != RegCreateKeyEx(HKEY_CURRENT_USER,
  189.                                         szTaskmanKey,
  190.                                         0,
  191.                                         TEXT("REG_BINARY"),
  192.                                         REG_OPTION_NON_VOLATILE,
  193.                                         KEY_WRITE,
  194.                                         NULL,
  195.                                         &hkSave,
  196.                                         &dwDisposition))
  197.     {
  198.         return GetLastHRESULT();
  199.     }
  200.     if (ERROR_SUCCESS != RegSetValueEx(hkSave,
  201.                                        szOptionsKey,
  202.                                        0,
  203.                                        REG_BINARY,
  204.                                        (LPBYTE) this,
  205.                                        sizeof(COptions)))
  206.     {
  207.         RegCloseKey(hkSave);
  208.         return GetLastHRESULT();
  209.     }
  210.     RegCloseKey(hkSave);
  211.     return S_OK;
  212. }
  213. /*++ COptions::Load
  214. Routine Description:
  215.    Loads current options to the registy
  216.  
  217. Arguments:
  218. Returns:
  219.     
  220.     HRESULT
  221. Revision History:
  222.       Jan-01-95 Davepl  Created
  223. --*/
  224. HRESULT COptions::Load()
  225. {
  226.     HKEY  hkSave;
  227.     // If ctrl-alt-shift is down at startup, "forget" registry settings
  228.     if (GetKeyState(VK_SHIFT) < 0 &&
  229.         GetKeyState(VK_MENU)  < 0 &&
  230.         GetKeyState(VK_CONTROL) < 0   )
  231.     {
  232.         SetDefaultValues();
  233.         return S_FALSE;
  234.     }
  235.     if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_CURRENT_USER,
  236.                                       szTaskmanKey,
  237.                                       0,
  238.                                       KEY_READ,
  239.                                       &hkSave))
  240.     {
  241.         return S_FALSE;
  242.     }
  243.     DWORD dwType;
  244.     DWORD dwSize = sizeof(COptions);
  245.     if (ERROR_SUCCESS       != RegQueryValueEx(hkSave,
  246.                                                szOptionsKey,
  247.                                                0,
  248.                                                &dwType,
  249.                                                (LPBYTE) this,
  250.                                                &dwSize) 
  251.         
  252.         // Validate type and size of options info we got from the registry
  253.         || dwType           != REG_BINARY 
  254.         || dwSize           != sizeof(COptions)
  255.         // Validate options, revert to default if any are invalid (like if
  256.         // the window would be offscreen)
  257.         || MonitorFromRect(&m_rcWindow, MONITOR_DEFAULTTONULL) == NULL
  258.         || m_iCurrentPage    > NUM_PAGES - 1)
  259.     {
  260.         // Reset to default values
  261.         SetDefaultValues();
  262.         RegCloseKey(hkSave);
  263.         return S_FALSE;
  264.     }
  265.     RegCloseKey(hkSave);
  266.     // if machine is updated to/from terminal server
  267.     // we might get wrong columns from the registry.
  268.     // for example if machine was previously hydra and 
  269.     // the last columns displayed contained username.
  270.     // Afterwords hydra is uninstalled. Now when we 
  271.     // load the columns next time we find username there
  272.     // which should not be in case of non hydra systems.
  273.     // lets take care of such situation here.
  274.     // _HYDRA_
  275.     if( !IsTerminalServer( ) )
  276.     {
  277.         // remove username, session id columns if present.
  278.         for( int i = 0; i < NUM_COLUMN + 1 ; i++ )
  279.         {
  280.             if( g_Options.m_ActiveProcCol[ i ] == -1 )
  281.             {
  282.                 // this is end of column list.
  283.                 break;  
  284.             }
  285.             if( g_Options.m_ActiveProcCol[ i ] == COL_SESSIONID || g_Options.m_ActiveProcCol[ i ] == COL_USERNAME )
  286.             {
  287.                 // current values are not good as they contain hydra specific column
  288.                 // so lets set the default values.
  289.                 SetDefaultValues();
  290.                 break;
  291.             }
  292.         }
  293.     }
  294.     //_HYDRA_
  295.     return S_OK;
  296. }
  297. BOOL FPalette(void)
  298. {
  299.     HDC hdc = GetDC(NULL);
  300.     BOOL fPalette = (GetDeviceCaps(hdc, NUMCOLORS) != -1);
  301.     ReleaseDC(NULL, hdc);
  302.     return fPalette;
  303. }
  304. #define CMS_FADE 200
  305. inline void MyShowWindow(HWND hwnd, UINT nShowCmd)
  306. {
  307. #if 0
  308.     SYSTEM_INFO si;
  309.     GetSystemInfo(&si);
  310.     int cx = g_Options.m_rcWindow.right-g_Options.m_rcWindow.left;
  311.     int cy = g_Options.m_rcWindow.bottom-g_Options.m_rcWindow.top;
  312.     
  313.     if ((nShowCmd == SW_SHOWNORMAL || nShowCmd == SW_SHOW || 
  314.             nShowCmd == SW_SHOWDEFAULT || nShowCmd == SW_HIDE)          && 
  315.             !FPalette()                                                 && 
  316.             (cx == g_minWidth)                                          && 
  317.             (cy == g_minHeight)                                         &&
  318.             ((si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL
  319.                     &&   si.wProcessorLevel >= 6) ||
  320.             (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_ALPHA
  321.                     &&   si.wProcessorLevel >= 21164))) {
  322.         if (nShowCmd != SW_HIDE) {
  323.             HWND hwndTab = GetDlgItem(g_hMainWnd, IDC_TABS);
  324.             SetWindowPos(hwndTab, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
  325.             AnimateWindow(g_hMainWnd, CMS_FADE, AW_BLEND | AW_ACTIVATE);
  326.             SetWindowPos(hwndTab, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
  327.         } else {
  328.             AnimateWindow(g_hMainWnd, CMS_FADE, AW_BLEND | AW_HIDE);
  329.         }
  330.    } else {
  331.        ShowWindow(g_hMainWnd, nShowCmd);
  332.    }
  333. #endif
  334.    ShowWindow(g_hMainWnd, nShowCmd);
  335. }
  336. inline void MyDestroyWindow(HWND hwnd)
  337. {
  338. //    MyShowWindow(hwnd, SW_HIDE);
  339.     DestroyWindow(hwnd);
  340. }
  341. /*++ InitDavesControls
  342. Routine Description:
  343.    Superclasses GroupBox for better drawing
  344.  
  345.    Note that I'm not very concerned about failure here, since it
  346.    something goes wrong the dialog creation will fail awayway, and
  347.    it will be handled there
  348.     
  349. Arguments:
  350. Revision History:
  351.       Nov-29-95 Davepl  Created
  352. --*/
  353. void InitDavesControls()
  354. {
  355.     static const TCHAR szControlName[] = TEXT("DavesFrameClass");
  356.     WNDCLASS wndclass;
  357.     // 
  358.     // Get the class info for the Button class (which is what group
  359.     // boxes really are) and create a new class based on it
  360.     //
  361.     GetClassInfo(g_hInstance, TEXT("Button"), &wndclass);
  362.     oldButtonWndProc = wndclass.lpfnWndProc;
  363.     
  364.     wndclass.hInstance = g_hInstance;
  365.     wndclass.lpfnWndProc = DavesFrameWndProc;
  366.     wndclass.lpszClassName = szControlName;
  367.     ATOM atom = RegisterClass(&wndclass);
  368.     return;
  369. }
  370. /*++ SetTitle
  371. Routine Description:
  372.     Sets the app's title in the title bar (we do this on startup and
  373.     when coming out of notitle mode).
  374.     
  375. Arguments:
  376.     
  377.     none
  378. Return Value:
  379.     none
  380. Revision History:
  381.     Jan-24-95 Davepl  Created
  382. --*/
  383. void SetTitle()
  384. {
  385.     TCHAR szTitle[MAX_PATH];
  386.     LoadString(g_hInstance, IDS_APPTITLE, szTitle, MAX_PATH);
  387.     SetWindowText(g_hMainWnd, szTitle);
  388. }
  389. /*++ UpdateMenuStates
  390. Routine Description:
  391.     Updates the menu checks / ghosting based on the
  392.     current settings and options
  393.     
  394. Arguments:
  395. Return Value:
  396. Revision History:
  397.       Nov-29-95 Davepl  Created
  398. --*/
  399. void UpdateMenuStates()
  400. {
  401.     HMENU hMenu = GetMenu(g_hMainWnd);
  402.     if (hMenu)
  403.     {
  404.         CheckMenuRadioItem(hMenu, VM_FIRST, VM_LAST, VM_FIRST + (UINT) g_Options.m_vmViewMode, MF_BYCOMMAND);
  405.         CheckMenuRadioItem(hMenu, CM_FIRST, CM_LAST, CM_FIRST + (UINT) g_Options.m_cmHistMode, MF_BYCOMMAND);
  406.         CheckMenuRadioItem(hMenu, US_FIRST, US_LAST, US_FIRST + (UINT) g_Options.m_usUpdateSpeed, MF_BYCOMMAND);
  407.     }
  408.     // REVIEW (davepl) could be table driven
  409.     
  410.     CheckMenuItem(hMenu, IDM_ALWAYSONTOP,       MF_BYCOMMAND | (g_Options.m_fAlwaysOnTop   ? MF_CHECKED : MF_UNCHECKED));
  411.     CheckMenuItem(hMenu, IDM_MINIMIZEONUSE,     MF_BYCOMMAND | (g_Options.m_fMinimizeOnUse ? MF_CHECKED : MF_UNCHECKED));
  412.     CheckMenuItem(hMenu, IDM_KERNELTIMES,       MF_BYCOMMAND | (g_Options.m_fKernelTimes   ? MF_CHECKED : MF_UNCHECKED));    
  413.     CheckMenuItem(hMenu, IDM_NOTITLE,           MF_BYCOMMAND | (g_Options.m_fNoTitle       ? MF_CHECKED : MF_UNCHECKED));
  414.     CheckMenuItem(hMenu, IDM_HIDEWHENMIN,       MF_BYCOMMAND | (g_Options.m_fHideWhenMin   ? MF_CHECKED : MF_UNCHECKED));
  415.     CheckMenuItem(hMenu, IDM_SHOW16BIT,         MF_BYCOMMAND | (g_Options.m_fShow16Bit     ? MF_CHECKED : MF_UNCHECKED));
  416. #ifdef LATER    // Put back when Sandbox becomes official
  417.     CheckMenuItem(hMenu, IDM_SHOWSANDBOXNAMES,  MF_BYCOMMAND | (g_Options.m_fShowSandboxNames     ? MF_CHECKED : MF_UNCHECKED));
  418. #endif
  419.     // Remove the CPU history style options on single processor machines
  420.     if (g_cProcessors < 2)
  421.     {
  422.         DeleteMenu(hMenu, IDM_ALLCPUS, MF_BYCOMMAND);
  423.     }
  424. }
  425. /*++ SizeChildPage
  426. Routine Description:
  427.     Size the active child page based on the tab control
  428.     
  429. Arguments:
  430.     hwndMain    - Main window
  431. Return Value:
  432. Revision History:
  433.       Nov-29-95 Davepl  Created
  434. --*/
  435. void SizeChildPage(HWND hwndMain)
  436. {
  437.     if (g_Options.m_iCurrentPage >= 0)
  438.     {
  439.         // If we are in maximum viewing mode, the page gets the whole
  440.         // window area
  441.         
  442.         HWND hwndPage = g_pPages[g_Options.m_iCurrentPage]->GetPageWindow();
  443.         DWORD dwStyle = GetWindowLong (g_hMainWnd, GWL_STYLE);
  444.     
  445.         if (g_Options.m_fNoTitle)
  446.         {
  447.             RECT rcMainWnd;
  448.             GetClientRect(g_hMainWnd, &rcMainWnd);
  449.             SetWindowPos(hwndPage, HWND_TOP, rcMainWnd.left, rcMainWnd.top,
  450.                     rcMainWnd.right - rcMainWnd.left, 
  451.                     rcMainWnd.bottom - rcMainWnd.top, SWP_NOZORDER | SWP_NOACTIVATE);
  452.     
  453.             // remove caption & menu bar, etc.
  454.             dwStyle &= ~(WS_DLGFRAME | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX);
  455.             // SetWindowLong (g_hMainWnd, GWL_ID, 0);            
  456.             SetWindowLong (g_hMainWnd, GWL_STYLE, dwStyle);
  457.             SetMenu(g_hMainWnd, NULL);
  458.         }
  459.         else
  460.         {                                
  461.             // If we have a page being displayed, we need to size it also
  462.             // put menu bar & caption back in 
  463.             dwStyle = WS_TILEDWINDOW | dwStyle;
  464.             SetWindowLong (g_hMainWnd, GWL_STYLE, dwStyle);
  465.             if (g_hMenu)
  466.             {
  467.                 SetMenu(g_hMainWnd, g_hMenu);
  468.                 UpdateMenuStates();
  469.             }
  470.             SetTitle();
  471.               
  472.             if (hwndPage)
  473.             {
  474.                 RECT rcCtl;
  475.                 HWND hwndCtl = GetDlgItem(hwndMain, IDC_TABS);
  476.                 GetClientRect(hwndCtl, &rcCtl);
  477.                 MapWindowPoints(hwndCtl, hwndMain, (LPPOINT)&rcCtl, 2);
  478.                 TabCtrl_AdjustRect(hwndCtl, FALSE, &rcCtl);
  479.                 SetWindowPos(hwndPage, HWND_TOP, rcCtl.left, rcCtl.top,
  480.                         rcCtl.right - rcCtl.left, rcCtl.bottom - rcCtl.top, SWP_NOZORDER | SWP_NOACTIVATE);
  481.             }
  482.         }
  483.     }
  484. }
  485. /*++ UpdateStatusBar
  486. Routine Description:
  487.     Draws the status bar with test based on data accumulated by all of
  488.     the various pages (basically a summary of most important info)
  489.     
  490. Arguments:
  491. Return Value:
  492. Revision History:
  493.       Nov-29-95 Davepl  Created
  494. --*/
  495. void UpdateStatusBar()
  496. {
  497.     //
  498.     // If we're in menu-tracking mode (sticking help text in the stat
  499.     // bar), we don't draw our standard text
  500.     //
  501.     if (FALSE == g_fMenuTracking)
  502.     {
  503.         TCHAR szText[MAX_PATH];
  504.     
  505.         wsprintf(szText, g_szfmtProcs, g_cProcesses);
  506.         SendMessage(g_hStatusWnd, SB_SETTEXT, 0, (LPARAM) szText);
  507.         wsprintf(szText, g_szfmtCPU, g_CPUUsage);
  508.         SendMessage(g_hStatusWnd, SB_SETTEXT, 1, (LPARAM) szText);
  509.         wsprintf(szText, g_szfmtMEM, g_MEMUsage, g_MEMMax);
  510.         SendMessage(g_hStatusWnd, SB_SETTEXT, 2, (LPARAM) szText);
  511.     }
  512. }
  513. /*++ MainWnd_OnTimer
  514. Routine Description:
  515.     Called when the refresh timer fires, we pass a timer event on to
  516.     each of the child pages.  
  517. Arguments:
  518.     hwnd    - window timer was received at
  519.     id      - id of timer that was received
  520. Return Value:
  521. Revision History:
  522.       Nov-30-95 Davepl  Created
  523. --*/
  524. void MainWnd_OnTimer(HWND hwnd, UINT id)
  525. {
  526.     if (GetForegroundWindow() == hwnd && GetAsyncKeyState(VK_CONTROL) < 0)
  527.     {
  528.         // CTRL alone means pause
  529.         return;
  530.     }
  531.     // Notify each of the pages in turn that they need to updatre
  532.     for (int i = 0; i < ARRAYSIZE(g_pPages); i++)
  533.     {
  534.         g_pPages[i]->TimerEvent();
  535.     }
  536.     // Update the tray icon
  537.     UINT iIconIndex = (g_CPUUsage * g_cTrayIcons) / 100;
  538.     if (iIconIndex >= g_cTrayIcons)
  539.     {
  540.         iIconIndex = g_cTrayIcons - 1;      // Handle 100% case
  541.     }
  542.     TCHAR szTipText[MAX_PATH];
  543.     wsprintf(szTipText, g_szfmtCPU, g_CPUUsage);
  544.     CTrayNotification * pNot = new CTrayNotification(hwnd, 
  545.                                                      PWM_TRAYICON, 
  546.                                                      NIM_MODIFY, 
  547.                                                      g_aTrayIcons[iIconIndex], 
  548.                                                      szTipText);
  549.     if (pNot)
  550.     {
  551.         if (FALSE == DeliverTrayNotification(pNot))
  552.         {
  553.             delete pNot;
  554.         }
  555.     }
  556.     UpdateStatusBar();
  557. }
  558. /*++ MainWnd_OnInitDialog
  559. Routine Description:
  560.     Processes WM_INITDIALOG for the main window (a modeless dialog)
  561.     
  562. Revision History:
  563.       Nov-29-95 Davepl  Created
  564. --*/
  565. BOOL MainWnd_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam)
  566. {
  567.     RECT rcMain;
  568.     GetWindowRect(hwnd, &rcMain);
  569.     g_minWidth  = rcMain.right - rcMain.left;
  570.     g_minHeight = rcMain.bottom - rcMain.top;
  571.     g_DefSpacing   = (DEFSPACING_BASE   * LOWORD(GetDialogBaseUnits())) / DLG_SCALE_X;
  572.     g_InnerSpacing = (INNERSPACING_BASE * LOWORD(GetDialogBaseUnits())) / DLG_SCALE_X; 
  573.     g_TopSpacing   = (TOPSPACING_BASE   * HIWORD(GetDialogBaseUnits())) / DLG_SCALE_Y;
  574.     // Load the user's defaults
  575.     g_Options.Load();
  576.     //
  577.     // On init, save away the window handle for all to see
  578.     //
  579.     g_hMainWnd = hwnd;
  580.     g_hMainDesktop = GetThreadDesktop(GetCurrentThreadId());
  581.     // init some globals
  582.     g_cxEdge = GetSystemMetrics(SM_CXEDGE);
  583.     g_hrgnView = CreateRectRgn(0, 0, 0, 0);
  584.     g_hrgnClip = CreateRectRgn(0, 0, 0, 0);
  585.     g_hbrWindow = CreateSolidBrush(GetSysColor(COLOR_WINDOW));
  586.     // If we're supposed to be TOPMOST, start out that way
  587.     if (g_Options.m_fAlwaysOnTop)
  588.     {
  589.         SetWindowPos(hwnd, HWND_TOPMOST, 0,0,0,0, SWP_NOMOVE | SWP_NOSIZE);
  590.     }
  591.     //        
  592.     // Create the status window
  593.     //
  594.     g_hStatusWnd = CreateStatusWindow(WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE | SBARS_SIZEGRIP,
  595.                                       NULL,
  596.                                       hwnd,
  597.                                       IDC_STATUSWND);
  598.     if (NULL == g_hStatusWnd)
  599.     {
  600.         return FALSE;
  601.     }
  602.     //
  603.     // Base the panes in the status bar off of the LOGPIXELSX system metric
  604.     //
  605.     HDC hdc = GetDC(NULL);
  606.     INT nInch = GetDeviceCaps(hdc, LOGPIXELSX);
  607.     ReleaseDC(NULL, hdc);
  608.     int ciParts[] = {             nInch, 
  609.                      ciParts[0] + (nInch * 5) / 4, 
  610.                      ciParts[1] + (nInch * 5) / 2, 
  611.                      -1};
  612.     if (g_hStatusWnd) 
  613.     {
  614.         SendMessage(g_hStatusWnd, SB_SETPARTS, ARRAYSIZE(ciParts), (LPARAM)ciParts);
  615.     }
  616.     //
  617.     // Load our app icon
  618.     //
  619.     HICON hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_MAIN));
  620.     if (hIcon)
  621.     {
  622.         SendMessage(hwnd, WM_SETICON, TRUE, LPARAM(hIcon));
  623.     }
  624.     // Add our tray icon
  625.     CTrayNotification * pNot = new CTrayNotification(hwnd, 
  626.                                                      PWM_TRAYICON, 
  627.                                                      NIM_ADD, 
  628.                                                      g_aTrayIcons[0], 
  629.                                                      NULL);
  630.     if (pNot)
  631.     {
  632.         if (FALSE == DeliverTrayNotification(pNot))
  633.         {
  634.             delete pNot;
  635.         }
  636.     }
  637.     //
  638.     // Turn on TOPMOST for the status bar so it doesn't slide under the
  639.     // tab control
  640.     //
  641.     SetWindowPos(g_hStatusWnd,
  642.                  HWND_TOPMOST,
  643.                  0,0,0,0,
  644.                  SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOREDRAW);
  645.     //
  646.     // Intialize each of the the pages in turn
  647.     //
  648.     HWND hwndTabs = GetDlgItem(hwnd, IDC_TABS);
  649.     for (int i = 0; i < ARRAYSIZE(g_pPages); i++)
  650.     {
  651.         HRESULT hr;
  652.                 
  653.         hr = g_pPages[i]->Initialize(hwndTabs);
  654.         if (SUCCEEDED(hr))
  655.         {
  656.             //
  657.             // Get the title of the new page, and use it as the title of
  658.             // the page which we insert into the tab control
  659.             //
  660.             TCHAR szTitle[MAX_PATH];
  661.             
  662.             g_pPages[i]->GetTitle(szTitle, ARRAYSIZE(szTitle));
  663.             TC_ITEM tcitem =
  664.             {
  665.                 TCIF_TEXT,          // value specifying which members to retrieve or set 
  666.                 NULL,               // reserved; do not use 
  667.                 NULL,               // reserved; do not use 
  668.                 szTitle,            // pointer to string containing tab text 
  669.                 ARRAYSIZE(szTitle), // size of buffer pointed to by the pszText member 
  670.                 0,                  // index to tab control's image 
  671.                 NULL                // application-defined data associated with tab 
  672.             };
  673.             if (- 1 == TabCtrl_InsertItem(hwndTabs, i, &tcitem))
  674.             {
  675.                 hr = E_FAIL;
  676.             }
  677.         }
  678.         //
  679.         // If a page failed to init, destroy the pages we have created up to this point
  680.         // We don't remove the tabs, on the assumption that the app is going away anyway
  681.         //
  682.         if (FAILED(hr))
  683.         {
  684.             for (int j = i; j >= 0; j--)
  685.             {
  686.                 g_pPages[i]->Destroy();
  687.             }
  688.             return FALSE;
  689.         }
  690.         else
  691.         {
  692.             
  693.         }
  694.     }
  695.     //
  696.     // Set the inital menu states
  697.     //
  698.     UpdateMenuStates();
  699.     //
  700.     // Activate a page (pick page 0 if no preference is set)
  701.     //
  702.     if (g_Options.m_iCurrentPage < 0)
  703.     {
  704.         g_Options.m_iCurrentPage = 0;
  705.     }
  706.     
  707.     TabCtrl_SetCurSel(GetDlgItem(g_hMainWnd, IDC_TABS), g_Options.m_iCurrentPage);
  708.     
  709.     g_pPages[g_Options.m_iCurrentPage]->Activate();
  710.     RECT rcMainClient;
  711.     GetClientRect(hwnd, &rcMainClient);
  712.     MainWnd_OnSize(g_hMainWnd, 0, rcMainClient.right - rcMainClient.left, rcMainClient.bottom - rcMainClient.top);
  713.     //
  714.     // Create the update timer
  715.     //
  716.     if (g_Options.m_dwTimerInterval)        // 0 == paused
  717.     {
  718.         SetTimer(g_hMainWnd, 0, g_Options.m_dwTimerInterval, NULL);
  719.     }
  720.     
  721.     // Force at least one intial update so that we don't need to wait
  722.     // for the first timed update to come through
  723.     MainWnd_OnTimer(g_hMainWnd, 0);
  724.     //
  725.     // Disable the MP-specific menu items
  726.     //
  727.     if (g_cProcessors <= 1)
  728.     {
  729.         HMENU hMenu = GetMenu(g_hMainWnd);
  730.         EnableMenuItem(hMenu, IDM_MULTIGRAPH, MF_BYCOMMAND | MF_GRAYED);
  731.     }
  732.     return TRUE;    
  733. }
  734. //
  735. // Draw an edge just below menu bar
  736. //
  737. void MainWnd_Draw(HWND hwnd, HDC hdc)
  738. {
  739.     RECT rc;
  740.     GetClientRect(hwnd, &rc);
  741.         DrawEdge(hdc, &rc, EDGE_ETCHED, BF_TOP);
  742. }
  743. void MainWnd_OnPrintClient(HWND hwnd, HDC hdc)
  744. {
  745.     MainWnd_Draw(hwnd, hdc);
  746. }
  747. /*++ MainWnd_OnPaint
  748. Routine Description:
  749.     Just draws a thin edge just below the main menu bar
  750.     
  751. Arguments:
  752.     hwnd    - Main window
  753. Return Value:
  754. Revision History:
  755.       Nov-29-95 Davepl  Created
  756. --*/
  757. void MainWnd_OnPaint(HWND hwnd)
  758. {
  759.     PAINTSTRUCT ps;
  760.     //
  761.     // Don't waste our time if we're minimized
  762.     //
  763.     if (FALSE == IsIconic(hwnd))
  764.     {
  765.         BeginPaint(hwnd, &ps);
  766.     MainWnd_Draw(hwnd, ps.hdc);
  767.         EndPaint(hwnd, &ps);
  768.     }
  769.     else
  770.     {
  771.         FORWARD_WM_PAINT(hwnd, DefWindowProc);
  772.     }
  773. }
  774. /*++ MainWnd_OnMenuSelect
  775. Routine Description:
  776.     As the user browses menus in the app, writes help text to the
  777.     status bar.  Also temporarily sets it to be a plain status bar
  778.     with no panes
  779. Arguments:
  780. Return Value:
  781. Revision History:
  782.       Nov-29-95 Davepl  Created
  783. --*/
  784. void MainWnd_OnMenuSelect(HWND hwnd, HMENU hmenu, int item, HMENU hmenuPopup, UINT flags)
  785. {
  786.     //
  787.     // If menu is dismissed, restore the panes in the status bar, turn off the
  788.     // global "menu tracking" flag, and redraw the status bar with normal info
  789.     //
  790.     if ((0xFFFF == LOWORD(flags) && NULL == hmenu) ||       // dismissed the menu
  791.         (flags & (MF_SYSMENU | MF_SEPARATOR)))              // sysmenu or separator
  792.     {
  793.         SendMessage(g_hStatusWnd, SB_SIMPLE, FALSE, 0L);    // Restore sb panes
  794.         g_fMenuTracking = FALSE;
  795.         g_fCantHide = FALSE;
  796.         UpdateStatusBar();
  797.         return;
  798.     }
  799.     else
  800.     {
  801.         //
  802.         // If its a popup, go get the submenu item that is selected instead
  803.         //
  804.         if (flags & MF_POPUP)
  805.         {
  806.             MENUITEMINFO miiSubMenu;
  807.             miiSubMenu.cbSize = sizeof(MENUITEMINFO);
  808.             miiSubMenu.fMask = MIIM_ID;
  809.             miiSubMenu.cch = 0;             
  810.             if (FALSE == GetMenuItemInfo(hmenu, item, TRUE, &miiSubMenu))
  811.             {
  812.                 return;
  813.             }
  814.             //
  815.             // Change the parameters to simulate a "normal" menu item
  816.             //
  817.             item = miiSubMenu.wID;
  818.             flags &= ~MF_POPUP;
  819.         }
  820.         //
  821.         // Our menus always have the same IDs as the strings that describe
  822.         // their functions... 
  823.         //
  824.         TCHAR szStatusText[MAX_PATH];
  825.         LoadString(g_hInstance, item, szStatusText, ARRAYSIZE(szStatusText));
  826.         g_fMenuTracking = TRUE;
  827.         SendMessage(g_hStatusWnd, SB_SETTEXT, SBT_NOBORDERS | 255, (LPARAM)szStatusText);
  828.         SendMessage(g_hStatusWnd, SB_SIMPLE, TRUE, 0L);  // Remove sb panes
  829.         SendMessage(g_hStatusWnd, SB_SETTEXT, SBT_NOBORDERS | 0, (LPARAM) szStatusText);
  830.     }
  831. }
  832. /*++ MainWnd_OnTabCtrlNotify
  833. Routine Description:
  834.     Handles WM_NOTIFY messages sent to the main window on behalf of the
  835.     tab control
  836. Arguments:
  837.     pnmhdr - ptr to the notification block's header
  838. Return Value:
  839.     BOOL - depends on message
  840. Revision History:
  841.       Nov-29-95 Davepl  Created
  842. --*/
  843. BOOL MainWnd_OnTabCtrlNotify(LPNMHDR pnmhdr)
  844. {
  845.     HWND hwndTab = pnmhdr->hwndFrom;
  846.     //
  847.     // Selection is changing (new page coming to the front), so activate
  848.     // the appropriate page
  849.     //
  850.     if (TCN_SELCHANGE == pnmhdr->code)
  851.     {
  852.         INT iTab = TabCtrl_GetCurSel(hwndTab);
  853.         
  854.         if (-1 != iTab)
  855.         {
  856.             if (-1 != g_Options.m_iCurrentPage)
  857.             {
  858.                 g_pPages[g_Options.m_iCurrentPage]->Deactivate();
  859.             }
  860.             if (FAILED(g_pPages[iTab]->Activate()))
  861.             {
  862.                 // If we weren't able to activate the new page,
  863.                 // reactivate the old page just to be sure
  864.                 if (-1 != g_Options.m_iCurrentPage)
  865.                 {
  866.                     g_pPages[iTab]->Activate();                    
  867.                     SizeChildPage(g_hMainWnd);
  868.                 }
  869.             }
  870.             else
  871.             {
  872.                 g_Options.m_iCurrentPage = iTab;
  873.                 SizeChildPage(g_hMainWnd);
  874.                 return TRUE;
  875.             }
  876.         }
  877.     }
  878.     return FALSE;    
  879. }
  880. /*++ MainWnd_OnSize
  881. Routine Description:
  882.     Sizes the children of the main window as the size of the main
  883.     window itself changes
  884. Arguments:
  885.     hwnd    - main window
  886.     state   - window state (not used here)
  887.     cx      - new x size
  888.     cy      - new y size
  889. Return Value:
  890.     BOOL - depends on message
  891. Revision History:
  892.       Nov-29-95 Davepl  Created
  893. --*/
  894. void MainWnd_OnSize(HWND hwnd, UINT state, int cx, int cy)
  895. {
  896.     if (state == SIZE_MINIMIZED)
  897.     {
  898.         // If there's a tray, we can just hide since we have a
  899.         // tray icon anyway.
  900.         if (GetShellWindow() && g_Options.m_fHideWhenMin)
  901.         {
  902.             ShowWindow(hwnd, SW_HIDE);
  903.         }
  904.     }
  905.     //
  906.     // Let the status bar adjust itself first, and we will work back
  907.     // from its new position
  908.     //
  909.     HDWP hdwp = BeginDeferWindowPos(20);
  910.     FORWARD_WM_SIZE(g_hStatusWnd, state, cx, cy, SendMessage);
  911.     RECT rcStatus;
  912.     GetClientRect(g_hStatusWnd, &rcStatus);
  913.     MapWindowPoints(g_hStatusWnd, g_hMainWnd, (LPPOINT) &rcStatus, 2);
  914.     //
  915.     // Size the tab controls based on where the status bar is
  916.     //
  917.     HWND hwndTabs = GetDlgItem(hwnd, IDC_TABS);
  918.     RECT rcTabs;
  919.     GetWindowRect(hwndTabs, &rcTabs);
  920.     MapWindowPoints(HWND_DESKTOP, g_hMainWnd, (LPPOINT) &rcTabs, 2);
  921.     
  922.     INT dx = cx - 2 * rcTabs.left;
  923.     
  924.     DeferWindowPos(hdwp, hwndTabs, NULL, 0, 0, 
  925.                   dx, 
  926.                   cy - (cy - rcStatus.top) - rcTabs.top * 2,
  927.                   SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
  928.     EndDeferWindowPos(hdwp);
  929.     //
  930.     // Now size the front page and its children
  931.     //
  932.     if (cx || cy)               // Don't size in minimized case
  933.         SizeChildPage(hwnd);   
  934. }
  935. /*++ RunDlg
  936. Routine Description:
  937.     Loads shell32.dll and invokes its Run dialog
  938. Arguments:
  939. Return Value:
  940. Revision History:
  941.       Nov-30-95 Davepl  Created
  942. --*/
  943. //
  944. // Prototype for RunFileDlg in shell32 to which we will dynamically bind
  945. //
  946. typedef int (* pfn_RunFileDlg) (HWND hwndParent, 
  947.                                 HICON hIcon, 
  948.                                 LPCTSTR lpszWorkingDir, 
  949.                                 LPCTSTR lpszTitle,
  950.                                 LPCTSTR lpszPrompt, 
  951.                                 DWORD dwFlags);
  952. DWORD RunDlg()
  953. {
  954.     //
  955.     // Load shell32 and get the entry point for the RunFileDlg function
  956.     //
  957. #ifdef SHELL_DYNA_LINK
  958.     HINSTANCE hShellDll = LoadLibrary(TEXT("shell32.dll"));
  959.     if (NULL == hShellDll)
  960.     {
  961.         return FALSE;
  962.     }
  963.     pfn_RunFileDlg pRunDlg = (pfn_RunFileDlg) GetProcAddress(hShellDll, (LPCSTR) 61);
  964.     if (NULL == pRunDlg)
  965.     {
  966.         FreeLibrary(hShellDll);
  967.         return FALSE;
  968.     }
  969. #endif
  970.     //
  971.     // Put up the RUN dialog for the user (its always a UNICODE
  972.     // entry point, so use WCHAR)
  973.     //
  974.     HICON hIcon = (HICON) LoadImage(g_hInstance, MAKEINTRESOURCE(IDI_MAIN), IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR | LR_DEFAULTSIZE);
  975.     if (hIcon)
  976.     {
  977.         WCHAR szCurDir[MAX_PATH];
  978.         WCHAR szTitle [MAX_PATH];
  979.         WCHAR szPrompt[MAX_PATH];
  980.         LoadStringW(g_hInstance, IDS_RUNTITLE, szTitle, MAX_PATH);
  981.         LoadStringW(g_hInstance, IDS_RUNTEXT, szPrompt, MAX_PATH);
  982.         GetCurrentDirectoryW(MAX_PATH, szCurDir);
  983. #ifdef SHELL_DYNA_LINK
  984.         pRunDlg(g_hMainWnd, hIcon, (LPTSTR) szCurDir, 
  985.                                 (LPTSTR) szTitle, 
  986.                                 (LPTSTR) szPrompt, RFD_USEFULLPATHDIR | RFD_WOW_APP);
  987. #else
  988.         RunFileDlg(g_hMainWnd, hIcon, (LPTSTR) szCurDir, 
  989.                                 (LPTSTR) szTitle, 
  990.                                 (LPTSTR) szPrompt, RFD_USEFULLPATHDIR | RFD_WOW_APP);
  991. #endif
  992.         DestroyIcon(hIcon);
  993.     }
  994. #ifdef SHELL_DYNA_LINK
  995.     FreeLibrary(hShellDll);
  996. #endif
  997.     return TRUE;
  998. }
  999. /*++ MainWnd_OnCommand
  1000. Routine Description:
  1001.     Processes WM_COMMAND messages received at the main window
  1002. Revision History:
  1003.       Nov-30-95 Davepl  Created
  1004. --*/
  1005. void MainWnd_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
  1006. {
  1007.     switch (id)
  1008.     {
  1009.         case IDM_HIDE:
  1010.         {
  1011.             ShowWindow(hwnd, SW_MINIMIZE);
  1012.             break;
  1013.         }
  1014.         case IDM_HELP:
  1015.         {
  1016.         HtmlHelpA(GetDesktopWindow(), "taskmgr.chm", HH_DISPLAY_TOPIC, 0);
  1017.             break;
  1018.         }
  1019.         case IDCANCEL:
  1020.         case IDM_EXIT:
  1021.         {
  1022.             MyDestroyWindow(hwnd);
  1023.             break;
  1024.         }
  1025.         case IDM_RESTORETASKMAN:
  1026.         {
  1027.             ShowRunningInstance();
  1028.             break;
  1029.         }
  1030.         case IDC_NEXTTAB:
  1031.         case IDC_PREVTAB:
  1032.         {
  1033.             INT iPage = g_Options.m_iCurrentPage;
  1034.             iPage += (id == IDC_NEXTTAB) ? 1 : -1;
  1035.             iPage = iPage < 0 ? NUM_PAGES - 1 : iPage;
  1036.             iPage = iPage >= NUM_PAGES ? 0 : iPage;
  1037.             // Activate the new page.  If it fails, revert to the current
  1038.             TabCtrl_SetCurSel(GetDlgItem(g_hMainWnd, IDC_TABS), iPage);
  1039.             // SetCurSel doesn't do the page change (that would make too much
  1040.             // sense), so we have to fake up a TCN_SELCHANGE notification
  1041.             NMHDR nmhdr;
  1042.             nmhdr.hwndFrom = GetDlgItem(g_hMainWnd, IDC_TABS);
  1043.             nmhdr.idFrom   = IDC_TABS;
  1044.             nmhdr.code     = TCN_SELCHANGE;
  1045.             if (MainWnd_OnTabCtrlNotify(&nmhdr))
  1046.             {
  1047.                 g_Options.m_iCurrentPage = iPage;
  1048.             }
  1049.             break;
  1050.         }
  1051.         case IDM_ALWAYSONTOP:
  1052.         {
  1053.             g_Options.m_fAlwaysOnTop = !g_Options.m_fAlwaysOnTop;
  1054.             SetWindowPos(hwnd, g_Options.m_fAlwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST, 
  1055.                             0,0,0,0, SWP_NOMOVE | SWP_NOSIZE);
  1056.             UpdateMenuStates();
  1057.             break;
  1058.         }
  1059.         case IDM_HIDEWHENMIN:
  1060.         {
  1061.             g_Options.m_fHideWhenMin = !g_Options.m_fHideWhenMin;
  1062.             UpdateMenuStates();
  1063.             break;
  1064.         }
  1065.         case IDM_MINIMIZEONUSE:
  1066.         {
  1067.             g_Options.m_fMinimizeOnUse = !g_Options.m_fMinimizeOnUse;
  1068.             UpdateMenuStates();
  1069.             break;
  1070.         }
  1071.         case IDM_NOTITLE:
  1072.         {
  1073.             g_Options.m_fNoTitle = !g_Options.m_fNoTitle;
  1074.             UpdateMenuStates();
  1075.             SizeChildPage(hwnd);
  1076.             break;
  1077.         }
  1078.         case IDM_SHOW16BIT:
  1079.         {
  1080.             g_Options.m_fShow16Bit = !g_Options.m_fShow16Bit;
  1081.             UpdateMenuStates();
  1082.             if (g_pPages[PROC_PAGE])
  1083.             {
  1084.                 g_pPages[PROC_PAGE]->TimerEvent();
  1085.             }
  1086.             break;
  1087.         }
  1088. #ifdef LATER        // Put back when Sandbox becomes official
  1089.     case IDM_SHOWSANDBOXNAMES:
  1090.     {
  1091.         g_Options.m_fShowSandboxNames = !g_Options.m_fShowSandboxNames;
  1092.         UpdateMenuStates();
  1093.         if (g_pPages[PROC_PAGE])
  1094.         {
  1095.         g_pPages[PROC_PAGE]->TimerEvent();
  1096.         }
  1097.         break;
  1098.     }
  1099. #endif // LATER
  1100.         case IDM_KERNELTIMES:
  1101.         {
  1102.             g_Options.m_fKernelTimes = !g_Options.m_fKernelTimes;
  1103.             UpdateMenuStates();
  1104.             if (g_pPages[PERF_PAGE])
  1105.             {
  1106.                 g_pPages[PERF_PAGE]->TimerEvent();
  1107.             }
  1108.             break;
  1109.         }
  1110.         case IDM_RUN:
  1111.         {
  1112.             RunDlg();
  1113.             break;
  1114.         }
  1115.         case IDM_SMALLICONS:
  1116.         case IDM_DETAILS:
  1117.         case IDM_LARGEICONS:
  1118.         {
  1119.             g_Options.m_vmViewMode = (VIEWMODE) (id - VM_FIRST);
  1120.             UpdateMenuStates();
  1121.             if (g_pPages[TASK_PAGE])
  1122.             {
  1123.                 g_pPages[TASK_PAGE]->TimerEvent();
  1124.             }
  1125.             break;
  1126.         }
  1127.         // The following few messages get deferred off to the task page
  1128.         case IDM_TASK_CASCADE:
  1129.         case IDM_TASK_MINIMIZE:
  1130.         case IDM_TASK_MAXIMIZE:
  1131.         case IDM_TASK_TILEHORZ:
  1132.         case IDM_TASK_TILEVERT:
  1133.         case IDM_TASK_BRINGTOFRONT:
  1134.         {
  1135.             SendMessage(g_pPages[TASK_PAGE]->GetPageWindow(), WM_COMMAND, id, NULL);
  1136.             break; 
  1137.         }
  1138.         case IDM_PROCCOLS:
  1139.         {
  1140.             if (g_pPages[PROC_PAGE])
  1141.             {
  1142.                 ((CProcPage *) (g_pPages[PROC_PAGE]))->PickColumns();
  1143.             }
  1144.             break;
  1145.         }
  1146.         case IDM_ALLCPUS:
  1147.         case IDM_MULTIGRAPH:
  1148.         {
  1149.             g_Options.m_cmHistMode = (CPUHISTMODE) (id - CM_FIRST);
  1150.             UpdateMenuStates();
  1151.             if (g_pPages[PERF_PAGE])
  1152.             {
  1153.                 ((CPerfPage *)(g_pPages[PERF_PAGE]))->UpdateGraphs();
  1154.                 g_pPages[PERF_PAGE]->TimerEvent();
  1155.             }
  1156.             break;
  1157.         }
  1158.         case IDM_REFRESH:
  1159.         {
  1160.             MainWnd_OnTimer(hwnd, 0);
  1161.             break;
  1162.         }
  1163.         case IDM_HIGH:
  1164.         case IDM_NORMAL:
  1165.         case IDM_LOW:
  1166.         case IDM_PAUSED:
  1167.         {
  1168.             static const int TimerDelays[] = { 500, 2000, 4000, 0, 0xFFFFFFFF };
  1169.             g_Options.m_usUpdateSpeed = (UPDATESPEED) (id - US_FIRST);
  1170.             ASSERT(g_Options.m_usUpdateSpeed <= ARRAYSIZE(TimerDelays));
  1171.             int cTicks = TimerDelays[ (INT) g_Options.m_usUpdateSpeed ];
  1172.             g_Options.m_dwTimerInterval = cTicks;
  1173.             KillTimer(g_hMainWnd, 0);
  1174.             if (cTicks)
  1175.             {
  1176.                 SetTimer(g_hMainWnd, 0, g_Options.m_dwTimerInterval, NULL);
  1177.             }
  1178.             UpdateMenuStates();
  1179.             break;
  1180.         }
  1181.         case IDM_ABOUT:
  1182.         {
  1183.             //
  1184.             // Display the "About Task Manager" dialog
  1185.             //
  1186.             
  1187.             HICON hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_MAIN));
  1188.             if (hIcon)
  1189.             {
  1190.                 TCHAR szTitle[MAX_PATH];
  1191.                 LoadString(g_hInstance, IDS_APPTITLE, szTitle, MAX_PATH);
  1192.                 ShellAbout(hwnd, szTitle, NULL, hIcon);
  1193.                 DestroyIcon(hIcon);
  1194.             }
  1195.         }
  1196.             
  1197.     }
  1198.     
  1199. }
  1200. /*++ CheckParentDeferrals
  1201. Routine Description:
  1202.     Called by the child pages, each child gives the main parent the
  1203.     opportunity to handle certain messages on its behalf
  1204. Arguments:
  1205.     MSG, WPARAM, LPARAM
  1206. Return Value:
  1207.     TRUE if parent handle the message on the childs behalf
  1208. Revision History:
  1209.     Jan-24-95 Davepl  Created
  1210. --*/
  1211. BOOL CheckParentDeferrals(UINT uMsg, WPARAM wParam, LPARAM lParam)
  1212. {
  1213.     switch(uMsg)
  1214.     {
  1215.         case WM_RBUTTONDOWN:
  1216.         case WM_NCRBUTTONDOWN:
  1217.         case WM_RBUTTONUP:
  1218.         case WM_NCRBUTTONUP:
  1219.         case WM_NCLBUTTONDBLCLK:
  1220.         case WM_LBUTTONDBLCLK:
  1221.         {
  1222.             SendMessage(g_hMainWnd, uMsg, wParam, lParam);
  1223.             return TRUE;
  1224.         }
  1225.     
  1226.         default:
  1227.             return FALSE;
  1228.     }
  1229. }
  1230. /*++ ShowRunningInstance
  1231. Routine Description:
  1232.     Brings this running instance to the top, and out of icon state
  1233. Revision History:
  1234.     Jan-27-95 Davepl  Created
  1235. --*/
  1236. void ShowRunningInstance()
  1237. {
  1238.     OpenIcon(g_hMainWnd);
  1239.     SetForegroundWindow(g_hMainWnd);
  1240.     SetWindowPos(g_hMainWnd, g_Options.m_fAlwaysOnTop ? HWND_TOPMOST : HWND_TOP, 
  1241.                  0,0,0,0, SWP_NOMOVE | SWP_NOSIZE);
  1242. }
  1243. /*++ MainWindowProc
  1244. Routine Description:
  1245.     WNDPROC for the main window
  1246. Arguments:
  1247.     Standard wndproc fare
  1248. Return Value:
  1249. Revision History:
  1250.       Nov-30-95 Davepl  Created
  1251. --*/
  1252. INT_PTR CALLBACK MainWindowProc(
  1253.                 HWND        hwnd,               // handle to dialog box
  1254.                 UINT        uMsg,                   // message
  1255.                 WPARAM      wParam,                 // first message parameter
  1256.                 LPARAM      lParam                  // second message parameter
  1257.                 )
  1258. {
  1259.     static BOOL fIsHidden = FALSE;
  1260.     // If this is a size or a move, update the position in the user's options
  1261.     if (uMsg == WM_SIZE || uMsg == WM_MOVE)
  1262.     {
  1263.         // We don't want to start recording the window pos until we've had
  1264.         // a chance to set it to the intialial position, or we'll lose the
  1265.         // user's preferences
  1266.         if (fAlreadySetPos)
  1267.             if (!IsIconic(hwnd) && !IsZoomed(hwnd))
  1268.                 GetWindowRect(hwnd, &g_Options.m_rcWindow);
  1269.     }
  1270.     if (uMsg == g_msgTaskbarCreated) 
  1271.     {
  1272.         // This is done async do taskmgr doesn't hand when the shell
  1273.         // is hung
  1274.             
  1275.         CTrayNotification * pNot = new CTrayNotification(hwnd, 
  1276.                                                          PWM_TRAYICON, 
  1277.                                                          NIM_ADD, 
  1278.                                                          g_aTrayIcons[0], 
  1279.                                                          NULL);
  1280.         if (pNot)
  1281.         {
  1282.             if (FALSE == DeliverTrayNotification(pNot))
  1283.             {
  1284.                 delete pNot;
  1285.             }
  1286.         }
  1287.     }
  1288.     switch(uMsg)
  1289.     {
  1290.         case WM_PAINT:
  1291.             MainWnd_OnPaint(hwnd);
  1292.             return TRUE;
  1293.         HANDLE_MSG(hwnd, WM_INITDIALOG, MainWnd_OnInitDialog);
  1294.         HANDLE_MSG(hwnd, WM_MENUSELECT, MainWnd_OnMenuSelect);
  1295.         HANDLE_MSG(hwnd, WM_SIZE,       MainWnd_OnSize);
  1296.         HANDLE_MSG(hwnd, WM_COMMAND,    MainWnd_OnCommand);
  1297.         HANDLE_MSG(hwnd, WM_TIMER,      MainWnd_OnTimer);
  1298.         case WM_PRINTCLIENT:
  1299.             MainWnd_OnPrintClient(hwnd, (HDC)wParam);
  1300.             break;
  1301.         // Don't let the window get too small when the title and
  1302.         // menu bars are ON
  1303.         case WM_GETMINMAXINFO:
  1304.         {
  1305.             if (FALSE == g_Options.m_fNoTitle)
  1306.             {
  1307.                 LPMINMAXINFO lpmmi   = (LPMINMAXINFO) lParam;
  1308.                 lpmmi->ptMinTrackSize.x = g_minWidth;
  1309.                 lpmmi->ptMinTrackSize.y = g_minHeight;
  1310.                 return FALSE;
  1311.             }
  1312.             break;
  1313.         }
  1314.                 
  1315.         // Handle notifications from out tray icon
  1316.         case PWM_TRAYICON:
  1317.         {
  1318.             Tray_Notify(hwnd, wParam, lParam);
  1319.             break;
  1320.         }
  1321.         // Someone externally is asking us to wake up and be shown
  1322.         case PWM_ACTIVATE:
  1323.         {
  1324.              ShowRunningInstance();            
  1325.              // Return PWM_ACTIVATE to the caller as just a little
  1326.              // more assurance that we really did handle this
  1327.              // message correctly.
  1328.              
  1329.              SetWindowLongPtr(hwnd, DWLP_MSGRESULT, PWM_ACTIVATE);
  1330.              return TRUE;
  1331.         }
  1332.         case WM_INITMENU:
  1333.         {
  1334.             // Don't let the right button hide the window during
  1335.             // menu operations
  1336.             g_fCantHide = TRUE;
  1337.             break;
  1338.         }
  1339.         // If we're in always-on-top mode, the right mouse button will
  1340.         // temporarily hide us so that the user can see whats under us
  1341.             
  1342.         case WM_RBUTTONDOWN:
  1343.         case WM_NCRBUTTONDOWN:
  1344.         {
  1345.             #ifdef ENABLE_HIDING
  1346.                 if (!g_fInPopup && !fIsHidden && !g_fCantHide && g_Options.m_fAlwaysOnTop)
  1347.                 {
  1348.                     ShowWindow (hwnd, SW_HIDE);
  1349.                     // We take the capture so that we're guaranteed to get
  1350.                     // the right button up even if its off our window 
  1351.                     SetCapture (hwnd);
  1352.                     fIsHidden = TRUE;
  1353.                 }
  1354.             #endif
  1355.             break;            
  1356.         }
  1357.         // On right button up, if we were hidden, unhide us.  We have
  1358.         // to use the correct state (min/max/restore)
  1359.         case WM_RBUTTONUP:
  1360.         case WM_NCRBUTTONUP:
  1361.         {
  1362.             #ifdef ENABLE_HIDING
  1363.                 if (fIsHidden)
  1364.                 {
  1365.                     ReleaseCapture();
  1366.                     if (IsIconic(hwnd))
  1367.                         ShowWindow (hwnd, SW_SHOWMINNOACTIVE);
  1368.                     else if (IsZoomed (hwnd))
  1369.                     {
  1370.                         ShowWindow (hwnd, SW_SHOWMAXIMIZED);
  1371.                         SetForegroundWindow (hwnd);
  1372.                     }
  1373.                     else
  1374.                     {
  1375.                         ShowWindow (hwnd, SW_SHOWNOACTIVATE);
  1376.                         SetForegroundWindow (hwnd);
  1377.                     }
  1378.                     fIsHidden = FALSE;
  1379.                 }
  1380.             #endif
  1381.             break;
  1382.         }
  1383.         case WM_NCHITTEST:
  1384.         {
  1385.             // If we have no title/menu bar, clicking and dragging the client
  1386.             // area moves the window. To do this, return HTCAPTION.
  1387.             // Note dragging not allowed if window maximized, or if caption
  1388.             // bar is present.
  1389.             //
  1390.             wParam = DefWindowProc(hwnd, uMsg, wParam, lParam);
  1391.             if (g_Options.m_fNoTitle && (wParam == HTCLIENT) && !IsZoomed(g_hMainWnd))
  1392.             {
  1393.                 SetWindowLongPtr(hwnd, DWLP_MSGRESULT, HTCAPTION);
  1394.                 return TRUE;
  1395.             }
  1396.             else
  1397.             {
  1398.                 return FALSE;       // Not handled
  1399.             }
  1400.         }
  1401.         case WM_NCLBUTTONDBLCLK:
  1402.         {
  1403.             // If we have no title, an NC dbl click means we should turn
  1404.             // them back on
  1405.             if (FALSE == g_Options.m_fNoTitle)
  1406.             {
  1407.                 break;
  1408.             }
  1409.             // Else, fall though
  1410.         }
  1411.         case WM_LBUTTONDBLCLK:
  1412.         {
  1413.             g_Options.m_fNoTitle = ~g_Options.m_fNoTitle;
  1414.             RECT rcMainWnd;
  1415.             GetWindowRect(g_hMainWnd, &rcMainWnd);
  1416.             if (g_pPages[PERF_PAGE])
  1417.             {
  1418.                 ((CPerfPage *)(g_pPages[PERF_PAGE]))->UpdateGraphs();
  1419.                 g_pPages[PERF_PAGE]->TimerEvent();
  1420.             }
  1421.             // Force a WM_SIZE event so that the window checks the min size
  1422.             // when coming out of notitle mode
  1423.             MoveWindow(g_hMainWnd, 
  1424.                        rcMainWnd.left, 
  1425.                        rcMainWnd.top, 
  1426.                        rcMainWnd.right - rcMainWnd.left,
  1427.                        rcMainWnd.bottom - rcMainWnd.top,
  1428.                        TRUE);
  1429.             SizeChildPage(hwnd);
  1430.             break;
  1431.         }
  1432.         // Someone (the task page) wants us to look up a process in the 
  1433.         // process view.  Switch to that page and send it the FINDPROC
  1434.         // message
  1435.         case WM_FINDPROC:
  1436.         {
  1437.             if (-1 != TabCtrl_SetCurSel(GetDlgItem(hwnd, IDC_TABS), PROC_PAGE))
  1438.             {
  1439.                 // SetCurSel doesn't do the page change (that would make too much
  1440.                 // sense), so we have to fake up a TCN_SELCHANGE notification
  1441.                 NMHDR nmhdr;
  1442.                 nmhdr.hwndFrom = GetDlgItem(hwnd, IDC_TABS);
  1443.                 nmhdr.idFrom   = IDC_TABS;
  1444.                 nmhdr.code     = TCN_SELCHANGE;
  1445.                 if (MainWnd_OnTabCtrlNotify(&nmhdr))
  1446.                 {
  1447.                     SendMessage(g_pPages[g_Options.m_iCurrentPage]->GetPageWindow(), 
  1448.                                     WM_FINDPROC, wParam, lParam);
  1449.                 }
  1450.             }
  1451.             else
  1452.             {
  1453.                 MessageBeep(0);
  1454.             }
  1455.             break;
  1456.         }
  1457.         case WM_NOTIFY:
  1458.         {
  1459.             switch(wParam)
  1460.             {
  1461.                 case IDC_TABS:
  1462.                     return MainWnd_OnTabCtrlNotify((LPNMHDR) lParam);
  1463.                 default:
  1464.                     break;
  1465.             }
  1466.             break;
  1467.         }
  1468.         
  1469.         case WM_ENDSESSION:
  1470.         {
  1471.             if (wParam)
  1472.             {
  1473.                 MyDestroyWindow(g_hMainWnd);
  1474.             }
  1475.             break;
  1476.         }
  1477.         case WM_CLOSE:
  1478.         {
  1479.             MyDestroyWindow(g_hMainWnd);
  1480.             break;
  1481.         }
  1482.         case WM_NCDESTROY:
  1483.         {
  1484.             // Remove the tray icon
  1485.             CTrayNotification * pNot = new CTrayNotification(hwnd, 
  1486.                                                              PWM_TRAYICON, 
  1487.                                                              NIM_DELETE, 
  1488.                                                              NULL, 
  1489.                                                              NULL);
  1490.             if (pNot)
  1491.             {
  1492.                 if (FALSE == DeliverTrayNotification(pNot))
  1493.                 {
  1494.                     delete pNot;
  1495.                 }
  1496.             }
  1497.             // If there's a tray thread, tell is to exit
  1498.             EnterCriticalSection(&g_CSTrayThread);
  1499.             if (g_idTrayThread)
  1500.             {
  1501.                 PostThreadMessage(g_idTrayThread, PM_QUITTRAYTHREAD, 0, 0);
  1502.             }
  1503.             LeaveCriticalSection(&g_CSTrayThread);
  1504.             // Wait around for some period of time for the tray thread to
  1505.             // do its cleanup work.  If the wait times out, worst case we
  1506.             // orphan the tray icon.
  1507.             if (g_hTrayThread)
  1508.             {
  1509.                 #define TRAY_THREAD_WAIT 3000
  1510.                 WaitForSingleObject(g_hTrayThread, TRAY_THREAD_WAIT);
  1511.                 CloseHandle(g_hTrayThread);
  1512.             }
  1513.             break;
  1514.         }
  1515.         case WM_SYSCOLORCHANGE:
  1516.         case WM_SETTINGCHANGE:
  1517.         {
  1518.             // pass these to the status bar
  1519.             SendMessage(g_hStatusWnd, uMsg, wParam, lParam);
  1520.             
  1521.             // also pass along to the pages
  1522.             for (int i = 0; i < ARRAYSIZE(g_pPages); i++)
  1523.             {
  1524.                 if (g_pPages[i])
  1525.                 {
  1526.                     SendMessage(g_pPages[i]->GetPageWindow(), uMsg, wParam, lParam);
  1527.                 }
  1528.             }   
  1529.             if (uMsg == WM_SETTINGCHANGE)
  1530.             {
  1531.                 // force a resizing of the main dialog
  1532.                 RECT rcMainClient;
  1533.                 GetClientRect(g_hMainWnd, &rcMainClient);
  1534.                 MainWnd_OnSize(g_hMainWnd, 0, rcMainClient.right - rcMainClient.left, rcMainClient.bottom - rcMainClient.top);
  1535.             }
  1536.             
  1537.             break; 
  1538.         }
  1539.         case WM_DESTROY:
  1540.         {       
  1541.             // Before shutting down, deactivate the current page, then 
  1542.             // destroy all pages
  1543.             if (g_Options.m_iCurrentPage && g_pPages[g_Options.m_iCurrentPage])
  1544.             {
  1545.                 g_pPages[g_Options.m_iCurrentPage]->Deactivate();
  1546.             }
  1547.             for (int i = 0; i < ARRAYSIZE(g_pPages); i++)
  1548.             {
  1549.                 if (g_pPages[i])
  1550.                 {
  1551.                     g_pPages[i]->Destroy();
  1552.                 }
  1553.             }
  1554.             // Save the current options
  1555.             g_Options.Save();
  1556.             PostQuitMessage(0);
  1557.         }
  1558.         default:
  1559.             
  1560.             return FALSE;       // Not handled here
  1561.     }
  1562.     return FALSE;
  1563. }
  1564. /*++ LoadGlobalResources
  1565. Routine Description:
  1566.     Loads those resources that are used frequently or that are expensive to
  1567.     load a single time at program startup
  1568. Return Value:
  1569.     BOOLEAN success value
  1570. Revision History:
  1571.       Nov-30-95 Davepl  Created
  1572. --*/
  1573. static const struct
  1574. {
  1575.     LPTSTR      psz;
  1576.     size_t      len;
  1577.     UINT        id;
  1578. }
  1579. g_aStrings[] =
  1580. {
  1581.     { g_szK,          ARRAYSIZE(g_szK),          IDS_K          },
  1582.     { g_szRealtime,   ARRAYSIZE(g_szRealtime),   IDS_REALTIME   },
  1583.     { g_szNormal,     ARRAYSIZE(g_szNormal),     IDS_NORMAL     },
  1584.     { g_szLow,        ARRAYSIZE(g_szLow),        IDS_LOW        },
  1585.     { g_szHigh,       ARRAYSIZE(g_szHigh),       IDS_HIGH       },
  1586.     { g_szUnknown,    ARRAYSIZE(g_szUnknown),    IDS_UNKNOWN    },
  1587.     { g_szAboveNormal,ARRAYSIZE(g_szAboveNormal),IDS_ABOVENORMAL},
  1588.     { g_szBelowNormal,ARRAYSIZE(g_szBelowNormal),IDS_BELOWNORMAL},
  1589.     { g_szRunning,    ARRAYSIZE(g_szRunning),    IDS_RUNNING    },
  1590.     { g_szHung,       ARRAYSIZE(g_szHung),       IDS_HUNG       },
  1591.     { g_szfmtTasks,   ARRAYSIZE(g_szfmtTasks),   IDS_FMTTASKS   },
  1592.     { g_szfmtProcs,   ARRAYSIZE(g_szfmtProcs),   IDS_FMTPROCS   },
  1593.     { g_szfmtCPU,     ARRAYSIZE(g_szfmtCPU),     IDS_FMTCPU     },
  1594.     { g_szfmtMEM,     ARRAYSIZE(g_szfmtMEM),     IDS_FMTMEM     },
  1595.     { g_szfmtCPUNum,  ARRAYSIZE(g_szfmtCPUNum),  IDS_FMTCPUNUM  },
  1596.     { g_szTotalCPU,   ARRAYSIZE(g_szTotalCPU),   IDS_TOTALTIME  },
  1597.     { g_szKernelCPU,  ARRAYSIZE(g_szKernelCPU),  IDS_KERNELTIME },
  1598.     { g_szMemUsage,   ARRAYSIZE(g_szMemUsage),   IDS_MEMUSAGE   },
  1599. };
  1600. static const UINT idTrayIcons[] =
  1601. {
  1602.     IDI_TRAY0, IDI_TRAY1, IDI_TRAY2, IDI_TRAY3, IDI_TRAY4, IDI_TRAY5,
  1603.     IDI_TRAY6, IDI_TRAY7, IDI_TRAY8, IDI_TRAY9, IDI_TRAY10, IDI_TRAY11
  1604. };
  1605. HICON g_aTrayIcons[ARRAYSIZE(idTrayIcons)];
  1606. UINT  g_cTrayIcons = ARRAYSIZE(idTrayIcons);
  1607. BOOL LoadGlobalResources()
  1608. {
  1609.     // If we don't get accelerators, its not worth failing the load
  1610.     
  1611.     g_hAccel = (HACCEL) LoadAccelerators(g_hInstance, MAKEINTRESOURCE(IDR_ACCELERATORS));
  1612.     Assert(g_hAccel);
  1613.     for (UINT i = 0; i < g_cTrayIcons; i++)
  1614.     {
  1615.         VERIFY( g_aTrayIcons[i] = (HICON) LoadImage(g_hInstance, 
  1616.                                                     MAKEINTRESOURCE(idTrayIcons[i]), 
  1617.                                                     IMAGE_ICON, 
  1618.                                                     0, 0, 
  1619.                                                     LR_DEFAULTCOLOR) );
  1620.     }
  1621.     for (i = 0; i < ARRAYSIZE(g_aStrings); i++)
  1622.     {
  1623.         if (FALSE == LoadString(g_hInstance, 
  1624.                                 g_aStrings[i].id, 
  1625.                                 g_aStrings[i].psz,
  1626.                                 g_aStrings[i].len))
  1627.         {
  1628.             return FALSE;
  1629.         }
  1630.     }
  1631.     g_hbmpBack = (HBITMAP) LoadImage(g_hInstance,
  1632.                                      MAKEINTRESOURCE(IDB_BMPBACK),
  1633.                                      IMAGE_BITMAP,
  1634.                                      0, 0,
  1635.                                      LR_LOADMAP3DCOLORS);
  1636.     if (NULL == g_hbmpBack)
  1637.     {
  1638.         return FALSE;
  1639.     }
  1640.     g_hbmpForward = (HBITMAP) LoadImage(g_hInstance,
  1641.                                      MAKEINTRESOURCE(IDB_BMPFORWARD),
  1642.                                      IMAGE_BITMAP,
  1643.                                      0, 0,
  1644.                                      LR_LOADMAP3DCOLORS);
  1645.     if (NULL == g_hbmpForward)
  1646.     {
  1647.         DeleteObject(g_hbmpBack);
  1648.         g_hbmpBack = NULL;
  1649.         return FALSE;
  1650.     }
  1651.     
  1652.     return TRUE;
  1653. }
  1654. /*++ WinMain
  1655. Routine Description:
  1656.     Windows app startup.  Does basic initialization and creates the main window
  1657. Arguments:
  1658.     Standard winmain
  1659. Return Value:
  1660.     App exit code
  1661. Revision History:
  1662.       Nov-30-95 Davepl  Created
  1663. --*/
  1664. int WINAPI WinMainT(
  1665.                 HINSTANCE   hInstance,          // handle to current instance
  1666.                 HINSTANCE   hPrevInstance,          // handle to previous instance (n/a)
  1667.                 LPTSTR      lpCmdLine,          // pointer to command line
  1668.                 int         nShowCmd            // show state of window
  1669.                 )
  1670. {
  1671.     g_hInstance   = hInstance;
  1672.     int retval    = TRUE;
  1673.     HKEY hKeyPolicy;
  1674.     DWORD dwType, dwData = 0, dwSize;
  1675.     int cx, cy;
  1676.     g_msgTaskbarCreated = RegisterWindowMessage(TEXT("TaskbarCreated"));
  1677.     InitializeCriticalSection(&g_CSTrayThread);
  1678.     // Try to create or grab the startup mutex.  Only in the case
  1679.     // where everything goes well and the mutex already existed AND
  1680.     // we were able to grab it do we deem ourselves to be a secondary instance
  1681.     g_hStartupMutex = CreateMutex(NULL, TRUE, cszStartupMutex);
  1682.     if (g_hStartupMutex && GetLastError() == ERROR_ALREADY_EXISTS)
  1683.     {
  1684.         // Give the other instance (the one that owns the startup mutex) 10
  1685.         // seconds to do its thing
  1686.         WaitForSingleObject(g_hStartupMutex, FINDME_TIMEOUT);
  1687.     }
  1688.         // Load Hydra's extensions
  1689.     HINSTANCE hWinstaDLL = LoadLibrary( TEXT( "winsta.dll" ) );
  1690.     if( hWinstaDLL != NULL)
  1691.     {
  1692.         gpfnWinStationGetProcessSid = ( pfnWinStationGetProcessSid )GetProcAddress(hWinstaDLL, "WinStationGetProcessSid");
  1693.         if( gpfnWinStationGetProcessSid == NULL )
  1694.         {
  1695.             dprintf(TEXT("GetProcAddress for WinStationGetProcessSid failed, Error %dn") , GetLastError() );
  1696.         }
  1697.         gpfnWinStationTerminateProcess = ( pfnWinStationTerminateProcess )GetProcAddress(hWinstaDLL, "WinStationTerminateProcess");
  1698.         if( gpfnWinStationTerminateProcess == NULL )
  1699.         {
  1700.             dprintf(TEXT("GetProcAddress for WinStationTerminateProcess failed, Error %dn") , GetLastError() );
  1701.         }
  1702.     }
  1703.     else
  1704.     {
  1705.         dprintf(TEXT("Cannot Load winsta.dll, Error %dn"), GetLastError() );
  1706.     }
  1707.     HINSTANCE hUtilDll = LoadLibrary( TEXT( "utildll.dll" ) ); 
  1708.     if( hUtilDll != NULL )
  1709.     {
  1710.         gpfnCachedGetUserFromSid = ( pfnCachedGetUserFromSid )GetProcAddress(hUtilDll, "CachedGetUserFromSid");
  1711.         if( gpfnCachedGetUserFromSid == NULL )
  1712.         {
  1713.             dprintf( TEXT( "GetProcAddress for CachedGetUserFromSid failed, Error %dn" ) , GetLastError( ) );
  1714.         }
  1715.     }
  1716.     else
  1717.     {
  1718.         dprintf( TEXT( "Cannot Load utildll.dll, Error %dn" ) , GetLastError( ) );
  1719.     }
  1720.     
  1721.     // 
  1722.     // Locate and activate a running instance if it exists.  
  1723.     //
  1724.     TCHAR szTitle[MAX_PATH];
  1725.     if (LoadString(hInstance, IDS_APPTITLE, szTitle, ARRAYSIZE(szTitle)))
  1726.     {
  1727.         HWND hwndOld = FindWindow(WC_DIALOG, szTitle);
  1728.         if (hwndOld)
  1729.         {
  1730.             // Send the other copy of ourselves a PWM_ACTIVATE message.  If that
  1731.             // succeeds, and it returns PWM_ACTIVATE back as the return code, it's
  1732.             // up and alive and we can exit this instance.
  1733.             DWORD dwPid = 0;
  1734.             GetWindowThreadProcessId(hwndOld, &dwPid);
  1735.             AllowSetForegroundWindow(dwPid);
  1736.             ULONG_PTR dwResult;
  1737.             if (SendMessageTimeout(hwndOld, 
  1738.                                    PWM_ACTIVATE, 
  1739.                                    0, 0, 
  1740.                                    SMTO_ABORTIFHUNG, 
  1741.                                    FINDME_TIMEOUT, 
  1742.                                    &dwResult))
  1743.             {
  1744.                 if (dwResult == PWM_ACTIVATE)
  1745.                 {
  1746.                     goto cleanup;
  1747.                 }
  1748.             }
  1749.         }
  1750.     }
  1751.     if (RegOpenKeyEx (HKEY_CURRENT_USER,
  1752.                       TEXT("Software\Microsoft\Windows\CurrentVersion\Policies\System"),
  1753.                       0, KEY_READ, &hKeyPolicy) == ERROR_SUCCESS)
  1754.     {
  1755.         dwSize = sizeof(dwData);
  1756.         RegQueryValueEx (hKeyPolicy, TEXT("DisableTaskMgr"), NULL,
  1757.                          &dwType, (LPBYTE) &dwData, &dwSize);
  1758.         RegCloseKey (hKeyPolicy);
  1759.         if (dwData)
  1760.         {
  1761.             TCHAR szTitle[25];
  1762.             TCHAR szMessage[200];
  1763.             LoadString (hInstance, IDS_TASKMGR, szTitle, ARRAYSIZE(szTitle));
  1764.             LoadString (hInstance, IDS_TASKMGRDISABLED , szMessage, ARRAYSIZE(szMessage));
  1765.             MessageBox (NULL, szMessage, szTitle, MB_OK | MB_ICONSTOP);
  1766.             retval = FALSE;
  1767.             goto cleanup;
  1768.         }
  1769.     }
  1770.     // No running instance found, so we run as normal
  1771.     InitCommonControls();
  1772.     InitDavesControls();
  1773.     // Start the worker thread.  If it fails, you just don't
  1774.     // get tray icons
  1775.     g_hTrayThread = CreateThread(NULL, 0, TrayThreadMessageLoop, NULL, 0, &g_idTrayThread);
  1776.     ASSERT(g_hTrayThread);
  1777.     // Init the page table
  1778.     g_pPages[0] = new CTaskPage;
  1779.     if (NULL == g_pPages[0])
  1780.     {
  1781.         retval = FALSE;
  1782.         goto cleanup;
  1783.     }
  1784.     g_pPages[1] = new CProcPage;
  1785.     if (NULL == g_pPages[1])
  1786.     {
  1787.         retval = FALSE;
  1788.         goto cleanup;
  1789.     }
  1790.     g_pPages[2] = new CPerfPage;
  1791.     if (NULL == g_pPages[2])
  1792.     {
  1793.         retval = FALSE;
  1794.         goto cleanup;
  1795.     }
  1796.     
  1797.     // Load whatever resources that we need available globally
  1798.     if (FALSE == LoadGlobalResources())
  1799.     {
  1800.         retval = FALSE;
  1801.         goto cleanup;
  1802.     }
  1803.     // Initialize the history buffers
  1804.     if (0 == InitPerfInfo())
  1805.     {
  1806.         retval = FALSE;
  1807.         goto cleanup;
  1808.     }
  1809.     // Create the main window (it's a modeless dialog, to be precise)
  1810.     g_hMainWnd = CreateDialog(hInstance,                  
  1811.                                  MAKEINTRESOURCE(IDD_MAINWND),  
  1812.                                  NULL,
  1813.                                  MainWindowProc);
  1814.     if (NULL == g_hMainWnd)
  1815.     {
  1816.         retval = FALSE;
  1817.         goto cleanup;
  1818.     }
  1819.     else
  1820.     {
  1821.         fAlreadySetPos = TRUE;
  1822.         cx = g_Options.m_rcWindow.right-g_Options.m_rcWindow.left;
  1823.         cy = g_Options.m_rcWindow.bottom-g_Options.m_rcWindow.top;
  1824.         SetWindowPos(g_hMainWnd, NULL, 
  1825.                          g_Options.m_rcWindow.left,
  1826.                          g_Options.m_rcWindow.top,
  1827.                          cx,
  1828.                          cy,
  1829.                          SWP_NOZORDER);
  1830.         MyShowWindow(g_hMainWnd, nShowCmd);
  1831.     }
  1832.     // We're out of the "starting up" phase so release the startup mutex
  1833.     if (g_hStartupMutex)
  1834.     {
  1835.         ReleaseMutex(g_hStartupMutex);
  1836.         CloseHandle(g_hStartupMutex);
  1837.         g_hStartupMutex = NULL;
  1838.     }
  1839.     // If we're the one, true, task manager, we can hang around till the
  1840.     // bitter end in case the user has problems during shutdown
  1841.     SetProcessShutdownParameters(1, SHUTDOWN_NORETRY);
  1842.     MSG msg;
  1843.     while (GetMessage(&msg, NULL, 0, 0))
  1844.     {
  1845.         // Give the page a crack at the accelerator
  1846.         
  1847.         HWND hwndPage = NULL;
  1848.         
  1849.         if (g_Options.m_iCurrentPage >= 0) 
  1850.         {
  1851.             g_pPages[g_Options.m_iCurrentPage]->GetPageWindow();
  1852.         }
  1853.         BOOL bHandled = FALSE;
  1854.         
  1855.         bHandled = TranslateAccelerator(g_hMainWnd, g_hAccel, &msg);
  1856.         if (FALSE == bHandled)
  1857.         {
  1858.             if (hwndPage)
  1859.             {
  1860.                 bHandled = TranslateAccelerator(hwndPage, g_hAccel, &msg);
  1861.             }
  1862.             if (FALSE == bHandled && FALSE == IsDialogMessage(g_hMainWnd, &msg))
  1863.             {
  1864.                 TranslateMessage(&msg);          // Translates virtual key codes 
  1865.                 DispatchMessage(&msg);           // Dispatches message to window 
  1866.             }
  1867.         }
  1868.     }
  1869. cleanup:
  1870.     // We're no longer "starting up"
  1871.     if (g_hStartupMutex)
  1872.     {
  1873.         ReleaseMutex(g_hStartupMutex);
  1874.         CloseHandle(g_hStartupMutex);
  1875.         g_hStartupMutex = NULL;
  1876.     }
  1877.     // Yes, I could use virtual destructors, but I could also poke
  1878.     // myself in the eye with a sharp stick.  Either way you wouldn't
  1879.     // be able to see what's going on.
  1880.     if (g_pPages[TASK_PAGE])
  1881.         delete (CTaskPage *) g_pPages[TASK_PAGE];
  1882.     if (g_pPages[PROC_PAGE])
  1883.         delete (CProcPage *) g_pPages[PROC_PAGE];
  1884.     if (g_pPages[PERF_PAGE])
  1885.         delete (CPerfPage *) g_pPages[PERF_PAGE];
  1886.     ReleasePerfInfo();
  1887.     if( hWinstaDLL != NULL )
  1888.     {
  1889.         FreeLibrary( hWinstaDLL );
  1890.     }
  1891.     if( hUtilDll != NULL )
  1892.     {
  1893.         FreeLibrary( hUtilDll );
  1894.     }
  1895.     return (retval);
  1896. }
  1897. //
  1898. // And now the magic begins.  The normal C++ CRT code walks a set of vectors
  1899. // and calls through them to perform global initializations.  Those vectors
  1900. // are always in data segments with a particular naming scheme.  By delcaring
  1901. // the variables below, I can determine where in my code they get stuck, and
  1902. // then call them myself
  1903. //
  1904. typedef void (__cdecl *_PVFV)(void);
  1905. #pragma data_seg(".CRT$XIA")
  1906. _PVFV __xi_a[] = { NULL };
  1907. #pragma data_seg(".CRT$XIZ")
  1908. _PVFV __xi_z[] = { NULL };
  1909. #pragma data_seg(".CRT$XCA")                                 
  1910. _PVFV __xc_a[] = { NULL };
  1911. #pragma data_seg(".CRT$XCZ")
  1912. _PVFV __xc_z[] = { NULL };
  1913. #pragma data_seg(".data")
  1914. /*++ _initterm
  1915. Routine Description:
  1916.     Walk the table of function pointers from the bottom up, until
  1917.     the end is encountered.  Do not skip the first entry.  The initial
  1918.     value of pfbegin points to the first valid entry.  Do not try to
  1919.     execute what pfend points to.  Only entries before pfend are valid.
  1920. Arguments:
  1921.     pfbegin - first pointer
  1922.     pfend   - last pointer
  1923. Revision History:
  1924.       Nov-30-95 Davepl  Created
  1925. --*/
  1926. static void __cdecl _initterm ( _PVFV * pfbegin, _PVFV * pfend )
  1927. {
  1928.         while ( pfbegin < pfend )
  1929.         {
  1930.             
  1931.              // if current table entry is non-NULL, call thru it.
  1932.              
  1933.             if ( *pfbegin != NULL )
  1934.             {
  1935.                 (**pfbegin)();
  1936.             }
  1937.             ++pfbegin;
  1938.         }
  1939. }
  1940. /*++ WinMain
  1941. Routine Description:
  1942.     Windows app startup.  Does basic initialization and creates the main window
  1943. Arguments:
  1944.     Standard winmain
  1945. Return Value:
  1946.     App exit code
  1947. Revision History:
  1948.       Nov-30-95 Davepl  Created
  1949. --*/
  1950. int _stdcall ModuleEntry(void)
  1951. {
  1952.     int i;
  1953.     STARTUPINFO si;
  1954.     SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
  1955.     //
  1956.     // Do runtime startup initializers.
  1957.     //
  1958.     _initterm( __xi_a, __xi_z );
  1959.     
  1960.     //
  1961.     // do C++ constructors (initializers) specific to this EXE
  1962.     //
  1963.     _initterm( __xc_a, __xc_z );
  1964.     LPTSTR pszCmdLine = GetCommandLine();
  1965.     if ( *pszCmdLine == TEXT('"') ) {
  1966.         /*
  1967.          * Scan, and skip over, subsequent characters until
  1968.          * another double-quote or a null is encountered.
  1969.          */
  1970.         while ( *++pszCmdLine && (*pszCmdLine
  1971.              != TEXT('"')) );
  1972.         /*
  1973.          * If we stopped on a double-quote (usual case), skip
  1974.          * over it.
  1975.          */
  1976.         if ( *pszCmdLine == TEXT('"') )
  1977.             pszCmdLine++;
  1978.     }
  1979.     else {
  1980.         while (*pszCmdLine > TEXT(' '))
  1981.             pszCmdLine++;
  1982.     }
  1983.     /*
  1984.      * Skip past any white space preceeding the second token.
  1985.      */
  1986.     while (*pszCmdLine && (*pszCmdLine <= TEXT(' '))) {
  1987.         pszCmdLine++;
  1988.     }
  1989.     si.dwFlags = 0;
  1990.     GetStartupInfo(&si);
  1991.     g_bMirroredOS = IS_MIRRORING_ENABLED();
  1992.     
  1993.     i = WinMainT(GetModuleHandle(NULL), NULL, pszCmdLine,
  1994.                    si.dwFlags & STARTF_USESHOWWINDOW ? si.wShowWindow : SW_SHOWDEFAULT);
  1995.     ExitProcess(i);
  1996.     return i;   // We never comes here.
  1997. }
  1998. // DisplayFailureMsg
  1999. //
  2000. // Displays a generic error message based on the error code
  2001. // and message box title provided
  2002. void DisplayFailureMsg(HWND hWnd, UINT idTitle, DWORD dwError)
  2003. {
  2004.     TCHAR szTitle[MAX_PATH];
  2005.     TCHAR szMsg[MAX_PATH * 2];
  2006.     TCHAR szError[MAX_PATH];
  2007.     if (0 == LoadString(g_hInstance, idTitle, szTitle, ARRAYSIZE(szTitle)))
  2008.     {
  2009.         return;
  2010.     }
  2011.     if (0 == LoadString(g_hInstance, IDS_GENFAILURE, szMsg, ARRAYSIZE(szMsg)))
  2012.     {
  2013.         return;
  2014.     }
  2015.     
  2016.     // "incorrect paramter" doesn't make a lot of sense for the user, so
  2017.     // massage it to be "Operation not allowed on this process".
  2018.                                                                              
  2019.     if (dwError == ERROR_INVALID_PARAMETER)
  2020.     {
  2021.         if (0 == LoadString(g_hInstance, IDS_BADPROC, szError, ARRAYSIZE(szError)))
  2022.         {
  2023.             return;
  2024.         }
  2025.     }
  2026.     else if (0 == FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
  2027.                                 NULL,
  2028.                                 dwError,
  2029.                                 LANG_USER_DEFAULT,
  2030.                                 szError,
  2031.                                 ARRAYSIZE(szError),
  2032.                                 NULL))
  2033.     {
  2034.         return;
  2035.     }
  2036.     lstrcat(szMsg, szError);
  2037.     MessageBox(hWnd, szMsg, szTitle, MB_OK | MB_ICONERROR);
  2038. }
  2039. /*++ NewDeskDlgProc
  2040. Routine Description:
  2041.     Dialog proc for the NewDesktop dialog
  2042.     
  2043. Arguments:
  2044. Return Value:
  2045.     BOOL, TRUE == success
  2046. Revision History:
  2047.       Nov-29-95 Davepl  Created
  2048. --*/
  2049. typedef struct _NewDesktopInfo
  2050. {
  2051.     TCHAR   m_szName[MAX_PATH];
  2052.     BOOL    m_fStartExplorer;
  2053. } NewDesktopInfo;
  2054. BOOL CALLBACK NewDeskDlgProc(HWND  hwndDlg, UINT  uMsg, WPARAM  wParam, LPARAM lParam)
  2055. {
  2056.     NewDesktopInfo * pndi = (NewDesktopInfo *) GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
  2057.     switch (uMsg)
  2058.     {
  2059.         case WM_INITDIALOG:
  2060.         {
  2061.             pndi = (NewDesktopInfo *) lParam;
  2062.             SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam);
  2063.       
  2064.             CheckDlgButton(hwndDlg, IDC_STARTEXPLORER, pndi->m_fStartExplorer);
  2065.             SetWindowText(GetDlgItem(hwndDlg, IDC_DESKTOPNAME), pndi->m_szName);
  2066.             
  2067.             return TRUE;
  2068.         }
  2069.         case WM_COMMAND:
  2070.         {
  2071.             switch(LOWORD(wParam))
  2072.             {
  2073.                 case IDCANCEL:
  2074.                 {
  2075.                     EndDialog(hwndDlg, IDCANCEL);
  2076.                     break;
  2077.                 }
  2078.                 case IDOK:
  2079.                 {
  2080.                     pndi->m_fStartExplorer = IsDlgButtonChecked(hwndDlg, IDC_STARTEXPLORER);
  2081.                     GetWindowText(GetDlgItem(hwndDlg, IDC_DESKTOPNAME), pndi->m_szName, ARRAYSIZE(pndi->m_szName));
  2082.                     EndDialog(hwndDlg, IDOK);
  2083.                     break;
  2084.                 }
  2085.                 default:
  2086.                     break;
  2087.             }
  2088.         }
  2089.             
  2090.         default:
  2091.         {
  2092.             return FALSE;
  2093.         }
  2094.     }
  2095. }
  2096. #ifdef DESKTOP_SUPPORT
  2097. /*++ CreateNewDesktop
  2098. Routine Description:
  2099.     Prompts the user for options and starts a new desktop
  2100.     
  2101. Arguments:
  2102. Return Value:
  2103.     BOOL, TRUE == success
  2104. Revision History:
  2105.       Nov-29-95 Davepl  Created
  2106. --*/
  2107. BOOL CreateNewDesktop()
  2108. {
  2109.     NewDesktopInfo ndi;
  2110.     ndi.m_fStartExplorer = TRUE;
  2111. TryAgain:
  2112.     //
  2113.     // Look for a desktop name that's not in use yet
  2114.     //
  2115.     for (int i = 0; ; i++)
  2116.     {
  2117.         wsprintf( ndi.m_szName, TEXT("Desktop%d"), i );
  2118.         HDESK hdesk = OpenDesktop( ndi.m_szName, 0, FALSE, DESKTOP_SWITCHDESKTOP );
  2119.         if (hdesk)
  2120.         {
  2121.             CloseDesktop(hdesk);
  2122.         }
  2123.         else
  2124.         {
  2125.             break;
  2126.         }
  2127.     }
  2128.     //
  2129.     // Allow the user to modify the options
  2130.     //
  2131.     int iRet = DialogBoxParam(g_hInstance, 
  2132.                               MAKEINTRESOURCE(IDD_CREATEDESKTOP), 
  2133.                               g_hMainWnd, 
  2134.                               NewDeskDlgProc,
  2135.                               (LPARAM) &ndi);
  2136.     if (iRet != IDOK)
  2137.     {
  2138.         return FALSE;
  2139.     }
  2140.     // 
  2141.     // Create the new desktop
  2142.     //
  2143.     HDESK hdesk = CreateDesktop( ndi.m_szName, NULL, NULL, 0, MAXIMUM_ALLOWED, NULL );
  2144.     if (NULL == hdesk)
  2145.     {
  2146.         DWORD dwError = GetLastError();
  2147.         DisplayFailureMsg(g_hMainWnd, IDS_CANTCREATEDESKTOP, dwError);
  2148.         goto TryAgain;
  2149.     }
  2150.     
  2151.     return S_OK;
  2152. }
  2153. #endif //DESKTOP_SUPPORT
  2154. /*++ LoadPopupMenu
  2155. Routine Description:
  2156.     Loads a popup menu from a resource.  Needed because USER
  2157.     does not support popup menus (yes, really)
  2158.     
  2159. Arguments:
  2160.     hinst       - module instance to look for resource in
  2161.     id          - resource id of popup menu
  2162. Return Value:
  2163. Revision History:
  2164.       Nov-22-95 Davepl  Created
  2165. --*/
  2166. HMENU LoadPopupMenu(HINSTANCE hinst, UINT id)
  2167. {
  2168.     HMENU hmenuParent = LoadMenu(hinst, MAKEINTRESOURCE(id));
  2169.     if (hmenuParent) 
  2170.     {
  2171.         HMENU hpopup = GetSubMenu(hmenuParent, 0);
  2172.         RemoveMenu(hmenuParent, 0, MF_BYPOSITION);
  2173.         DestroyMenu(hmenuParent);
  2174.         return hpopup;
  2175.     }
  2176.     return NULL;
  2177. }
  2178. /*++ SubclassListView, ListViewWndProc, LV_GetViewRect
  2179. Routine Description:
  2180.     Routines for flicker-free painting of listview controls.
  2181.     
  2182. Revision History:
  2183.       Dec-23-96 vadimg  Created
  2184. --*/
  2185. typedef LRESULT (CALLBACK *ListView)(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
  2186. ListView gfnListView;
  2187. void inline _SetRectRgnIndirect(HRGN hrgn, LPRECT prc)
  2188. {
  2189.     SetRectRgn(hrgn, prc->left, prc->top, prc->right, prc->bottom);
  2190. }
  2191. void LV_GetViewRgn(HWND hwnd)
  2192. {
  2193.     RECT rcItem;
  2194.     int nCount, i;
  2195.     
  2196.     SetRectRgn(g_hrgnView, 0, 0, 0, 0);
  2197.     nCount = ListView_GetItemCount(hwnd);
  2198.     for (i = 0; i < nCount; i++) {
  2199.         // exclude the selected item, listview does not erase it
  2200.         if (ListView_GetItemState(hwnd, i, LVIS_SELECTED))
  2201.             continue;
  2202.         ListView_GetItemRect(hwnd, i, &rcItem, LVIR_BOUNDS);
  2203.         
  2204.         // listview leaves off SM_CXEDGE when painting first column
  2205.         rcItem.left += g_cxEdge;
  2206.         _SetRectRgnIndirect(g_hrgnClip, &rcItem);
  2207.         CombineRgn(g_hrgnView, g_hrgnView, g_hrgnClip, RGN_OR);
  2208.     }
  2209. }
  2210. LRESULT CALLBACK ListViewWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  2211. {
  2212.     switch (msg) {
  2213.     case WM_SYSCOLORCHANGE:
  2214.         if (g_hbrWindow)
  2215.             DeleteObject(g_hbrWindow);
  2216.         g_hbrWindow = CreateSolidBrush(GetSysColor(COLOR_WINDOW));
  2217.         InvalidateRect(hwnd, NULL, TRUE);
  2218.         break;
  2219.     case WM_ERASEBKGND:
  2220.         {
  2221.             // To achieve the flicker-free effect only erase the area not
  2222.             // taken up by the items. The items will be filled by listview.
  2223.             RECT rcAll;
  2224.             HDC hdc = (HDC)wParam;
  2225.             
  2226.             LV_GetViewRgn(hwnd);
  2227.             
  2228.             GetClientRect(hwnd, &rcAll);
  2229.             _SetRectRgnIndirect(g_hrgnClip, &rcAll);
  2230.             
  2231.             CombineRgn(g_hrgnClip, g_hrgnClip, g_hrgnView, RGN_DIFF);
  2232.             FillRgn(hdc, g_hrgnClip, g_hbrWindow);
  2233.         }
  2234.         return TRUE;
  2235.     }
  2236.     
  2237.     return CallWindowProc(gfnListView, hwnd, msg, wParam, lParam);
  2238. }
  2239. void SubclassListView(HWND hwnd)
  2240. {
  2241.     gfnListView = (ListView)SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)ListViewWndProc);
  2242. }