2020-09-30 17:12:32 +02:00

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;
}
}