taskmgr: Fix includes order and avoid tchar.h and memory.h.
[wine] / programs / taskmgr / procpage.c
1 /*
2  *  ReactOS Task Manager
3  *
4  *  procpage.c
5  *
6  *  Copyright (C) 1999 - 2001  Brian Palmer  <brianp@reactos.org>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22
23 #include <ctype.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26
27 #include <windows.h>
28 #include <commctrl.h>
29 #include <winnt.h>
30
31 #include "taskmgr.h"
32 #include "perfdata.h"
33 #include "column.h"
34
35 HWND hProcessPage;                        /* Process List Property Page */
36
37 HWND hProcessPageListCtrl;                /* Process ListCtrl Window */
38 HWND hProcessPageHeaderCtrl;            /* Process Header Control */
39 HWND hProcessPageEndProcessButton;        /* Process End Process button */
40 HWND hProcessPageShowAllProcessesButton;/* Process Show All Processes checkbox */
41
42 static int    nProcessPageWidth;
43 static int    nProcessPageHeight;
44
45 static HANDLE    hProcessPageEvent = NULL;    /* When this event becomes signaled then we refresh the process list */
46
47
48 static void CommaSeparateNumberString(LPWSTR strNumber, int nMaxCount)
49 {
50     WCHAR    temp[260];
51     UINT    i, j, k;
52     int len = lstrlenW(strNumber);
53
54     for (i=0; i < len % 3; i++)
55         temp[i] = strNumber[i];
56     for (k=0,j=i; i < len; i++,j++,k++) {
57         if ((k % 3 == 0) && (j > 0))
58             temp[j++] = ',';
59         temp[j] = strNumber[i];
60     }
61     temp[j++] = 0;
62     memcpy(strNumber, temp, min(nMaxCount, j) * sizeof(WCHAR));
63 }
64
65 static void ProcessPageShowContextMenu(DWORD dwProcessId)
66 {
67     HMENU        hMenu;
68     HMENU        hSubMenu;
69     HMENU        hPriorityMenu;
70     POINT        pt;
71     SYSTEM_INFO  si;
72     HANDLE       hProcess;
73     DWORD        dwProcessPriorityClass;
74     WCHAR        strDebugger[260];
75     DWORD        dwDebuggerSize;
76     HKEY         hKey;
77     UINT         Idx;
78     static const WCHAR wszAeDebugRegPath[] = {
79         'S','o','f','t','w','a','r','e','\\',
80         'M','i','c','r','o','s','o','f','t','\\',
81         'W','i','n','d','o','w','s',' ','N','T','\\',
82         'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
83         'A','e','D','e','b','u','g',0};
84
85     memset(&si, 0, sizeof(SYSTEM_INFO));
86
87     GetCursorPos(&pt);
88     GetSystemInfo(&si);
89
90     hMenu = LoadMenuW(hInst, MAKEINTRESOURCEW(IDR_PROCESS_PAGE_CONTEXT));
91     hSubMenu = GetSubMenu(hMenu, 0);
92     hPriorityMenu = GetSubMenu(hSubMenu, 4);
93
94     hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwProcessId);
95     dwProcessPriorityClass = GetPriorityClass(hProcess);
96     CloseHandle(hProcess);
97
98     if (si.dwNumberOfProcessors < 2)
99         RemoveMenu(hSubMenu, ID_PROCESS_PAGE_SETAFFINITY, MF_BYCOMMAND);
100     
101     if (!AreDebugChannelsSupported())
102         RemoveMenu(hSubMenu, ID_PROCESS_PAGE_DEBUGCHANNELS, MF_BYCOMMAND);
103
104     switch (dwProcessPriorityClass)    {
105     case REALTIME_PRIORITY_CLASS:
106         CheckMenuRadioItem(hPriorityMenu, ID_PROCESS_PAGE_SETPRIORITY_REALTIME, ID_PROCESS_PAGE_SETPRIORITY_LOW, ID_PROCESS_PAGE_SETPRIORITY_REALTIME, MF_BYCOMMAND);
107         break;
108     case HIGH_PRIORITY_CLASS:
109         CheckMenuRadioItem(hPriorityMenu, ID_PROCESS_PAGE_SETPRIORITY_REALTIME, ID_PROCESS_PAGE_SETPRIORITY_LOW, ID_PROCESS_PAGE_SETPRIORITY_HIGH, MF_BYCOMMAND);
110         break;
111     case ABOVE_NORMAL_PRIORITY_CLASS:
112         CheckMenuRadioItem(hPriorityMenu, ID_PROCESS_PAGE_SETPRIORITY_REALTIME, ID_PROCESS_PAGE_SETPRIORITY_LOW, ID_PROCESS_PAGE_SETPRIORITY_ABOVENORMAL, MF_BYCOMMAND);
113         break;
114     case NORMAL_PRIORITY_CLASS:
115         CheckMenuRadioItem(hPriorityMenu, ID_PROCESS_PAGE_SETPRIORITY_REALTIME, ID_PROCESS_PAGE_SETPRIORITY_LOW, ID_PROCESS_PAGE_SETPRIORITY_NORMAL, MF_BYCOMMAND);
116         break;
117     case BELOW_NORMAL_PRIORITY_CLASS:
118         CheckMenuRadioItem(hPriorityMenu, ID_PROCESS_PAGE_SETPRIORITY_REALTIME, ID_PROCESS_PAGE_SETPRIORITY_LOW, ID_PROCESS_PAGE_SETPRIORITY_BELOWNORMAL, MF_BYCOMMAND);
119         break;
120     case IDLE_PRIORITY_CLASS:
121         CheckMenuRadioItem(hPriorityMenu, ID_PROCESS_PAGE_SETPRIORITY_REALTIME, ID_PROCESS_PAGE_SETPRIORITY_LOW, ID_PROCESS_PAGE_SETPRIORITY_LOW, MF_BYCOMMAND);
122         break;
123     }
124
125     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszAeDebugRegPath, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
126     {
127         static const WCHAR wszDebugger[] = {'D','e','b','u','g','g','e','r',0};
128         dwDebuggerSize = 260;
129         if (RegQueryValueExW(hKey, wszDebugger, NULL, NULL, (LPBYTE)strDebugger, &dwDebuggerSize) == ERROR_SUCCESS)
130         {
131             static const WCHAR wszDRWTSN32[] = {'D','R','W','T','S','N','3','2',0};
132             for (Idx=0; Idx < lstrlenW(strDebugger); Idx++)
133                 strDebugger[Idx] = toupper(strDebugger[Idx]);
134
135             if (wcsstr(strDebugger, wszDRWTSN32))
136                 EnableMenuItem(hSubMenu, ID_PROCESS_PAGE_DEBUG, MF_BYCOMMAND|MF_DISABLED|MF_GRAYED);
137         }
138         else
139             EnableMenuItem(hSubMenu, ID_PROCESS_PAGE_DEBUG, MF_BYCOMMAND|MF_DISABLED|MF_GRAYED);
140
141         RegCloseKey(hKey);
142     } else {
143         EnableMenuItem(hSubMenu, ID_PROCESS_PAGE_DEBUG, MF_BYCOMMAND|MF_DISABLED|MF_GRAYED);
144     }
145     TrackPopupMenu(hSubMenu, TPM_LEFTALIGN|TPM_TOPALIGN|TPM_LEFTBUTTON, pt.x, pt.y, 0, hMainWnd, NULL);
146     DestroyMenu(hMenu);
147 }
148
149 static void ProcessPageOnNotify(LPARAM lParam)
150 {
151     LPNMHDR            pnmh;
152     NMLVDISPINFOW*     pnmdi;
153     LVITEMW            lvitem;
154     ULONG              Index, Count;
155     ULONG              ColumnIndex;
156     IO_COUNTERS        iocounters;
157     TIME               time;
158     static const WCHAR wszFmtD[] = {'%','d',0};
159     static const WCHAR wszFmt02D[] = {'%','0','2','d',0};
160     static const WCHAR wszUnitK[] = {' ','K',0};
161
162     pnmh = (LPNMHDR) lParam;
163     pnmdi = (NMLVDISPINFOW*) lParam;
164
165     if (pnmh->hwndFrom == hProcessPageListCtrl)
166     {
167         switch (pnmh->code)
168         {
169 #if 0
170         case LVN_ITEMCHANGED:
171             ProcessPageUpdate();
172             break;
173 #endif
174             
175         case LVN_GETDISPINFOW:
176
177             if (!(pnmdi->item.mask & LVIF_TEXT))
178                 break;
179             
180             ColumnIndex = pnmdi->item.iSubItem;
181             Index = pnmdi->item.iItem;
182
183             if (ColumnDataHints[ColumnIndex] == COLUMN_IMAGENAME)
184                 PerfDataGetImageName(Index, pnmdi->item.pszText, pnmdi->item.cchTextMax);
185             if (ColumnDataHints[ColumnIndex] == COLUMN_PID)
186                 wsprintfW(pnmdi->item.pszText, wszFmtD, PerfDataGetProcessId(Index));
187             if (ColumnDataHints[ColumnIndex] == COLUMN_USERNAME)
188                 PerfDataGetUserName(Index, pnmdi->item.pszText, pnmdi->item.cchTextMax);
189             if (ColumnDataHints[ColumnIndex] == COLUMN_SESSIONID)
190                 wsprintfW(pnmdi->item.pszText, wszFmtD, PerfDataGetSessionId(Index));
191             if (ColumnDataHints[ColumnIndex] == COLUMN_CPUUSAGE)
192                 wsprintfW(pnmdi->item.pszText, wszFmt02D, PerfDataGetCPUUsage(Index));
193             if (ColumnDataHints[ColumnIndex] == COLUMN_CPUTIME)
194             {
195                 DWORD dwHours;
196                 DWORD dwMinutes;
197                 DWORD dwSeconds;
198                 ULONGLONG secs;
199                 static const WCHAR timefmt[] = {'%','d',':','%','0','2','d',':','%','0','2','d',0};
200
201                 time = PerfDataGetCPUTime(Index);
202                 secs = time.QuadPart / 10000000;
203                 dwHours = secs / 3600;
204                 dwMinutes = (secs % 3600) / 60;
205                 dwSeconds = (secs % 3600) % 60;
206                 wsprintfW(pnmdi->item.pszText, timefmt, dwHours, dwMinutes, dwSeconds);
207             }
208             if (ColumnDataHints[ColumnIndex] == COLUMN_MEMORYUSAGE)
209             {
210                 wsprintfW(pnmdi->item.pszText, wszFmtD, PerfDataGetWorkingSetSizeBytes(Index) / 1024);
211                 CommaSeparateNumberString(pnmdi->item.pszText, pnmdi->item.cchTextMax);
212                 wcscat(pnmdi->item.pszText, wszUnitK);
213             }
214             if (ColumnDataHints[ColumnIndex] == COLUMN_PEAKMEMORYUSAGE)
215             {
216                 wsprintfW(pnmdi->item.pszText, wszFmtD, PerfDataGetPeakWorkingSetSizeBytes(Index) / 1024);
217                 CommaSeparateNumberString(pnmdi->item.pszText, pnmdi->item.cchTextMax);
218                 wcscat(pnmdi->item.pszText, wszUnitK);
219             }
220             if (ColumnDataHints[ColumnIndex] == COLUMN_MEMORYUSAGEDELTA)
221             {
222                 wsprintfW(pnmdi->item.pszText, wszFmtD, PerfDataGetWorkingSetSizeDelta(Index) / 1024);
223                 CommaSeparateNumberString(pnmdi->item.pszText, pnmdi->item.cchTextMax);
224                 wcscat(pnmdi->item.pszText, wszUnitK);
225             }
226             if (ColumnDataHints[ColumnIndex] == COLUMN_PAGEFAULTS)
227             {
228                 wsprintfW(pnmdi->item.pszText, wszFmtD, PerfDataGetPageFaultCount(Index));
229                 CommaSeparateNumberString(pnmdi->item.pszText, pnmdi->item.cchTextMax);
230             }
231             if (ColumnDataHints[ColumnIndex] == COLUMN_PAGEFAULTSDELTA)
232             {
233                 wsprintfW(pnmdi->item.pszText, wszFmtD, PerfDataGetPageFaultCountDelta(Index));
234                 CommaSeparateNumberString(pnmdi->item.pszText, pnmdi->item.cchTextMax);
235             }
236             if (ColumnDataHints[ColumnIndex] == COLUMN_VIRTUALMEMORYSIZE)
237             {
238                 wsprintfW(pnmdi->item.pszText, wszFmtD, PerfDataGetVirtualMemorySizeBytes(Index) / 1024);
239                 CommaSeparateNumberString(pnmdi->item.pszText, pnmdi->item.cchTextMax);
240                 wcscat(pnmdi->item.pszText, wszUnitK);
241             }
242             if (ColumnDataHints[ColumnIndex] == COLUMN_PAGEDPOOL)
243             {
244                 wsprintfW(pnmdi->item.pszText, wszFmtD, PerfDataGetPagedPoolUsagePages(Index) / 1024);
245                 CommaSeparateNumberString(pnmdi->item.pszText, pnmdi->item.cchTextMax);
246                 wcscat(pnmdi->item.pszText, wszUnitK);
247             }
248             if (ColumnDataHints[ColumnIndex] == COLUMN_NONPAGEDPOOL)
249             {
250                 wsprintfW(pnmdi->item.pszText, wszFmtD, PerfDataGetNonPagedPoolUsagePages(Index) / 1024);
251                 CommaSeparateNumberString(pnmdi->item.pszText, pnmdi->item.cchTextMax);
252                 wcscat(pnmdi->item.pszText, wszUnitK);
253             }
254             if (ColumnDataHints[ColumnIndex] == COLUMN_BASEPRIORITY)
255                 wsprintfW(pnmdi->item.pszText, wszFmtD, PerfDataGetBasePriority(Index));
256             if (ColumnDataHints[ColumnIndex] == COLUMN_HANDLECOUNT)
257             {
258                 wsprintfW(pnmdi->item.pszText, wszFmtD, PerfDataGetHandleCount(Index));
259                 CommaSeparateNumberString(pnmdi->item.pszText, pnmdi->item.cchTextMax);
260             }
261             if (ColumnDataHints[ColumnIndex] == COLUMN_THREADCOUNT)
262             {
263                 wsprintfW(pnmdi->item.pszText, wszFmtD, PerfDataGetThreadCount(Index));
264                 CommaSeparateNumberString(pnmdi->item.pszText, pnmdi->item.cchTextMax);
265             }
266             if (ColumnDataHints[ColumnIndex] == COLUMN_USEROBJECTS)
267             {
268                 wsprintfW(pnmdi->item.pszText, wszFmtD, PerfDataGetUSERObjectCount(Index));
269                 CommaSeparateNumberString(pnmdi->item.pszText, pnmdi->item.cchTextMax);
270             }
271             if (ColumnDataHints[ColumnIndex] == COLUMN_GDIOBJECTS)
272             {
273                 wsprintfW(pnmdi->item.pszText, wszFmtD, PerfDataGetGDIObjectCount(Index));
274                 CommaSeparateNumberString(pnmdi->item.pszText, pnmdi->item.cchTextMax);
275             }
276             if (ColumnDataHints[ColumnIndex] == COLUMN_IOREADS)
277             {
278                 PerfDataGetIOCounters(Index, &iocounters);
279                 /* wsprintfW(pnmdi->item.pszText, wszFmtD, iocounters.ReadOperationCount); */
280                 _ui64tow(iocounters.ReadOperationCount, pnmdi->item.pszText, 10);
281                 CommaSeparateNumberString(pnmdi->item.pszText, pnmdi->item.cchTextMax);
282             }
283             if (ColumnDataHints[ColumnIndex] == COLUMN_IOWRITES)
284             {
285                 PerfDataGetIOCounters(Index, &iocounters);
286                 /* wsprintfW(pnmdi->item.pszText, wszFmtD, iocounters.WriteOperationCount); */
287                 _ui64tow(iocounters.WriteOperationCount, pnmdi->item.pszText, 10);
288                 CommaSeparateNumberString(pnmdi->item.pszText, pnmdi->item.cchTextMax);
289             }
290             if (ColumnDataHints[ColumnIndex] == COLUMN_IOOTHER)
291             {
292                 PerfDataGetIOCounters(Index, &iocounters);
293                 /* wsprintfW(pnmdi->item.pszText, wszFmtD, iocounters.OtherOperationCount); */
294                 _ui64tow(iocounters.OtherOperationCount, pnmdi->item.pszText, 10);
295                 CommaSeparateNumberString(pnmdi->item.pszText, pnmdi->item.cchTextMax);
296             }
297             if (ColumnDataHints[ColumnIndex] == COLUMN_IOREADBYTES)
298             {
299                 PerfDataGetIOCounters(Index, &iocounters);
300                 /* wsprintfW(pnmdi->item.pszText, wszFmtD, iocounters.ReadTransferCount); */
301                 _ui64tow(iocounters.ReadTransferCount, pnmdi->item.pszText, 10);
302                 CommaSeparateNumberString(pnmdi->item.pszText, pnmdi->item.cchTextMax);
303             }
304             if (ColumnDataHints[ColumnIndex] == COLUMN_IOWRITEBYTES)
305             {
306                 PerfDataGetIOCounters(Index, &iocounters);
307                 /* wsprintfW(pnmdi->item.pszText, wszFmtD, iocounters.WriteTransferCount); */
308                 _ui64tow(iocounters.WriteTransferCount, pnmdi->item.pszText, 10);
309                 CommaSeparateNumberString(pnmdi->item.pszText, pnmdi->item.cchTextMax);
310             }
311             if (ColumnDataHints[ColumnIndex] == COLUMN_IOOTHERBYTES)
312             {
313                 PerfDataGetIOCounters(Index, &iocounters);
314                 /* wsprintfW(pnmdi->item.pszText, wszFmtD, iocounters.OtherTransferCount); */
315                 _ui64tow(iocounters.OtherTransferCount, pnmdi->item.pszText, 10);
316                 CommaSeparateNumberString(pnmdi->item.pszText, pnmdi->item.cchTextMax);
317             }
318
319             break;
320
321         case NM_RCLICK:
322             Count = SendMessageW(hProcessPageListCtrl, LVM_GETITEMCOUNT, 0, 0);
323             for (Index=0; Index<Count; Index++)
324             {
325                 lvitem.mask = LVIF_STATE;
326                 lvitem.stateMask = LVIS_SELECTED;
327                 lvitem.iItem = Index;
328                 lvitem.iSubItem = 0;
329
330                 SendMessageW(hProcessPageListCtrl, LVM_GETITEMW, 0, (LPARAM) &lvitem);
331
332                 if (lvitem.state & LVIS_SELECTED)
333                     break;
334             }
335
336             if ((SendMessageW(hProcessPageListCtrl, LVM_GETSELECTEDCOUNT, 0, 0) == 1) &&
337                 (PerfDataGetProcessId(Index) != 0))
338             {
339                 ProcessPageShowContextMenu(PerfDataGetProcessId(Index));
340             }
341
342             break;
343
344         }
345     }
346     else if (pnmh->hwndFrom == hProcessPageHeaderCtrl)
347     {
348         switch (pnmh->code)
349         {
350         case HDN_ITEMCLICKW:
351
352             /*
353              * FIXME: Fix the column sorting
354              *
355              *ListView_SortItems(hApplicationPageListCtrl, ApplicationPageCompareFunc, NULL);
356              *bSortAscending = !bSortAscending;
357              */
358
359             break;
360
361         case HDN_ITEMCHANGEDW:
362
363             UpdateColumnDataHints();
364
365             break;
366
367         case HDN_ENDDRAG:
368
369             UpdateColumnDataHints();
370
371             break;
372
373         }
374     }
375
376 }
377
378 void RefreshProcessPage(void)
379 {
380     /* Signal the event so that our refresh thread */
381     /* will wake up and refresh the process page */
382     SetEvent(hProcessPageEvent);
383 }
384
385 static DWORD WINAPI ProcessPageRefreshThread(void *lpParameter)
386 {
387     ULONG    OldProcessorUsage = 0;
388     ULONG    OldProcessCount = 0;
389
390     WCHAR    wszCPU_Usage[255];
391     WCHAR    wszProcesses[255];
392
393     LoadStringW(hInst, IDS_STATUS_BAR_CPU_USAGE, wszCPU_Usage, sizeof(wszCPU_Usage)/sizeof(WCHAR));
394     LoadStringW(hInst, IDS_STATUS_BAR_PROCESSES, wszProcesses, sizeof(wszProcesses)/sizeof(WCHAR));
395
396     /* Create the event */
397     hProcessPageEvent = CreateEventW(NULL, TRUE, TRUE, NULL);
398
399     /* If we couldn't create the event then exit the thread */
400     if (!hProcessPageEvent)
401         return 0;
402
403     while (1) {
404         DWORD    dwWaitVal;
405
406         /* Wait on the event */
407         dwWaitVal = WaitForSingleObject(hProcessPageEvent, INFINITE);
408
409         /* If the wait failed then the event object must have been */
410         /* closed and the task manager is exiting so exit this thread */
411         if (dwWaitVal == WAIT_FAILED)
412             return 0;
413
414         if (dwWaitVal == WAIT_OBJECT_0) {
415             WCHAR    text[256];
416
417             /* Reset our event */
418             ResetEvent(hProcessPageEvent);
419
420             if (SendMessageW(hProcessPageListCtrl, LVM_GETITEMCOUNT, 0, 0) != PerfDataGetProcessCount())
421                 SendMessageW(hProcessPageListCtrl, LVM_SETITEMCOUNT, PerfDataGetProcessCount(), /*LVSICF_NOINVALIDATEALL|*/LVSICF_NOSCROLL);
422
423             if (IsWindowVisible(hProcessPage))
424                 InvalidateRect(hProcessPageListCtrl, NULL, FALSE);
425
426             if (OldProcessorUsage != PerfDataGetProcessorUsage()) {
427                 OldProcessorUsage = PerfDataGetProcessorUsage();
428                 wsprintfW(text, wszCPU_Usage, OldProcessorUsage);
429                 SendMessageW(hStatusWnd, SB_SETTEXTW, 1, (LPARAM)text);
430             }
431             if (OldProcessCount != PerfDataGetProcessCount()) {
432                 OldProcessCount = PerfDataGetProcessCount();
433                 wsprintfW(text, wszProcesses, OldProcessCount);
434                 SendMessageW(hStatusWnd, SB_SETTEXTW, 0, (LPARAM)text);
435             }
436         }
437     }
438         return 0;
439 }
440
441 INT_PTR CALLBACK
442 ProcessPageWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
443 {
444     RECT    rc;
445     int        nXDifference;
446     int        nYDifference;
447     int        cx, cy;
448     DWORD      extended_styles;
449
450     switch (message) {
451     case WM_INITDIALOG:
452         /*
453          * Save the width and height
454          */
455         GetClientRect(hDlg, &rc);
456         nProcessPageWidth = rc.right;
457         nProcessPageHeight = rc.bottom;
458
459         /* Update window position */
460         SetWindowPos(hDlg, NULL, 15, 30, 0, 0, SWP_NOACTIVATE|SWP_NOOWNERZORDER|SWP_NOSIZE|SWP_NOZORDER);
461
462         /*
463          * Get handles to the controls
464          */
465         hProcessPageListCtrl = GetDlgItem(hDlg, IDC_PROCESSLIST);
466         hProcessPageHeaderCtrl = (HWND)SendMessageW(hProcessPageListCtrl, LVM_GETHEADER, 0, 0);
467         hProcessPageEndProcessButton = GetDlgItem(hDlg, IDC_ENDPROCESS);
468         hProcessPageShowAllProcessesButton = GetDlgItem(hDlg, IDC_SHOWALLPROCESSES);
469
470         /*
471          * Set the extended window styles for the list control
472          */
473         extended_styles = SendMessageW(hProcessPageListCtrl, LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
474         SendMessageW(hProcessPageListCtrl, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, extended_styles | LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP);
475
476         AddColumns();
477
478         /*
479          * Subclass the process list control so we can intercept WM_ERASEBKGND
480          */
481         OldProcessListWndProc = (WNDPROC)SetWindowLongPtrW(hProcessPageListCtrl, GWLP_WNDPROC, (LONG_PTR)ProcessListWndProc);
482
483         /* Start our refresh thread */
484         CloseHandle( CreateThread(NULL, 0, ProcessPageRefreshThread, NULL, 0, NULL));
485
486         return TRUE;
487
488     case WM_DESTROY:
489         /* Close the event handle, this will make the */
490         /* refresh thread exit when the wait fails */
491         CloseHandle(hProcessPageEvent);
492
493         SaveColumnSettings();
494
495         break;
496
497     case WM_COMMAND:
498         /* Handle the button clicks */
499         switch (LOWORD(wParam))
500         {
501                 case IDC_ENDPROCESS:
502                         ProcessPage_OnEndProcess();
503         }
504         break;
505
506     case WM_SIZE:
507         if (wParam == SIZE_MINIMIZED)
508             return 0;
509
510         cx = LOWORD(lParam);
511         cy = HIWORD(lParam);
512         nXDifference = cx - nProcessPageWidth;
513         nYDifference = cy - nProcessPageHeight;
514         nProcessPageWidth = cx;
515         nProcessPageHeight = cy;
516
517         /* Reposition the application page's controls */
518         GetWindowRect(hProcessPageListCtrl, &rc);
519         cx = (rc.right - rc.left) + nXDifference;
520         cy = (rc.bottom - rc.top) + nYDifference;
521         SetWindowPos(hProcessPageListCtrl, NULL, 0, 0, cx, cy, SWP_NOACTIVATE|SWP_NOOWNERZORDER|SWP_NOMOVE|SWP_NOZORDER);
522         InvalidateRect(hProcessPageListCtrl, NULL, TRUE);
523         
524         GetClientRect(hProcessPageEndProcessButton, &rc);
525         MapWindowPoints(hProcessPageEndProcessButton, hDlg, (LPPOINT)(&rc), (sizeof(RECT)/sizeof(POINT)) );
526            cx = rc.left + nXDifference;
527         cy = rc.top + nYDifference;
528         SetWindowPos(hProcessPageEndProcessButton, NULL, cx, cy, 0, 0, SWP_NOACTIVATE|SWP_NOOWNERZORDER|SWP_NOSIZE|SWP_NOZORDER);
529         InvalidateRect(hProcessPageEndProcessButton, NULL, TRUE);
530         
531         GetClientRect(hProcessPageShowAllProcessesButton, &rc);
532         MapWindowPoints(hProcessPageShowAllProcessesButton, hDlg, (LPPOINT)(&rc), (sizeof(RECT)/sizeof(POINT)) );
533            cx = rc.left;
534         cy = rc.top + nYDifference;
535         SetWindowPos(hProcessPageShowAllProcessesButton, NULL, cx, cy, 0, 0, SWP_NOACTIVATE|SWP_NOOWNERZORDER|SWP_NOSIZE|SWP_NOZORDER);
536         InvalidateRect(hProcessPageShowAllProcessesButton, NULL, TRUE);
537
538         break;
539
540     case WM_NOTIFY:
541         ProcessPageOnNotify(lParam);
542         break;
543     }
544
545     return 0;
546 }