600 lines
17 KiB
C
600 lines
17 KiB
C
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1994 - 1995.
|
|
|
|
// File: taskman.c
|
|
|
|
// Contents: Windows NT task manager for the shell
|
|
|
|
// History: 6-05-95 davepl Created
|
|
|
|
|
|
#include "cabinet.h"
|
|
#pragma hdrstop
|
|
|
|
#include "taskres.h"
|
|
|
|
// Globals
|
|
extern HINSTANCE hInst;
|
|
HANDLE g_hHeap;
|
|
HWND g_hwndTaskMan = NULL;
|
|
|
|
// Locally-defined prototypes
|
|
BOOL_PTR CALLBACK TaskManDlgProc(HWND, UINT, WPARAM, LPARAM);
|
|
BOOL OnCommand (HWND hwnd, int id, HWND hwndCtl, UINT codeNotify);
|
|
BOOL OnInitDialog (HWND hwnd, HWND hwndFocus, LPARAM lParam);
|
|
void OnShowWindow (HWND hwnd, BOOL fShow, UINT status);
|
|
void SetControlStates(HWND hWnd);
|
|
HRESULT GetWindowList (HWND hwndTasklist, HWND ** paHwnds, UINT * pcHwnds);
|
|
|
|
|
|
LRESULT CALLBACK TaskManStubWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
return DefWindowProc(hWnd, msg, wParam, lParam);
|
|
}
|
|
|
|
|
|
// Synopsis: Thread entry point for taskman creation
|
|
|
|
// History: 6-05-95 davepl Created
|
|
|
|
// Notes: Returns a success code when the taskman is subsequently
|
|
// destroyed
|
|
DWORD WINAPI StartTaskMan(LPVOID hWndParent)
|
|
{
|
|
HWND hwndstub;
|
|
WNDCLASS wndclass;
|
|
HANDLE hThisThread;
|
|
|
|
|
|
// Make sure the taskman isn't already running
|
|
|
|
|
|
if (g_hwndTaskMan)
|
|
return (DWORD) S_FALSE;
|
|
|
|
g_hHeap = GetProcessHeap(); // Store away the process heap
|
|
if (NULL == g_hHeap)
|
|
{
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
|
|
// Boost this thread's priority so that the taskman can come up
|
|
// even when the system is heavily loaded (like under stress).
|
|
|
|
|
|
hThisThread = GetCurrentThread();
|
|
if (NULL == hThisThread)
|
|
{
|
|
return (DWORD)E_FAIL;
|
|
}
|
|
|
|
SetThreadPriority(hThisThread, HIGH_PRIORITY_CLASS);
|
|
|
|
if (!GetClassInfo(hinstCabinet, TEXT("TaskManStubWndClass"), &wndclass))
|
|
{
|
|
ZeroMemory(&wndclass, SIZEOF(wndclass));
|
|
//wndclass.style = 0;
|
|
wndclass.lpfnWndProc = TaskManStubWndProc;
|
|
//wndclass.cbClsExtra = 0;
|
|
//wndclass.cbWndExtra = 0;
|
|
wndclass.hInstance = hinstCabinet;
|
|
//wndclass.hIcon = NULL;
|
|
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
|
|
wndclass.hbrBackground = GetStockObject (WHITE_BRUSH);
|
|
//wndclass.lpszMenuName = NULL;
|
|
wndclass.lpszClassName = TEXT("TaskManStubWndClass");
|
|
|
|
if (!RegisterClass(&wndclass))
|
|
return (DWORD)HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
|
|
// Create the stub window
|
|
|
|
|
|
if (hwndstub = CreateWindow(TEXT("TaskManStubWndClass"), // class
|
|
TEXT("TaskManStubWndTitle"), // title
|
|
WS_POPUP, // style
|
|
10, 10, 100, 100, // pos & size
|
|
NULL, // parent
|
|
NULL, // menu
|
|
hinstCabinet, // instance
|
|
NULL)) // window data
|
|
{
|
|
MSG msg;
|
|
HWND hDlg;
|
|
if (0 == (hDlg = CreateDialog(hinstCabinet,
|
|
MAKEINTRESOURCE(IDD_TASKMAN),
|
|
hwndstub,
|
|
TaskManDlgProc)))
|
|
{
|
|
DWORD dwError = HRESULT_FROM_WIN32(GetLastError());
|
|
PostMessage(hwndstub, WM_CLOSE, 0, 0);
|
|
return dwError;
|
|
}
|
|
|
|
|
|
// Churn, churn, churn...
|
|
|
|
|
|
while (GetMessage(&msg, NULL, 0, 0))
|
|
{
|
|
if (FALSE == IsDialogMessage(hDlg, &msg))
|
|
{
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
}
|
|
}
|
|
return (DWORD)HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
|
|
// Synopsis: Sets the default button in a dialog
|
|
|
|
// Arguments: [hwndDlg] -- Dialog
|
|
// [idButton] -- Button to make the default
|
|
|
|
// History: 6-05-95 davepl NT Port
|
|
|
|
// Notes: We use this to mark the SWITCHTO button as default when
|
|
// working with the task listview, so that when enter is
|
|
// pressed it acts as if SWITCHTO was selected (got to be
|
|
// and easier way...)
|
|
VOID SetDefButton(HWND hwndDlg, INT idButton)
|
|
{
|
|
LRESULT lr;
|
|
|
|
if (HIWORD(lr = SendMessage(hwndDlg, DM_GETDEFID, 0, 0)) == DC_HASDEFID)
|
|
{
|
|
HWND hwndOldDefButton = GetDlgItem(hwndDlg, LOWORD(lr));
|
|
if (hwndOldDefButton)
|
|
{
|
|
SendMessage (hwndOldDefButton,
|
|
BM_SETSTYLE,
|
|
MAKEWPARAM(BS_PUSHBUTTON, 0),
|
|
MAKELPARAM(TRUE, 0));
|
|
}
|
|
}
|
|
|
|
SendMessage( hwndDlg, DM_SETDEFID, idButton, 0L );
|
|
SendMessage( GetDlgItem(hwndDlg, idButton),
|
|
BM_SETSTYLE,
|
|
MAKEWPARAM( BS_DEFPUSHBUTTON, 0 ),
|
|
MAKELPARAM( TRUE, 0 ));
|
|
}
|
|
|
|
|
|
// Synopsis: Wndproc for the taskman dialog
|
|
|
|
// History: 6-05-95 davepl NT Port
|
|
BOOL_PTR CALLBACK TaskManDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
INSTRUMENT_WNDPROC(SHCNFI_TASKMAN_DLGPROC, hWnd, msg, wParam, lParam);
|
|
|
|
switch(msg)
|
|
{
|
|
HANDLE_MSG(hWnd, WM_INITDIALOG, OnInitDialog);
|
|
HANDLE_MSG(hWnd, WM_COMMAND, OnCommand);
|
|
HANDLE_MSG(hWnd, WM_SHOWWINDOW, OnShowWindow);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
// Synopsis: Caches the process heap. This could use the shell allocator,
|
|
// but I'm trying to keep the code as separate as possible
|
|
// at this point
|
|
|
|
// History: 6-05-95 davepl NT Port
|
|
|
|
// Notes: Also sets the global handle to indicate that the taskman
|
|
// is up and running.
|
|
|
|
// BUGBUG (Davepl) Potential race condition here?
|
|
BOOL OnInitDialog(HWND hWnd, HWND hwndFocus, LPARAM lParam)
|
|
{
|
|
ENTERCRITICAL;
|
|
if (g_hwndTaskMan)
|
|
{
|
|
LEAVECRITICAL;
|
|
PostMessage(hWnd, WM_CLOSE, 0, 0);
|
|
return FALSE;
|
|
}
|
|
g_hwndTaskMan = hWnd;
|
|
LEAVECRITICAL;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
// Synopsis: Processes all WM_COMMAND messages from the dialog,
|
|
// typically resulting from user interaction
|
|
|
|
// History: 6-05-95 davepl NT Port
|
|
BOOL OnCommand(HWND hWnd, int id, HWND hwndCtl, UINT codeNotify)
|
|
{
|
|
switch(id)
|
|
{
|
|
case IDCANCEL:
|
|
{
|
|
ShowWindow(hWnd, SW_HIDE);
|
|
break;
|
|
}
|
|
case IDC_SWITCHTO:
|
|
{
|
|
HWND * aHwnds;
|
|
UINT cHwnds;
|
|
HRESULT hr = GetWindowList(GetDlgItem(hWnd, IDC_TASKLIST), &aHwnds, &cHwnds);
|
|
if (S_OK == hr)
|
|
{
|
|
if (cHwnds)
|
|
{
|
|
ShowWindow(aHwnds[0], SW_RESTORE);
|
|
ShowWindow(hWnd, SW_HIDE);
|
|
SetForegroundWindow(aHwnds[0]);
|
|
HeapFree(g_hHeap, 0, aHwnds);
|
|
}
|
|
}
|
|
else if (S_FALSE == hr)
|
|
{
|
|
HeapFree(g_hHeap, 0, aHwnds);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case IDC_REFRESH:
|
|
{
|
|
PostMessage(hWnd, WM_SHOWWINDOW, TRUE, 0);
|
|
break;
|
|
}
|
|
|
|
// Handle tiling, minimizing, and cascading
|
|
|
|
case IDC_MINIMIZE:
|
|
{
|
|
HWND * aHwnds;
|
|
UINT cHwnds;
|
|
|
|
if (SUCCEEDED(GetWindowList(GetDlgItem(hWnd, IDC_TASKLIST), &aHwnds, &cHwnds) && cHwnds))
|
|
{
|
|
UINT i;
|
|
for(i = 0; i < cHwnds; i++)
|
|
{
|
|
ShowWindow(aHwnds[i], SW_SHOWMINNOACTIVE);
|
|
}
|
|
HeapFree(g_hHeap, 0, aHwnds);
|
|
SetForegroundWindow(hWnd);
|
|
}
|
|
break;
|
|
}
|
|
case IDC_ENDTASK:
|
|
{
|
|
HWND * aHwnds;
|
|
UINT cHwnds;
|
|
HRESULT hr = GetWindowList(GetDlgItem(hWnd, IDC_TASKLIST), &aHwnds, &cHwnds);
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
UINT i;
|
|
BOOL fForce = GetKeyState(VK_CONTROL) & ( 1 << 16) ? TRUE : FALSE;
|
|
for(i = 0; i < cHwnds; i++)
|
|
{
|
|
SetActiveWindow(aHwnds[i]);
|
|
EndTask(aHwnds[i], FALSE, fForce);
|
|
}
|
|
|
|
// If we've removed an app, we need to refresh the dialog, which we can do
|
|
// by "pretending" we've just been opened
|
|
|
|
if (cHwnds)
|
|
{
|
|
PostMessage(hWnd, WM_SHOWWINDOW, TRUE, 0);
|
|
}
|
|
|
|
HeapFree(g_hHeap, 0, aHwnds);
|
|
SetForegroundWindow(hWnd);
|
|
}
|
|
else if (S_FALSE == hr) // Nothing select, so free the list and
|
|
{ // do nothing
|
|
HeapFree(g_hHeap, 0, aHwnds);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case IDC_CASCADE:
|
|
case IDC_TILEVERTICALLY:
|
|
case IDC_TILEHORIZONTALLY:
|
|
{
|
|
HWND * aHwnds;
|
|
UINT cHwnds;
|
|
|
|
if (SUCCEEDED(GetWindowList(GetDlgItem(hWnd, IDC_TASKLIST), &aHwnds, &cHwnds) && cHwnds))
|
|
{
|
|
UINT i;
|
|
for(i = 0; i < cHwnds; i++)
|
|
{
|
|
ShowWindow(aHwnds[i], SW_RESTORE);
|
|
SetForegroundWindow(aHwnds[i]);
|
|
}
|
|
|
|
if (id == IDC_CASCADE)
|
|
{
|
|
CascadeWindows(GetDesktopWindow(),
|
|
0,
|
|
NULL,
|
|
cHwnds,
|
|
aHwnds);
|
|
}
|
|
else
|
|
{
|
|
TileWindows(GetDesktopWindow(),
|
|
id == IDC_TILEVERTICALLY ? MDITILE_VERTICAL : MDITILE_HORIZONTAL,
|
|
NULL,
|
|
cHwnds,
|
|
aHwnds);
|
|
}
|
|
|
|
HeapFree(g_hHeap, 0, aHwnds);
|
|
SetForegroundWindow(hWnd);
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Listbox messages
|
|
|
|
case IDC_TASKLIST:
|
|
{
|
|
switch (codeNotify)
|
|
{
|
|
case LBN_SELCHANGE:
|
|
{
|
|
SetControlStates(hWnd);
|
|
break;
|
|
}
|
|
case LBN_DBLCLK:
|
|
{
|
|
HWND * aHwnds;
|
|
UINT cHwnds;
|
|
HRESULT hr = GetWindowList(GetDlgItem(hWnd, IDC_TASKLIST), &aHwnds, &cHwnds);
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
if (cHwnds)
|
|
{
|
|
ShowWindow(aHwnds[0], SW_RESTORE);
|
|
SetForegroundWindow(aHwnds[0]);
|
|
HeapFree(g_hHeap, 0, aHwnds);
|
|
ShowWindow(hWnd, SW_HIDE);
|
|
}
|
|
}
|
|
else if (S_FALSE == hr)
|
|
{
|
|
HeapFree(g_hHeap, 0, aHwnds);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Always leave the switchto button as the default button so that if ENTER
|
|
// is pressed, we'll do switchto
|
|
|
|
SetDefButton(hWnd, IDC_SWITCHTO);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
|
|
// Function: SetControlStates
|
|
|
|
// Synopsis: Enables/Disables the appropriate dialog controls depending on
|
|
// whether or not anything is currently selected in the dialog
|
|
|
|
// History: 6-05-95 davepl NT Port
|
|
|
|
// Notes:
|
|
|
|
|
|
|
|
|
|
// This is the list of items that need to be updated
|
|
|
|
|
|
static const UINT aidItems[] =
|
|
{
|
|
// IDC_CASCADE,
|
|
// IDC_TILEHORIZONTALLY,
|
|
// IDC_TILEVERTICALLY,
|
|
IDC_ENDTASK,
|
|
IDC_IDLE,
|
|
// IDC_MINIMIZE,
|
|
IDC_SWITCHTO,
|
|
};
|
|
|
|
void SetControlStates(HWND hWnd)
|
|
{
|
|
UINT cSelected;
|
|
UINT i;
|
|
HWND hListBox = GetDlgItem(hWnd, IDC_TASKLIST);
|
|
if (NULL == hListBox)
|
|
{
|
|
return;
|
|
}
|
|
|
|
cSelected = ListBox_GetSelCount(hListBox);
|
|
|
|
for (i=0; i < ARRAYSIZE(aidItems); i++)
|
|
{
|
|
EnableWindow(GetDlgItem(hWnd, aidItems[i]), cSelected > 0 );
|
|
}
|
|
}
|
|
|
|
|
|
// Synopsis: Enumerates the active toplevel windows and adds an entry for
|
|
// each to the task list listbox
|
|
|
|
// History: 6-05-95 davepl NT Port
|
|
|
|
// Notes: Also called to refresh the listbox when tasks are killed
|
|
void OnShowWindow(HWND hWnd, BOOL fShow, UINT status)
|
|
{
|
|
HWND hwndNext;
|
|
HWND hwndLB = GetDlgItem(hWnd, IDC_TASKLIST);
|
|
|
|
// Empty the listbox...
|
|
while ((int)SendMessage(hwndLB, LB_DELETESTRING, 0, 0) != LB_ERR)
|
|
NULL;
|
|
|
|
// Enumerate top-level windows (what we call tasks around here)
|
|
hwndNext = GetWindow(hWnd, GW_HWNDFIRST);
|
|
while (hwndNext)
|
|
{
|
|
TCHAR szTitle[64] = { TEXT('\0') };
|
|
GetWindowText(hwndNext, szTitle, ARRAYSIZE(szTitle));
|
|
|
|
// Criteria for a window to be shown in the tasklist:
|
|
|
|
// 1) Must be top level
|
|
// 2) Must be unowned
|
|
// 3) Must not be the tasklist or "Program Manager" (Explorer)
|
|
// 4) Must be visible
|
|
if ((hwndNext != hWnd) &&
|
|
(IsWindowVisible(hwndNext)) &&
|
|
(lstrcmp(szTitle, TEXT("Program Manager"))) &&
|
|
(!GetWindow(hwndNext, GW_OWNER)))
|
|
{
|
|
TCHAR szWindowName[MAX_PATH];
|
|
if (GetWindowText(hwndNext, szWindowName, ARRAYSIZE(szWindowName)))
|
|
{
|
|
// Add the task name to the list box, and store its associated
|
|
// window as the item data of the next listbox entry
|
|
|
|
int iIndex = (int)SendMessage(hwndLB, LB_ADDSTRING, 0,
|
|
(LPARAM)(LPTSTR)szWindowName);
|
|
SendMessage(hwndLB, LB_SETITEMDATA, iIndex, (LPARAM)hwndNext);
|
|
}
|
|
}
|
|
|
|
hwndNext = GetWindow(hwndNext, GW_HWNDNEXT);
|
|
}
|
|
|
|
// Select the first item in the list box, and if that goes OK, enable
|
|
// the buttons on the dialog (otherwise, disable them as if no selection had been made)
|
|
SendMessage(hwndLB, LB_SETCURSEL, 0, 0);
|
|
SetControlStates(hWnd);
|
|
SetForegroundWindow(hWnd);
|
|
}
|
|
|
|
|
|
// Synopsis: Returns an array of HWNDS corresponding to the app selected
|
|
// in the listbox. Return S_OK for success, S_FALSE if nothing
|
|
// is selected, and failure codes otherwise. If nothing is
|
|
// selected, we return a list of all windows.
|
|
|
|
// History: 6-05-95 davepl NT Port
|
|
HRESULT GetWindowList(HWND hwndTasklist, HWND ** paHwnds, UINT * pcHwnds)
|
|
{
|
|
BOOL fSucceeded;
|
|
INT * aItems = NULL;
|
|
UINT cSelected = ListBox_GetSelCount(hwndTasklist);
|
|
|
|
// NULL out in case of error
|
|
|
|
*paHwnds = NULL;
|
|
*pcHwnds = 0;
|
|
|
|
// If nothing is selected, we return the list of all windows
|
|
|
|
if (0 == cSelected)
|
|
{
|
|
UINT i;
|
|
UINT cItems = ListBox_GetCount(hwndTasklist);
|
|
HWND * aHwnds;
|
|
|
|
if (cItems == 0)
|
|
{
|
|
return E_FAIL; // Nothing even in listbox, call it an "error"
|
|
}
|
|
|
|
aHwnds = (HWND *) HeapAlloc(g_hHeap, 0, cItems * sizeof(HWND));
|
|
if (NULL == aHwnds)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
for (i = 0; i < cItems; i++)
|
|
{
|
|
HWND hwnd = (HWND) ListBox_GetItemData(hwndTasklist, i);
|
|
if (hwnd != (HWND) LB_ERR)
|
|
{
|
|
aHwnds[i] = hwnd;
|
|
}
|
|
else
|
|
{
|
|
HeapFree(g_hHeap, 0, aHwnds);
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
|
|
*paHwnds = aHwnds;
|
|
*pcHwnds = cItems;
|
|
|
|
return S_FALSE; // S_FALSE == nothing selected, but got full list OK
|
|
}
|
|
|
|
// Grab the table of selected items and process it
|
|
fSucceeded = TRUE;
|
|
aItems = (INT *) HeapAlloc(g_hHeap, 0, cSelected * sizeof(INT));
|
|
if (NULL == aItems)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
if (LB_ERR != ListBox_GetSelItems(hwndTasklist, cSelected, aItems))
|
|
{
|
|
UINT i;
|
|
|
|
// Walk through the list and replace the indexes with the
|
|
// actual HWNDs associated with that listbox entry. We will
|
|
// reuse this index list and pass it back as the HWND list
|
|
|
|
for (i = 0; i < cSelected; i++)
|
|
{
|
|
// BUGBUG Assumes sizeof(HWND) == sizeof(INT)
|
|
|
|
aItems[i] = (INT) ListBox_GetItemData(hwndTasklist, aItems[i]);
|
|
if (LB_ERR == aItems[i])
|
|
{
|
|
fSucceeded = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If everything went OK, hand off this list, otherwise free it
|
|
|
|
if (fSucceeded)
|
|
{
|
|
*paHwnds = (HWND *) aItems;
|
|
*pcHwnds = cSelected;
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
HeapFree(g_hHeap, 0, aItems);
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
HeapFree(g_hHeap, 0, aItems);
|
|
return E_FAIL;
|
|
}
|
|
} |