Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2561 lines
54 KiB

//+-------------------------------------------------------------------------
//
// TaskMan - NT TaskManager
// Copyright (C) Microsoft
//
// File: Main.CPP
//
// History: Nov-10-95 DavePl Created
//
//--------------------------------------------------------------------------
#include "precomp.h"
//
// Control IDs
//
#define IDC_STATUSWND 100
//
// Globals - this app is (effectively) single threaded and these values
// are used by all pages
//
const TCHAR cszStartupMutex[] = TEXT("NTShell Taskman Startup Mutex");
#define FINDME_TIMEOUT 10000 // Wait to to 10 seconds for a response
void MainWnd_OnSize(HWND hwnd, UINT state, int cx, int cy);
HANDLE g_hStartupMutex = NULL;
BOOL g_fMenuTracking = FALSE;
HWND g_hMainWnd = NULL;
HDESK g_hMainDesktop = NULL;
HWND g_hStatusWnd = NULL;
HINSTANCE g_hInstance = NULL;
HACCEL g_hAccel = NULL;
BYTE g_cProcessors = (BYTE) 0;
HBITMAP g_hbmpBack = NULL;
HBITMAP g_hbmpForward = NULL;
HMENU g_hMenu = NULL;
BOOL g_fCantHide = FALSE;
BOOL g_fInPopup = FALSE;
DWORD g_idTrayThread = 0;
HANDLE g_hTrayThread = NULL;
LONG g_minWidth = 0;
LONG g_minHeight = 0;
LONG g_DefSpacing = 0;
LONG g_InnerSpacing = 0;
LONG g_TopSpacing = 0;
COptions g_Options;
static BOOL fAlreadySetPos = FALSE;
#define SHORTSTRLEN 32
//
// Global strings - short strings used too often to be LoadString'd
// every time
//
TCHAR g_szRealtime [SHORTSTRLEN];
TCHAR g_szNormal [SHORTSTRLEN];
TCHAR g_szHigh [SHORTSTRLEN];
TCHAR g_szLow [SHORTSTRLEN];
TCHAR g_szUnknown [SHORTSTRLEN];
TCHAR g_szHung [SHORTSTRLEN];
TCHAR g_szRunning [SHORTSTRLEN];
TCHAR g_szfmtTasks [SHORTSTRLEN];
TCHAR g_szfmtProcs [SHORTSTRLEN];
TCHAR g_szfmtCPU [SHORTSTRLEN];
TCHAR g_szfmtMEM [SHORTSTRLEN];
TCHAR g_szfmtCPUNum [SHORTSTRLEN];
TCHAR g_szTotalCPU [SHORTSTRLEN];
TCHAR g_szKernelCPU [SHORTSTRLEN];
TCHAR g_szMemUsage [SHORTSTRLEN];
TCHAR g_szK[10]; // Localized "K"ilobyte symbol
// Page Array
//
// Each of the page objects is delcared here, and g_pPages is an array
// of pointers to those instantiated objects (at global scope). The main
// window code can call through the base members of the CPage class to
// do things like sizing, etc., without worrying about whatever specific
// stuff each page might do
CPage * g_pPages[NUM_PAGES] = { NULL };
/*
Superclass of GROUPBOX
We need to turn on clipchildren for our dialog which contains the
history graphs, so they don't get erased during the repaint cycle.
Unfortunately, group boxes don't erase their backgrounds, so we
have to superclass them and provide a control that does.
This is a lot of extra crap, but the painting is several orders of
magnitude nicer with it...
*/
/*++ DavesFrameWndProc
Routine Description:
WndProc for the custom group box class. Primary difference from
standard group box is that this one knows how to erase its own
background, and doesn't rely on the parent to do it for it.
These controls also have CLIPSIBLINGS turn on so as not to stomp
on the ownderdraw graphs they surround.
Arguments:
standard wndproc fare
Revision History:
Nov-29-95 Davepl Created
--*/
WNDPROC oldButtonWndProc = NULL;
LONG FAR PASCAL DavesFrameWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (msg == WM_CREATE)
{
//
// Turn on clipsiblings for the frame
//
DWORD dwStyle = GetWindowLong(hWnd, GWL_STYLE);
dwStyle |= WS_CLIPSIBLINGS;
SetWindowLong(hWnd, GWL_STYLE, dwStyle);
}
else if (msg == WM_ERASEBKGND)
{
//
// Erase our background so that we don't get ugly in a dialog that has
// clip children on (as our parents will in this app)
//
HDC hdc = (HDC) wParam;
//
// If no dc is supplied by the caller, we create our own by building the
// update region and using GetDCEx to get a DC that corresponds to only
// the actual area that needs to be erased.
//
HRGN hRegion;
if (0 == wParam)
{
// Isn't Windows painting self-evident?
hRegion = CreateRectRgn(0, 0, 0, 0);
if (hRegion)
{
GetUpdateRgn(hWnd, hRegion, TRUE);
hdc = GetDCEx(hWnd, hRegion, DCX_CACHE | DCX_CLIPSIBLINGS | DCX_INTERSECTRGN);
}
}
RECT rcClient;
GetClientRect(hWnd, &rcClient);
FillRect(hdc, &rcClient, (HBRUSH)(COLOR_3DFACE + 1));
if (0 == wParam)
{
// If your compiler warns about hRegion not being initialized, it's lying
ReleaseDC(hWnd, hdc);
if (hRegion)
{
DeleteObject(hRegion);
}
}
return TRUE;
}
// For anything else, we defer to the standard button class code
return oldButtonWndProc(hWnd, msg, wParam, lParam);
}
/*++ COptions::Save
Routine Description:
Saves current options to the registy
Arguments:
Returns:
HRESULT
Revision History:
Jan-01-95 Davepl Created
--*/
const TCHAR szTaskmanKey[] = TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\TaskManager");
const TCHAR szOptionsKey[] = TEXT("Preferences");
HRESULT COptions::Save()
{
DWORD dwDisposition;
HKEY hkSave;
if (ERROR_SUCCESS != RegCreateKeyEx(HKEY_CURRENT_USER,
szTaskmanKey,
0,
TEXT("REG_BINARY"),
REG_OPTION_NON_VOLATILE,
KEY_WRITE,
NULL,
&hkSave,
&dwDisposition))
{
return GetLastHRESULT();
}
if (ERROR_SUCCESS != RegSetValueEx(hkSave,
szOptionsKey,
0,
REG_BINARY,
(LPBYTE) this,
sizeof(COptions)))
{
RegCloseKey(hkSave);
return GetLastHRESULT();
}
RegCloseKey(hkSave);
return S_OK;
}
/*++ COptions::Load
Routine Description:
Loads current options to the registy
Arguments:
Returns:
HRESULT
Revision History:
Jan-01-95 Davepl Created
--*/
HRESULT COptions::Load()
{
HKEY hkSave;
// If ctrl-alt-shift is down at startup, "forget" registry settings
if (GetKeyState(VK_SHIFT) < 0 &&
GetKeyState(VK_MENU) < 0 &&
GetKeyState(VK_CONTROL) < 0 )
{
SetDefaultValues();
return S_FALSE;
}
if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_CURRENT_USER,
szTaskmanKey,
0,
KEY_READ,
&hkSave))
{
return S_FALSE;
}
DWORD dwType;
DWORD dwSize = sizeof(COptions);
if (ERROR_SUCCESS != RegQueryValueEx(hkSave,
szOptionsKey,
0,
&dwType,
(LPBYTE) this,
&dwSize)
// Validate type and size of options info we got from the registry
|| dwType != REG_BINARY
|| dwSize != sizeof(COptions)
// Validate options, revert to default if any are invalid (like if
// the window would be offscreen)
|| m_rcWindow.left > GetSystemMetrics(SM_CXMAXIMIZED)
|| m_rcWindow.top > GetSystemMetrics(SM_CYMAXIMIZED)
|| m_rcWindow.right < 0
|| m_rcWindow.bottom < 0
|| m_iCurrentPage > NUM_PAGES - 1)
{
// Reset to default values
SetDefaultValues();
RegCloseKey(hkSave);
return S_FALSE;
}
RegCloseKey(hkSave);
return S_OK;
}
/*++ InitDavesControls
Routine Description:
Superclasses GroupBox for better drawing
Note that I'm not very concerned about failure here, since it
something goes wrong the dialog creation will fail awayway, and
it will be handled there
Arguments:
Revision History:
Nov-29-95 Davepl Created
--*/
void InitDavesControls()
{
static const TCHAR szControlName[] = TEXT("DavesFrameClass");
WNDCLASS wndclass;
//
// Get the class info for the Button class (which is what group
// boxes really are) and create a new class based on it
//
GetClassInfo(g_hInstance, TEXT("Button"), &wndclass);
oldButtonWndProc = wndclass.lpfnWndProc;
wndclass.hInstance = g_hInstance;
wndclass.lpfnWndProc = DavesFrameWndProc;
wndclass.lpszClassName = szControlName;
ATOM atom = RegisterClass(&wndclass);
return;
}
/*++ SetTitle
Routine Description:
Sets the app's title in the title bar (we do this on startup and
when coming out of notitle mode).
Arguments:
none
Return Value:
none
Revision History:
Jan-24-95 Davepl Created
--*/
void SetTitle()
{
TCHAR szTitle[MAX_PATH];
LoadString(g_hInstance, IDS_APPTITLE, szTitle, MAX_PATH);
SetWindowText(g_hMainWnd, szTitle);
}
/*++ UpdateMenuStates
Routine Description:
Updates the menu checks / ghosting based on the
current settings and options
Arguments:
Return Value:
Revision History:
Nov-29-95 Davepl Created
--*/
void UpdateMenuStates()
{
HMENU hMenu = GetMenu(g_hMainWnd);
if (hMenu)
{
CheckMenuRadioItem(hMenu, VM_FIRST, VM_LAST, VM_FIRST + (UINT) g_Options.m_vmViewMode, MF_BYCOMMAND);
CheckMenuRadioItem(hMenu, CM_FIRST, CM_LAST, CM_FIRST + (UINT) g_Options.m_cmHistMode, MF_BYCOMMAND);
CheckMenuRadioItem(hMenu, US_FIRST, US_LAST, US_FIRST + (UINT) g_Options.m_usUpdateSpeed, MF_BYCOMMAND);
}
// REVIEW (davepl) could be table driven
CheckMenuItem(hMenu, IDM_ALWAYSONTOP, MF_BYCOMMAND | (g_Options.m_fAlwaysOnTop ? MF_CHECKED : MF_UNCHECKED));
CheckMenuItem(hMenu, IDM_MINIMIZEONUSE, MF_BYCOMMAND | (g_Options.m_fMinimizeOnUse ? MF_CHECKED : MF_UNCHECKED));
CheckMenuItem(hMenu, IDM_CONFIRMATIONS, MF_BYCOMMAND | (g_Options.m_fConfirmations ? MF_CHECKED : MF_UNCHECKED));
CheckMenuItem(hMenu, IDM_KERNELTIMES, MF_BYCOMMAND | (g_Options.m_fKernelTimes ? MF_CHECKED : MF_UNCHECKED));
CheckMenuItem(hMenu, IDM_NOTITLE, MF_BYCOMMAND | (g_Options.m_fNoTitle ? MF_CHECKED : MF_UNCHECKED));
CheckMenuItem(hMenu, IDM_HIDEWHENMIN, MF_BYCOMMAND | (g_Options.m_fHideWhenMin ? MF_CHECKED : MF_UNCHECKED));
CheckMenuItem(hMenu, IDM_SHOW16BIT , MF_BYCOMMAND | (g_Options.m_fShow16Bit ? MF_CHECKED : MF_UNCHECKED));
// Remove the CPU history style options on single processor machines
if (g_cProcessors < 2)
{
DeleteMenu(hMenu, IDM_ALLCPUS, MF_BYCOMMAND);
}
}
/*++ SizeChildPage
Routine Description:
Size the active child page based on the tab control
Arguments:
hwndMain - Main window
Return Value:
Revision History:
Nov-29-95 Davepl Created
--*/
void SizeChildPage(HWND hwndMain)
{
if (g_Options.m_iCurrentPage >= 0)
{
// If we are in maximum viewing mode, the page gets the whole
// window area
HWND hwndPage = g_pPages[g_Options.m_iCurrentPage]->GetPageWindow();
DWORD dwStyle = GetWindowLong (g_hMainWnd, GWL_STYLE);
if (g_Options.m_fNoTitle)
{
RECT rcMainWnd;
GetClientRect(g_hMainWnd, &rcMainWnd);
SetWindowPos(hwndPage, HWND_TOP, rcMainWnd.left, rcMainWnd.top,
rcMainWnd.right - rcMainWnd.left,
rcMainWnd.bottom - rcMainWnd.top, SWP_NOZORDER | SWP_NOACTIVATE);
// remove caption & menu bar, etc.
dwStyle &= ~(WS_DLGFRAME | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX);
// SetWindowLong (g_hMainWnd, GWL_ID, 0);
SetWindowLong (g_hMainWnd, GWL_STYLE, dwStyle);
SetMenu(g_hMainWnd, NULL);
}
else
{
// If we have a page being displayed, we need to size it also
// put menu bar & caption back in
dwStyle = WS_TILEDWINDOW | dwStyle;
SetWindowLong (g_hMainWnd, GWL_STYLE, dwStyle);
if (g_hMenu)
{
SetMenu(g_hMainWnd, g_hMenu);
UpdateMenuStates();
}
SetTitle();
if (hwndPage)
{
RECT rcCtl;
HWND hwndCtl = GetDlgItem(hwndMain, IDC_TABS);
GetClientRect(hwndCtl, &rcCtl);
MapWindowPoints(hwndCtl, hwndMain, (LPPOINT)&rcCtl, 2);
TabCtrl_AdjustRect(hwndCtl, FALSE, &rcCtl);
SetWindowPos(hwndPage, HWND_TOP, rcCtl.left, rcCtl.top,
rcCtl.right - rcCtl.left, rcCtl.bottom - rcCtl.top, SWP_NOZORDER | SWP_NOACTIVATE);
}
}
}
}
/*++ UpdateStatusBar
Routine Description:
Draws the status bar with test based on data accumulated by all of
the various pages (basically a summary of most important info)
Arguments:
Return Value:
Revision History:
Nov-29-95 Davepl Created
--*/
void UpdateStatusBar()
{
//
// If we're in menu-tracking mode (sticking help text in the stat
// bar), we don't draw our standard text
//
if (FALSE == g_fMenuTracking)
{
TCHAR szText[MAX_PATH];
wsprintf(szText, g_szfmtProcs, g_cProcesses);
SendMessage(g_hStatusWnd, SB_SETTEXT, 0, (LPARAM) szText);
wsprintf(szText, g_szfmtCPU, g_CPUUsage);
SendMessage(g_hStatusWnd, SB_SETTEXT, 1, (LPARAM) szText);
wsprintf(szText, g_szfmtMEM, g_MEMUsage, g_MEMMax);
SendMessage(g_hStatusWnd, SB_SETTEXT, 2, (LPARAM) szText);
}
}
/*++ MainWnd_OnTimer
Routine Description:
Called when the refresh timer fires, we pass a timer event on to
each of the child pages.
Arguments:
hwnd - window timer was received at
id - id of timer that was received
Return Value:
Revision History:
Nov-30-95 Davepl Created
--*/
void MainWnd_OnTimer(HWND hwnd, UINT id)
{
if (GetForegroundWindow() == hwnd && GetAsyncKeyState(VK_CONTROL) < 0)
{
// CTRL alone means pause
return;
}
// Notify each of the pages in turn that they need to updatre
for (int i = 0; i < ARRAYSIZE(g_pPages); i++)
{
g_pPages[i]->TimerEvent();
}
// Update the tray icon
UINT iIconIndex = (g_CPUUsage * g_cTrayIcons) / 100;
if (iIconIndex >= g_cTrayIcons)
{
iIconIndex = g_cTrayIcons - 1; // Handle 100% case
}
TCHAR szTipText[MAX_PATH];
wsprintf(szTipText, g_szfmtCPU, g_CPUUsage);
CTrayNotification * pNot = new CTrayNotification(hwnd,
PWM_TRAYICON,
NIM_MODIFY,
g_aTrayIcons[iIconIndex],
szTipText);
if (pNot)
{
if (FALSE == DeliverTrayNotification(pNot))
{
delete pNot;
}
}
UpdateStatusBar();
}
/*++ MainWnd_OnInitDialog
Routine Description:
Processes WM_INITDIALOG for the main window (a modeless dialog)
Revision History:
Nov-29-95 Davepl Created
--*/
BOOL MainWnd_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam)
{
RECT rcMain;
GetWindowRect(hwnd, &rcMain);
g_minWidth = rcMain.right - rcMain.left;
g_minHeight = rcMain.bottom - rcMain.top;
g_DefSpacing = (DEFSPACING_BASE * LOWORD(GetDialogBaseUnits())) / DLG_SCALE_X;
g_InnerSpacing = (INNERSPACING_BASE * LOWORD(GetDialogBaseUnits())) / DLG_SCALE_X;
g_TopSpacing = (TOPSPACING_BASE * HIWORD(GetDialogBaseUnits())) / DLG_SCALE_Y;
// Load the user's defaults
g_Options.Load();
//
// On init, save away the window handle for all to see
//
g_hMainWnd = hwnd;
g_hMainDesktop = GetThreadDesktop(GetCurrentThreadId());
// If we're supposed to be TOPMOST, start out that way
if (g_Options.m_fAlwaysOnTop)
{
SetWindowPos(hwnd, HWND_TOPMOST, 0,0,0,0, SWP_NOMOVE | SWP_NOSIZE);
}
//
// Create the status window
//
g_hStatusWnd = CreateStatusWindow(WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE | SBARS_SIZEGRIP,
NULL,
hwnd,
IDC_STATUSWND);
if (NULL == g_hStatusWnd)
{
return FALSE;
}
//
// Base the panes in the status bar off of the LOGPIXELSX system metric
//
HDC hdc = GetDC(NULL);
INT nInch = GetDeviceCaps(hdc, LOGPIXELSX);
ReleaseDC(NULL, hdc);
int ciParts[] = { nInch,
ciParts[0] + (nInch * 5) / 4,
ciParts[1] + (nInch * 5) / 2,
-1};
if (g_hStatusWnd)
{
SendMessage(g_hStatusWnd, SB_SETPARTS, ARRAYSIZE(ciParts), (LPARAM)ciParts);
}
//
// Load our app icon
//
HICON hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_MAIN));
if (hIcon)
{
SendMessage(hwnd, WM_SETICON, TRUE, LPARAM(hIcon));
}
// Add our tray icon
CTrayNotification * pNot = new CTrayNotification(hwnd,
PWM_TRAYICON,
NIM_ADD,
g_aTrayIcons[0],
NULL);
if (pNot)
{
if (FALSE == DeliverTrayNotification(pNot))
{
delete pNot;
}
}
//
// Turn on TOPMOST for the status bar so it doesn't slide under the
// tab control
//
SetWindowPos(g_hStatusWnd,
HWND_TOPMOST,
0,0,0,0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOREDRAW);
//
// Intialize each of the the pages in turn
//
HWND hwndTabs = GetDlgItem(hwnd, IDC_TABS);
for (int i = 0; i < ARRAYSIZE(g_pPages); i++)
{
HRESULT hr;
hr = g_pPages[i]->Initialize(hwndTabs);
if (SUCCEEDED(hr))
{
//
// Get the title of the new page, and use it as the title of
// the page which we insert into the tab control
//
TCHAR szTitle[MAX_PATH];
g_pPages[i]->GetTitle(szTitle, ARRAYSIZE(szTitle));
TC_ITEM tcitem =
{
TCIF_TEXT, // value specifying which members to retrieve or set
NULL, // reserved; do not use
NULL, // reserved; do not use
szTitle, // pointer to string containing tab text
ARRAYSIZE(szTitle), // size of buffer pointed to by the pszText member
0, // index to tab control's image
NULL // application-defined data associated with tab
};
if (- 1 == TabCtrl_InsertItem(hwndTabs, i, &tcitem))
{
hr = E_FAIL;
}
}
//
// If a page failed to init, destroy the pages we have created up to this point
// We don't remove the tabs, on the assumption that the app is going away anyway
//
if (FAILED(hr))
{
for (int j = i; j >= 0; j--)
{
g_pPages[i]->Destroy();
}
return FALSE;
}
else
{
}
}
//
// Set the inital menu states
//
UpdateMenuStates();
//
// Activate a page (pick page 0 if no preference is set)
//
if (g_Options.m_iCurrentPage < 0)
{
g_Options.m_iCurrentPage = 0;
}
TabCtrl_SetCurSel(GetDlgItem(g_hMainWnd, IDC_TABS), g_Options.m_iCurrentPage);
g_pPages[g_Options.m_iCurrentPage]->Activate();
RECT rcMainClient;
GetClientRect(hwnd, &rcMainClient);
MainWnd_OnSize(g_hMainWnd, 0, rcMainClient.right - rcMainClient.left, rcMainClient.bottom - rcMainClient.top);
//
// Create the update timer
//
if (g_Options.m_dwTimerInterval) // 0 == paused
{
SetTimer(g_hMainWnd, 0, g_Options.m_dwTimerInterval, NULL);
}
// Force at least one intial update so that we don't need to wait
// for the first timed update to come through
MainWnd_OnTimer(g_hMainWnd, 0);
//
// Disable the MP-specific menu items
//
if (g_cProcessors <= 1)
{
HMENU hMenu = GetMenu(g_hMainWnd);
EnableMenuItem(hMenu, IDM_MULTIGRAPH, MF_BYCOMMAND | MF_GRAYED);
}
return TRUE;
}
/*++ MainWnd_OnPaint
Routine Description:
Just draws a thin edge just below the main menu bar
Arguments:
hwnd - Main window
Return Value:
Revision History:
Nov-29-95 Davepl Created
--*/
void MainWnd_OnPaint(HWND hwnd)
{
PAINTSTRUCT ps;
RECT rc;
//
// Don't waste our time if we're minimized
//
if (FALSE == IsIconic(hwnd))
{
BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rc);
//
// Draw an edge just below menu bar
//
DrawEdge(ps.hdc, &rc, EDGE_ETCHED, BF_TOP);
EndPaint(hwnd, &ps);
}
else
{
FORWARD_WM_PAINT(hwnd, DefWindowProc);
}
}
/*++ MainWnd_OnMenuSelect
Routine Description:
As the user browses menus in the app, writes help text to the
status bar. Also temporarily sets it to be a plain status bar
with no panes
Arguments:
Return Value:
Revision History:
Nov-29-95 Davepl Created
--*/
void MainWnd_OnMenuSelect(HWND hwnd, HMENU hmenu, int item, HMENU hmenuPopup, UINT flags)
{
//
// If menu is dismissed, restore the panes in the status bar, turn off the
// global "menu tracking" flag, and redraw the status bar with normal info
//
if ((0xFFFF == LOWORD(flags) && NULL == hmenu) || // dismissed the menu
(flags & (MF_SYSMENU | MF_SEPARATOR))) // sysmenu or separator
{
SendMessage(g_hStatusWnd, SB_SIMPLE, FALSE, 0L); // Restore sb panes
g_fMenuTracking = FALSE;
g_fCantHide = FALSE;
UpdateStatusBar();
return;
}
else
{
//
// If its a popup, go get the submenu item that is selected instead
//
if (flags & MF_POPUP)
{
MENUITEMINFO miiSubMenu;
miiSubMenu.cbSize = sizeof(MENUITEMINFO);
miiSubMenu.fMask = MIIM_ID;
miiSubMenu.cch = 0;
if (FALSE == GetMenuItemInfo(hmenu, item, TRUE, &miiSubMenu))
{
return;
}
//
// Change the parameters to simulate a "normal" menu item
//
item = miiSubMenu.wID;
flags &= ~MF_POPUP;
}
//
// Our menus always have the same IDs as the strings that describe
// their functions...
//
TCHAR szStatusText[MAX_PATH];
LoadString(g_hInstance, item, szStatusText, ARRAYSIZE(szStatusText));
g_fMenuTracking = TRUE;
SendMessage(g_hStatusWnd, SB_SETTEXT, SBT_NOBORDERS | 255, (LPARAM)szStatusText);
SendMessage(g_hStatusWnd, SB_SIMPLE, TRUE, 0L); // Remove sb panes
SendMessage(g_hStatusWnd, SB_SETTEXT, SBT_NOBORDERS | 0, (LPARAM) szStatusText);
}
}
/*++ MainWnd_OnTabCtrlNotify
Routine Description:
Handles WM_NOTIFY messages sent to the main window on behalf of the
tab control
Arguments:
pnmhdr - ptr to the notification block's header
Return Value:
BOOL - depends on message
Revision History:
Nov-29-95 Davepl Created
--*/
BOOL MainWnd_OnTabCtrlNotify(LPNMHDR pnmhdr)
{
HWND hwndTab = pnmhdr->hwndFrom;
//
// Selection is changing (new page coming to the front), so activate
// the appropriate page
//
if (TCN_SELCHANGE == pnmhdr->code)
{
INT iTab = TabCtrl_GetCurSel(hwndTab);
if (-1 != iTab)
{
if (-1 != g_Options.m_iCurrentPage)
{
g_pPages[g_Options.m_iCurrentPage]->Deactivate();
}
if (FAILED(g_pPages[iTab]->Activate()))
{
// If we weren't able to activate the new page,
// reactivate the old page just to be sure
if (-1 != g_Options.m_iCurrentPage)
{
g_pPages[iTab]->Activate();
SizeChildPage(g_hMainWnd);
}
}
else
{
g_Options.m_iCurrentPage = iTab;
SizeChildPage(g_hMainWnd);
return TRUE;
}
}
}
return FALSE;
}
/*++ MainWnd_OnSize
Routine Description:
Sizes the children of the main window as the size of the main
window itself changes
Arguments:
hwnd - main window
state - window state (not used here)
cx - new x size
cy - new y size
Return Value:
BOOL - depends on message
Revision History:
Nov-29-95 Davepl Created
--*/
void MainWnd_OnSize(HWND hwnd, UINT state, int cx, int cy)
{
if (state == SIZE_MINIMIZED)
{
// If there's a tray, we can just hide since we have a
// tray icon anyway.
if (GetShellWindow() && g_Options.m_fHideWhenMin)
{
ShowWindow(hwnd, SW_HIDE);
}
}
//
// Let the status bar adjust itself first, and we will work back
// from its new position
//
HDWP hdwp = BeginDeferWindowPos(20);
FORWARD_WM_SIZE(g_hStatusWnd, state, cx, cy, SendMessage);
RECT rcStatus;
GetClientRect(g_hStatusWnd, &rcStatus);
MapWindowPoints(g_hStatusWnd, g_hMainWnd, (LPPOINT) &rcStatus, 2);
//
// Size the tab controls based on where the status bar is
//
HWND hwndTabs = GetDlgItem(hwnd, IDC_TABS);
RECT rcTabs;
GetWindowRect(hwndTabs, &rcTabs);
MapWindowPoints(HWND_DESKTOP, g_hMainWnd, (LPPOINT) &rcTabs, 2);
INT dx = cx - 2 * rcTabs.left;
DeferWindowPos(hdwp, hwndTabs, NULL, 0, 0,
dx,
cy - (cy - rcStatus.top) - rcTabs.top * 2,
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
EndDeferWindowPos(hdwp);
//
// Now size the front page and its children
//
SizeChildPage(hwnd);
}
/*++ RunDlg
Routine Description:
Loads shell32.dll and invokes its Run dialog
Arguments:
Return Value:
Revision History:
Nov-30-95 Davepl Created
--*/
//
// Prototype for RunFileDlg in shell32 to which we will dynamically bind
//
typedef int (* pfn_RunFileDlg) (HWND hwndParent,
HICON hIcon,
LPCTSTR lpszWorkingDir,
LPCTSTR lpszTitle,
LPCTSTR lpszPrompt,
DWORD dwFlags);
DWORD RunDlg()
{
//
// Load shell32 and get the entry point for the RunFileDlg function
//
#ifdef SHELL_DYNA_LINK
HINSTANCE hShellDll = LoadLibrary(TEXT("shell32.dll"));
if (NULL == hShellDll)
{
return FALSE;
}
pfn_RunFileDlg pRunDlg = (pfn_RunFileDlg) GetProcAddress(hShellDll, (LPCSTR) 61);
if (NULL == pRunDlg)
{
FreeLibrary(hShellDll);
return FALSE;
}
#endif
//
// Put up the RUN dialog for the user (its always a UNICODE
// entry point, so use WCHAR)
//
HICON hIcon = (HICON) LoadImage(g_hInstance, MAKEINTRESOURCE(IDI_MAIN), IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR | LR_DEFAULTSIZE);
if (hIcon)
{
WCHAR szCurDir[MAX_PATH];
WCHAR szTitle [MAX_PATH];
WCHAR szPrompt[MAX_PATH];
LoadStringW(g_hInstance, IDS_RUNTITLE, szTitle, MAX_PATH);
LoadStringW(g_hInstance, IDS_RUNTEXT, szPrompt, MAX_PATH);
GetCurrentDirectoryW(MAX_PATH, szCurDir);
#ifdef SHELL_DYNA_LINK
pRunDlg(g_hMainWnd, hIcon, (LPTSTR) szCurDir,
(LPTSTR) szTitle,
(LPTSTR) szPrompt, RFD_USEFULLPATHDIR | RFD_WOW_APP);
#else
RunFileDlg(g_hMainWnd, hIcon, (LPTSTR) szCurDir,
(LPTSTR) szTitle,
(LPTSTR) szPrompt, RFD_USEFULLPATHDIR | RFD_WOW_APP);
#endif
DestroyIcon(hIcon);
}
#ifdef SHELL_DYNA_LINK
FreeLibrary(hShellDll);
#endif
return TRUE;
}
/*++ MainWnd_OnCommand
Routine Description:
Processes WM_COMMAND messages received at the main window
Revision History:
Nov-30-95 Davepl Created
--*/
void MainWnd_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
{
switch (id)
{
case IDM_HIDE:
{
ShowWindow(hwnd, SW_MINIMIZE);
break;
}
case IDM_HELP:
{
WinHelp(hwnd, TEXT("taskmgr.hlp"), HELP_FINDER, 0);
break;
}
case IDCANCEL:
case IDM_EXIT:
{
DestroyWindow(hwnd);
break;
}
case IDM_RESTORETASKMAN:
{
ShowRunningInstance();
break;
}
case IDC_NEXTTAB:
case IDC_PREVTAB:
{
INT iPage = g_Options.m_iCurrentPage;
iPage += (id == IDC_NEXTTAB) ? 1 : -1;
iPage = iPage < 0 ? NUM_PAGES - 1 : iPage;
iPage = iPage >= NUM_PAGES ? 0 : iPage;
// Activate the new page. If it fails, revert to the current
TabCtrl_SetCurSel(GetDlgItem(g_hMainWnd, IDC_TABS), iPage);
// SetCurSel doesn't do the page change (that would make too much
// sense), so we have to fake up a TCN_SELCHANGE notification
NMHDR nmhdr;
nmhdr.hwndFrom = GetDlgItem(g_hMainWnd, IDC_TABS);
nmhdr.idFrom = IDC_TABS;
nmhdr.code = TCN_SELCHANGE;
if (MainWnd_OnTabCtrlNotify(&nmhdr))
{
g_Options.m_iCurrentPage = iPage;
}
break;
}
case IDM_ALWAYSONTOP:
{
g_Options.m_fAlwaysOnTop = !g_Options.m_fAlwaysOnTop;
SetWindowPos(hwnd, g_Options.m_fAlwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
0,0,0,0, SWP_NOMOVE | SWP_NOSIZE);
UpdateMenuStates();
break;
}
case IDM_HIDEWHENMIN:
{
g_Options.m_fHideWhenMin = !g_Options.m_fHideWhenMin;
UpdateMenuStates();
break;
}
case IDM_MINIMIZEONUSE:
{
g_Options.m_fMinimizeOnUse = !g_Options.m_fMinimizeOnUse;
UpdateMenuStates();
break;
}
case IDM_CONFIRMATIONS:
{
g_Options.m_fConfirmations = !g_Options.m_fConfirmations;
UpdateMenuStates();
break;
}
case IDM_NOTITLE:
{
g_Options.m_fNoTitle = !g_Options.m_fNoTitle;
UpdateMenuStates();
SizeChildPage(hwnd);
break;
}
case IDM_SHOW16BIT:
{
g_Options.m_fShow16Bit = !g_Options.m_fShow16Bit;
UpdateMenuStates();
if (g_pPages[PROC_PAGE])
{
g_pPages[PROC_PAGE]->TimerEvent();
}
break;
}
case IDM_KERNELTIMES:
{
g_Options.m_fKernelTimes = !g_Options.m_fKernelTimes;
UpdateMenuStates();
if (g_pPages[PERF_PAGE])
{
g_pPages[PERF_PAGE]->TimerEvent();
}
break;
}
case IDM_RUN:
{
RunDlg();
break;
}
case IDM_SMALLICONS:
case IDM_DETAILS:
case IDM_LARGEICONS:
{
g_Options.m_vmViewMode = (VIEWMODE) (id - VM_FIRST);
UpdateMenuStates();
if (g_pPages[TASK_PAGE])
{
g_pPages[TASK_PAGE]->TimerEvent();
}
break;
}
// The following few messages get deferred off to the task page
case IDM_TASK_CASCADE:
case IDM_TASK_MINIMIZE:
case IDM_TASK_MAXIMIZE:
case IDM_TASK_TILEHORZ:
case IDM_TASK_TILEVERT:
case IDM_TASK_BRINGTOFRONT:
{
SendMessage(g_pPages[TASK_PAGE]->GetPageWindow(), WM_COMMAND, id, NULL);
break;
}
case IDM_PROCCOLS:
{
if (g_pPages[PROC_PAGE])
{
((CProcPage *) (g_pPages[PROC_PAGE]))->PickColumns();
}
break;
}
case IDM_ALLCPUS:
case IDM_MULTIGRAPH:
{
g_Options.m_cmHistMode = (CPUHISTMODE) (id - CM_FIRST);
UpdateMenuStates();
if (g_pPages[PERF_PAGE])
{
((CPerfPage *)(g_pPages[PERF_PAGE]))->UpdateGraphs();
g_pPages[PERF_PAGE]->TimerEvent();
}
break;
}
case IDM_REFRESH:
{
MainWnd_OnTimer(hwnd, 0);
break;
}
case IDM_HIGH:
case IDM_NORMAL:
case IDM_LOW:
case IDM_PAUSED:
{
static const int TimerDelays[] = { 500, 2000, 4000, 0, 0xFFFFFFFF };
g_Options.m_usUpdateSpeed = (UPDATESPEED) (id - US_FIRST);
ASSERT(g_Options.m_usUpdateSpeed <= ARRAYSIZE(TimerDelays));
int cTicks = TimerDelays[ (INT) g_Options.m_usUpdateSpeed ];
g_Options.m_dwTimerInterval = cTicks;
KillTimer(g_hMainWnd, 0);
if (cTicks)
{
SetTimer(g_hMainWnd, 0, g_Options.m_dwTimerInterval, NULL);
}
UpdateMenuStates();
break;
}
case IDM_ABOUT:
{
//
// Display the "About Task Manager" dialog
//
HICON hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_MAIN));
if (hIcon)
{
TCHAR szTitle[MAX_PATH];
LoadString(g_hInstance, IDS_APPTITLE, szTitle, MAX_PATH);
ShellAbout(hwnd, szTitle, NULL, hIcon);
DestroyIcon(hIcon);
}
}
}
}
/*++ CheckParentDeferrals
Routine Description:
Called by the child pages, each child gives the main parent the
opportunity to handle certain messages on its behalf
Arguments:
MSG, WPARAM, LPARAM
Return Value:
TRUE if parent handle the message on the childs behalf
Revision History:
Jan-24-95 Davepl Created
--*/
BOOL CheckParentDeferrals(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_RBUTTONDOWN:
case WM_NCRBUTTONDOWN:
case WM_RBUTTONUP:
case WM_NCRBUTTONUP:
case WM_NCLBUTTONDBLCLK:
case WM_LBUTTONDBLCLK:
{
SendMessage(g_hMainWnd, uMsg, wParam, lParam);
return TRUE;
}
default:
return FALSE;
}
}
/*++ ShowRunningInstance
Routine Description:
Brings this running instance to the top, and out of icon state
Revision History:
Jan-27-95 Davepl Created
--*/
void ShowRunningInstance()
{
OpenIcon(g_hMainWnd);
SetForegroundWindow(g_hMainWnd);
SetWindowPos(g_hMainWnd, g_Options.m_fAlwaysOnTop ? HWND_TOPMOST : HWND_TOP,
0,0,0,0, SWP_NOMOVE | SWP_NOSIZE);
}
/*++ MainWindowProc
Routine Description:
WNDPROC for the main window
Arguments:
Standard wndproc fare
Return Value:
Revision History:
Nov-30-95 Davepl Created
--*/
BOOL CALLBACK MainWindowProc(
HWND hwnd, // handle to dialog box
UINT uMsg, // message
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
)
{
static BOOL fIsHidden = FALSE;
// If this is a size or a move, update the position in the user's options
if (uMsg == WM_SIZE || uMsg == WM_MOVE)
{
// We don't want to start recording the window pos until we've had
// a chance to set it to the intialial position, or we'll lose the
// user's preferences
if (fAlreadySetPos)
{
WINDOWPLACEMENT wp;
wp.length = sizeof(WINDOWPLACEMENT);
GetWindowPlacement(hwnd, &wp);
g_Options.m_rcWindow = wp.rcNormalPosition;
}
}
switch(uMsg)
{
HANDLE_MSG(hwnd, WM_INITDIALOG, MainWnd_OnInitDialog);
HANDLE_MSG(hwnd, WM_MENUSELECT, MainWnd_OnMenuSelect);
HANDLE_MSG(hwnd, WM_SIZE, MainWnd_OnSize);
HANDLE_MSG(hwnd, WM_PAINT, MainWnd_OnPaint);
HANDLE_MSG(hwnd, WM_COMMAND, MainWnd_OnCommand);
HANDLE_MSG(hwnd, WM_TIMER, MainWnd_OnTimer);
// Don't let the window get too small when the title and
// menu bars are ON
case WM_GETMINMAXINFO:
{
if (FALSE == g_Options.m_fNoTitle)
{
LPMINMAXINFO lpmmi = (LPMINMAXINFO) lParam;
lpmmi->ptMinTrackSize.x = g_minWidth;
lpmmi->ptMinTrackSize.y = g_minHeight;
return FALSE;
}
break;
}
// Handle notifications from out tray icon
case PWM_TRAYICON:
{
Tray_Notify(hwnd, wParam, lParam);
break;
}
// Someone externally is asking us to wake up and be shown
case PWM_ACTIVATE:
{
ShowRunningInstance();
// Return PWM_ACTIVATE to the caller as just a little
// more assurance that we really did handle this
// message correctly.
SetWindowLong(hwnd, DWL_MSGRESULT, PWM_ACTIVATE);
return TRUE;
}
case WM_INITMENU:
{
// Don't let the right button hide the window during
// menu operations
g_fCantHide = TRUE;
break;
}
// If we're in always-on-top mode, the right mouse button will
// temporarily hide us so that the user can see whats under us
case WM_RBUTTONDOWN:
case WM_NCRBUTTONDOWN:
{
#ifdef ENABLE_HIDING
if (!g_fInPopup && !fIsHidden && !g_fCantHide && g_Options.m_fAlwaysOnTop)
{
ShowWindow (hwnd, SW_HIDE);
// We take the capture so that we're guaranteed to get
// the right button up even if its off our window
SetCapture (hwnd);
fIsHidden = TRUE;
}
#endif
break;
}
// On right button up, if we were hidden, unhide us. We have
// to use the correct state (min/max/restore)
case WM_RBUTTONUP:
case WM_NCRBUTTONUP:
{
#ifdef ENABLE_HIDING
if (fIsHidden)
{
ReleaseCapture();
if (IsIconic(hwnd))
ShowWindow (hwnd, SW_SHOWMINNOACTIVE);
else if (IsZoomed (hwnd))
{
ShowWindow (hwnd, SW_SHOWMAXIMIZED);
SetForegroundWindow (hwnd);
}
else
{
ShowWindow (hwnd, SW_SHOWNOACTIVATE);
SetForegroundWindow (hwnd);
}
fIsHidden = FALSE;
}
#endif
break;
}
case WM_NCHITTEST:
{
// If we have no title/menu bar, clicking and dragging the client
// area moves the window. To do this, return HTCAPTION.
// Note dragging not allowed if window maximized, or if caption
// bar is present.
//
wParam = DefWindowProc(hwnd, uMsg, wParam, lParam);
if (g_Options.m_fNoTitle && (wParam == HTCLIENT) && !IsZoomed(g_hMainWnd))
{
SetWindowLong(hwnd, DWL_MSGRESULT, HTCAPTION);
return TRUE;
}
else
{
return FALSE; // Not handled
}
}
case WM_NCLBUTTONDBLCLK:
{
// If we have no title, an NC dbl click means we should turn
// them back on
if (FALSE == g_Options.m_fNoTitle)
{
break;
}
// Else, fall though
}
case WM_LBUTTONDBLCLK:
{
g_Options.m_fNoTitle = ~g_Options.m_fNoTitle;
RECT rcMainWnd;
GetWindowRect(g_hMainWnd, &rcMainWnd);
if (g_pPages[PERF_PAGE])
{
((CPerfPage *)(g_pPages[PERF_PAGE]))->UpdateGraphs();
g_pPages[PERF_PAGE]->TimerEvent();
}
// Force a WM_SIZE event so that the window checks the min size
// when coming out of notitle mode
MoveWindow(g_hMainWnd,
rcMainWnd.left,
rcMainWnd.top,
rcMainWnd.right - rcMainWnd.left,
rcMainWnd.bottom - rcMainWnd.top,
TRUE);
SizeChildPage(hwnd);
break;
}
// Someone (the task page) wants us to look up a process in the
// process view. Switch to that page and send it the FINDPROC
// message
case WM_FINDPROC:
{
if (-1 != TabCtrl_SetCurSel(GetDlgItem(hwnd, IDC_TABS), PROC_PAGE))
{
// SetCurSel doesn't do the page change (that would make too much
// sense), so we have to fake up a TCN_SELCHANGE notification
NMHDR nmhdr;
nmhdr.hwndFrom = GetDlgItem(hwnd, IDC_TABS);
nmhdr.idFrom = IDC_TABS;
nmhdr.code = TCN_SELCHANGE;
if (MainWnd_OnTabCtrlNotify(&nmhdr))
{
SendMessage(g_pPages[g_Options.m_iCurrentPage]->GetPageWindow(),
WM_FINDPROC, wParam, lParam);
}
}
else
{
MessageBeep(0);
}
break;
}
case WM_NOTIFY:
{
switch(wParam)
{
case IDC_TABS:
return MainWnd_OnTabCtrlNotify((LPNMHDR) lParam);
default:
break;
}
break;
}
case WM_ENDSESSION:
{
if (wParam)
{
DestroyWindow(g_hMainWnd);
}
break;
}
case WM_CLOSE:
{
DestroyWindow(g_hMainWnd);
break;
}
case WM_NCDESTROY:
{
// Remove the tray icon
CTrayNotification * pNot = new CTrayNotification(hwnd,
PWM_TRAYICON,
NIM_DELETE,
NULL,
NULL);
if (pNot)
{
if (FALSE == DeliverTrayNotification(pNot))
{
delete pNot;
}
}
// If there's a tray thread, tell is to exit
EnterCriticalSection(&g_CSTrayThread);
if (g_idTrayThread)
{
PostThreadMessage(g_idTrayThread, PM_QUITTRAYTHREAD, 0, 0);
}
LeaveCriticalSection(&g_CSTrayThread);
// Wait around for some period of time for the tray thread to
// do its cleanup work. If the wait times out, worst case we
// orphan the tray icon.
if (g_hTrayThread)
{
#define TRAY_THREAD_WAIT 3000
WaitForSingleObject(g_hTrayThread, TRAY_THREAD_WAIT);
CloseHandle(g_hTrayThread);
}
break;
}
case WM_DESTROY:
{
// Before shutting down, deactivate the current page, then
// destroy all pages
if (g_Options.m_iCurrentPage && g_pPages[g_Options.m_iCurrentPage])
{
g_pPages[g_Options.m_iCurrentPage]->Deactivate();
}
for (int i = 0; i < ARRAYSIZE(g_pPages); i++)
{
if (g_pPages[i])
{
g_pPages[i]->Destroy();
}
}
// Save the current options
g_Options.Save();
PostQuitMessage(0);
}
default:
return FALSE; // Not handled here
}
return FALSE;
}
/*++ LoadGlobalResources
Routine Description:
Loads those resources that are used frequently or that are expensive to
load a single time at program startup
Return Value:
BOOLEAN success value
Revision History:
Nov-30-95 Davepl Created
--*/
static const struct
{
LPTSTR psz;
size_t len;
UINT id;
}
g_aStrings[] =
{
{ g_szK, ARRAYSIZE(g_szK), IDS_K },
{ g_szRealtime, ARRAYSIZE(g_szRealtime), IDS_REALTIME },
{ g_szNormal, ARRAYSIZE(g_szNormal), IDS_NORMAL },
{ g_szLow, ARRAYSIZE(g_szLow), IDS_LOW },
{ g_szHigh, ARRAYSIZE(g_szHigh), IDS_HIGH },
{ g_szUnknown, ARRAYSIZE(g_szUnknown), IDS_UNKNOWN },
{ g_szRunning, ARRAYSIZE(g_szRunning), IDS_RUNNING },
{ g_szHung, ARRAYSIZE(g_szHung), IDS_HUNG },
{ g_szfmtTasks, ARRAYSIZE(g_szfmtTasks), IDS_FMTTASKS },
{ g_szfmtProcs, ARRAYSIZE(g_szfmtProcs), IDS_FMTPROCS },
{ g_szfmtCPU, ARRAYSIZE(g_szfmtCPU), IDS_FMTCPU },
{ g_szfmtMEM, ARRAYSIZE(g_szfmtMEM), IDS_FMTMEM },
{ g_szfmtCPUNum, ARRAYSIZE(g_szfmtCPUNum), IDS_FMTCPUNUM },
{ g_szTotalCPU, ARRAYSIZE(g_szTotalCPU), IDS_TOTALTIME },
{ g_szKernelCPU, ARRAYSIZE(g_szKernelCPU), IDS_KERNELTIME},
{ g_szMemUsage, ARRAYSIZE(g_szMemUsage), IDS_MEMUSAGE },
};
static const UINT idTrayIcons[] =
{
IDI_TRAY0, IDI_TRAY1, IDI_TRAY2, IDI_TRAY3, IDI_TRAY4, IDI_TRAY5,
IDI_TRAY6, IDI_TRAY7, IDI_TRAY8, IDI_TRAY9, IDI_TRAY10, IDI_TRAY11
};
HICON g_aTrayIcons[ARRAYSIZE(idTrayIcons)];
UINT g_cTrayIcons = ARRAYSIZE(idTrayIcons);
BOOL LoadGlobalResources()
{
// If we don't get accelerators, its not worth failing the load
g_hAccel = (HACCEL) LoadAccelerators(g_hInstance, MAKEINTRESOURCE(IDR_ACCELERATORS));
Assert(g_hAccel);
for (UINT i = 0; i < g_cTrayIcons; i++)
{
VERIFY( g_aTrayIcons[i] = (HICON) LoadImage(g_hInstance,
MAKEINTRESOURCE(idTrayIcons[i]),
IMAGE_ICON,
0, 0,
LR_DEFAULTCOLOR) );
}
for (i = 0; i < ARRAYSIZE(g_aStrings); i++)
{
if (FALSE == LoadString(g_hInstance,
g_aStrings[i].id,
g_aStrings[i].psz,
g_aStrings[i].len))
{
return FALSE;
}
}
g_hbmpBack = (HBITMAP) LoadImage(g_hInstance,
MAKEINTRESOURCE(IDB_BMPBACK),
IMAGE_BITMAP,
0, 0,
LR_LOADMAP3DCOLORS);
if (NULL == g_hbmpBack)
{
return FALSE;
}
g_hbmpForward = (HBITMAP) LoadImage(g_hInstance,
MAKEINTRESOURCE(IDB_BMPFORWARD),
IMAGE_BITMAP,
0, 0,
LR_LOADMAP3DCOLORS);
if (NULL == g_hbmpForward)
{
DeleteObject(g_hbmpBack);
g_hbmpBack = NULL;
return FALSE;
}
return TRUE;
}
/*++ WinMain
Routine Description:
Windows app startup. Does basic initialization and creates the main window
Arguments:
Standard winmain
Return Value:
App exit code
Revision History:
Nov-30-95 Davepl Created
--*/
int WINAPI WinMainT(
HINSTANCE hInstance, // handle to current instance
HINSTANCE hPrevInstance, // handle to previous instance (n/a)
LPTSTR lpCmdLine, // pointer to command line
int nShowCmd // show state of window
)
{
g_hInstance = hInstance;
int retval = TRUE;
HKEY hKeyPolicy;
DWORD dwType, dwData = 0, dwSize;
InitializeCriticalSection(&g_CSTrayThread);
// Try to create or grab the startup mutex. Only in the case
// where everything goes well and the mutex already existed AND
// we were able to grab it do we deem ourselves to be a secondary instance
g_hStartupMutex = CreateMutex(NULL, TRUE, cszStartupMutex);
if (g_hStartupMutex && GetLastError() == ERROR_ALREADY_EXISTS)
{
// Give the other instance (the one that owns the startup mutex) 10
// seconds to do its thing
WaitForSingleObject(g_hStartupMutex, FINDME_TIMEOUT);
}
//
// Locate and activate a running instance if it exists.
//
TCHAR szTitle[MAX_PATH];
if (LoadString(hInstance, IDS_APPTITLE, szTitle, ARRAYSIZE(szTitle)))
{
HWND hwndOld = FindWindow(WC_DIALOG, szTitle);
if (hwndOld)
{
// Send the other copy of ourselves a PWM_ACTIVATE message. If that
// succeeds, and it returns PWM_ACTIVATE back as the return code, it's
// up and alive and we can exit this instance.
DWORD dwResult;
if (SendMessageTimeout(hwndOld,
PWM_ACTIVATE,
0, 0,
SMTO_ABORTIFHUNG,
FINDME_TIMEOUT,
&dwResult))
{
if (dwResult == PWM_ACTIVATE)
{
goto cleanup;
}
}
}
}
if (RegOpenKeyEx (HKEY_CURRENT_USER,
TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System"),
0, KEY_READ, &hKeyPolicy) == ERROR_SUCCESS)
{
dwSize = sizeof(dwData);
RegQueryValueEx (hKeyPolicy, TEXT("DisableTaskMgr"), NULL,
&dwType, (LPBYTE) &dwData, &dwSize);
RegCloseKey (hKeyPolicy);
if (dwData)
{
TCHAR szTitle[25];
TCHAR szMessage[200];
LoadString (hInstance, IDS_TASKMGR, szTitle, ARRAYSIZE(szTitle));
LoadString (hInstance, IDS_TASKMGRDISABLED , szMessage, ARRAYSIZE(szMessage));
MessageBox (NULL, szMessage, szTitle, MB_OK | MB_ICONSTOP);
retval = FALSE;
goto cleanup;
}
}
// No running instance found, so we run as normal
InitCommonControls();
InitDavesControls();
// Start the worker thread. If it fails, you just don't
// get tray icons
g_hTrayThread = CreateThread(NULL, 0, TrayThreadMessageLoop, NULL, 0, &g_idTrayThread);
ASSERT(g_hTrayThread);
// Init the page table
g_pPages[0] = new CTaskPage;
if (NULL == g_pPages[0])
{
retval = FALSE;
goto cleanup;
}
g_pPages[1] = new CProcPage;
if (NULL == g_pPages[1])
{
retval = FALSE;
goto cleanup;
}
g_pPages[2] = new CPerfPage;
if (NULL == g_pPages[2])
{
retval = FALSE;
goto cleanup;
}
// Load whatever resources that we need available globally
if (FALSE == LoadGlobalResources())
{
retval = FALSE;
goto cleanup;
}
// Initialize the history buffers
if (0 == InitPerfInfo())
{
retval = FALSE;
goto cleanup;
}
// Create the main window (it's a modeless dialog, to be precise)
g_hMainWnd = CreateDialog(hInstance,
MAKEINTRESOURCE(IDD_MAINWND),
NULL,
MainWindowProc);
if (NULL == g_hMainWnd)
{
retval = FALSE;
goto cleanup;
}
else
{
fAlreadySetPos = TRUE;
SetWindowPos(g_hMainWnd, NULL,
g_Options.m_rcWindow.left,
g_Options.m_rcWindow.top,
g_Options.m_rcWindow.right-g_Options.m_rcWindow.left,
g_Options.m_rcWindow.bottom-g_Options.m_rcWindow.top,
SWP_NOZORDER);
ShowWindow(g_hMainWnd, SW_SHOW);
}
// We're out of the "starting up" phase so release the startup mutex
if (g_hStartupMutex)
{
ReleaseMutex(g_hStartupMutex);
CloseHandle(g_hStartupMutex);
g_hStartupMutex = NULL;
}
// If we're the one, true, task manager, we can hang around till the
// bitter end in case the user has problems during shutdown
SetProcessShutdownParameters(1, SHUTDOWN_NORETRY);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
// Give the page a crack at the accelerator
HWND hwndPage = NULL;
if (g_Options.m_iCurrentPage >= 0)
{
g_pPages[g_Options.m_iCurrentPage]->GetPageWindow();
}
BOOL bHandled = FALSE;
bHandled = TranslateAccelerator(g_hMainWnd, g_hAccel, &msg);
if (FALSE == bHandled)
{
if (hwndPage)
{
bHandled = TranslateAccelerator(hwndPage, g_hAccel, &msg);
}
if (FALSE == bHandled && FALSE == IsDialogMessage(g_hMainWnd, &msg))
{
TranslateMessage(&msg); // Translates virtual key codes
DispatchMessage(&msg); // Dispatches message to window
}
}
}
cleanup:
// We're no longer "starting up"
if (g_hStartupMutex)
{
ReleaseMutex(g_hStartupMutex);
CloseHandle(g_hStartupMutex);
g_hStartupMutex = NULL;
}
// Yes, I could use virtual destructors, but I could also poke
// myself in the eye with a sharp stick. Either way you wouldn't
// be able to see what's going on.
if (g_pPages[TASK_PAGE])
delete (CTaskPage *) g_pPages[TASK_PAGE];
if (g_pPages[PROC_PAGE])
delete (CProcPage *) g_pPages[PROC_PAGE];
if (g_pPages[PERF_PAGE])
delete (CPerfPage *) g_pPages[PERF_PAGE];
ReleasePerfInfo();
return (retval);
}
//
// And now the magic begins. The normal C++ CRT code walks a set of vectors
// and calls through them to perform global initializations. Those vectors
// are always in data segments with a particular naming scheme. By delcaring
// the variables below, I can determine where in my code they get stuck, and
// then call them myself
//
typedef void (__cdecl *_PVFV)(void);
#pragma data_seg(".CRT$XIA")
_PVFV __xi_a[] = { NULL };
#pragma data_seg(".CRT$XIZ")
_PVFV __xi_z[] = { NULL };
#pragma data_seg(".CRT$XCA")
_PVFV __xc_a[] = { NULL };
#pragma data_seg(".CRT$XCZ")
_PVFV __xc_z[] = { NULL };
#pragma data_seg(".data")
/*++ _initterm
Routine Description:
Walk the table of function pointers from the bottom up, until
the end is encountered. Do not skip the first entry. The initial
value of pfbegin points to the first valid entry. Do not try to
execute what pfend points to. Only entries before pfend are valid.
Arguments:
pfbegin - first pointer
pfend - last pointer
Revision History:
Nov-30-95 Davepl Created
--*/
static void __cdecl _initterm ( _PVFV * pfbegin, _PVFV * pfend )
{
while ( pfbegin < pfend )
{
// if current table entry is non-NULL, call thru it.
if ( *pfbegin != NULL )
{
(**pfbegin)();
}
++pfbegin;
}
}
/*++ WinMain
Routine Description:
Windows app startup. Does basic initialization and creates the main window
Arguments:
Standard winmain
Return Value:
App exit code
Revision History:
Nov-30-95 Davepl Created
--*/
int _stdcall ModuleEntry(void)
{
int i;
STARTUPINFO si;
SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
//
// Do runtime startup initializers.
//
_initterm( __xi_a, __xi_z );
//
// do C++ constructors (initializers) specific to this EXE
//
_initterm( __xc_a, __xc_z );
LPTSTR pszCmdLine = GetCommandLine();
if ( *pszCmdLine == TEXT('\"') ) {
/*
* Scan, and skip over, subsequent characters until
* another double-quote or a null is encountered.
*/
while ( *++pszCmdLine && (*pszCmdLine
!= TEXT('\"')) );
/*
* If we stopped on a double-quote (usual case), skip
* over it.
*/
if ( *pszCmdLine == TEXT('\"') )
pszCmdLine++;
}
else {
while (*pszCmdLine > TEXT(' '))
pszCmdLine++;
}
/*
* Skip past any white space preceeding the second token.
*/
while (*pszCmdLine && (*pszCmdLine <= TEXT(' '))) {
pszCmdLine++;
}
si.dwFlags = 0;
GetStartupInfo(&si);
i = WinMainT(GetModuleHandle(NULL), NULL, pszCmdLine,
si.dwFlags & STARTF_USESHOWWINDOW ? si.wShowWindow : SW_SHOWDEFAULT);
ExitProcess(i);
return i; // We never comes here.
}
// DisplayFailureMsg
//
// Displays a generic error message based on the error code
// and message box title provided
void DisplayFailureMsg(HWND hWnd, UINT idTitle, DWORD dwError)
{
TCHAR szTitle[MAX_PATH];
TCHAR szMsg[MAX_PATH * 2];
TCHAR szError[MAX_PATH];
if (0 == LoadString(g_hInstance, idTitle, szTitle, ARRAYSIZE(szTitle)))
{
return;
}
if (0 == LoadString(g_hInstance, IDS_GENFAILURE, szMsg, ARRAYSIZE(szMsg)))
{
return;
}
// "incorrect paramter" doesn't make a lot of sense for the user, so
// massage it to be "Operation not allowed on this process".
if (dwError == ERROR_INVALID_PARAMETER)
{
if (0 == LoadString(g_hInstance, IDS_BADPROC, szError, ARRAYSIZE(szError)))
{
return;
}
}
else if (0 == FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
dwError,
LANG_USER_DEFAULT,
szError,
ARRAYSIZE(szError),
NULL))
{
return;
}
lstrcat(szMsg, szError);
MessageBox(hWnd, szMsg, szTitle, MB_OK | MB_ICONERROR);
}
/*++ NewDeskDlgProc
Routine Description:
Dialog proc for the NewDesktop dialog
Arguments:
Return Value:
BOOL, TRUE == success
Revision History:
Nov-29-95 Davepl Created
--*/
typedef struct _NewDesktopInfo
{
TCHAR m_szName[MAX_PATH];
BOOL m_fStartExplorer;
} NewDesktopInfo;
BOOL CALLBACK NewDeskDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
NewDesktopInfo * pndi = (NewDesktopInfo *) GetWindowLong(hwndDlg, GWL_USERDATA);
switch (uMsg)
{
case WM_INITDIALOG:
{
pndi = (NewDesktopInfo *) lParam;
SetWindowLong(hwndDlg, GWL_USERDATA, lParam);
CheckDlgButton(hwndDlg, IDC_STARTEXPLORER, pndi->m_fStartExplorer);
SetWindowText(GetDlgItem(hwndDlg, IDC_DESKTOPNAME), pndi->m_szName);
return TRUE;
}
case WM_COMMAND:
{
switch(LOWORD(wParam))
{
case IDCANCEL:
{
EndDialog(hwndDlg, IDCANCEL);
break;
}
case IDOK:
{
pndi->m_fStartExplorer = IsDlgButtonChecked(hwndDlg, IDC_STARTEXPLORER);
GetWindowText(GetDlgItem(hwndDlg, IDC_DESKTOPNAME), pndi->m_szName, ARRAYSIZE(pndi->m_szName));
EndDialog(hwndDlg, IDOK);
break;
}
default:
break;
}
}
default:
{
return FALSE;
}
}
}
#ifdef DESKTOP_SUPPORT
/*++ CreateNewDesktop
Routine Description:
Prompts the user for options and starts a new desktop
Arguments:
Return Value:
BOOL, TRUE == success
Revision History:
Nov-29-95 Davepl Created
--*/
BOOL CreateNewDesktop()
{
NewDesktopInfo ndi;
ndi.m_fStartExplorer = TRUE;
TryAgain:
//
// Look for a desktop name that's not in use yet
//
for (int i = 0; ; i++)
{
wsprintf( ndi.m_szName, TEXT("Desktop%d"), i );
HDESK hdesk = OpenDesktop( ndi.m_szName, 0, FALSE, DESKTOP_SWITCHDESKTOP );
if (hdesk)
{
CloseDesktop(hdesk);
}
else
{
break;
}
}
//
// Allow the user to modify the options
//
int iRet = DialogBoxParam(g_hInstance,
MAKEINTRESOURCE(IDD_CREATEDESKTOP),
g_hMainWnd,
NewDeskDlgProc,
(LPARAM) &ndi);
if (iRet != IDOK)
{
return FALSE;
}
//
// Create the new desktop
//
HDESK hdesk = CreateDesktop( ndi.m_szName, NULL, NULL, 0, MAXIMUM_ALLOWED, NULL );
if (NULL == hdesk)
{
DWORD dwError = GetLastError();
DisplayFailureMsg(g_hMainWnd, IDS_CANTCREATEDESKTOP, dwError);
goto TryAgain;
}
return S_OK;
}
#endif //DESKTOP_SUPPORT
/*++ LoadPopupMenu
Routine Description:
Loads a popup menu from a resource. Needed because USER
does not support popup menus (yes, really)
Arguments:
hinst - module instance to look for resource in
id - resource id of popup menu
Return Value:
Revision History:
Nov-22-95 Davepl Created
--*/
HMENU LoadPopupMenu(HINSTANCE hinst, UINT id)
{
HMENU hmenuParent = LoadMenu(hinst, MAKEINTRESOURCE(id));
if (hmenuParent)
{
HMENU hpopup = GetSubMenu(hmenuParent, 0);
RemoveMenu(hmenuParent, 0, MF_BYPOSITION);
DestroyMenu(hmenuParent);
return hpopup;
}
return NULL;
}