Process Hacker
termator.c
Go to the documentation of this file.
1 /*
2  * Process Hacker -
3  * process termination tool
4  *
5  * Copyright (C) 2010-2011 wj32
6  *
7  * This file is part of Process Hacker.
8  *
9  * Process Hacker is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * Process Hacker is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include <phapp.h>
24 #include <kphuser.h>
25 
26 typedef NTSTATUS (NTAPI *_NtGetNextProcess)(
27  _In_ HANDLE ProcessHandle,
28  _In_ ACCESS_MASK DesiredAccess,
29  _In_ ULONG HandleAttributes,
30  _In_ ULONG Flags,
31  _Out_ PHANDLE NewProcessHandle
32  );
33 
34 typedef NTSTATUS (NTAPI *_NtGetNextThread)(
35  _In_ HANDLE ProcessHandle,
36  _In_ HANDLE ThreadHandle,
37  _In_ ACCESS_MASK DesiredAccess,
38  _In_ ULONG HandleAttributes,
39  _In_ ULONG Flags,
40  _Out_ PHANDLE NewThreadHandle
41  );
42 
43 #define CROSS_INDEX 0
44 #define TICK_INDEX 1
45 
46 typedef NTSTATUS (NTAPI *PTEST_PROC)(
47  _In_ HANDLE ProcessId
48  );
49 
50 typedef struct _TEST_ITEM
51 {
52  PWSTR Id;
53  PWSTR Description;
54  PTEST_PROC TestProc;
56 
57 INT_PTR CALLBACK PhpProcessTerminatorDlgProc(
58  _In_ HWND hwndDlg,
59  _In_ UINT uMsg,
60  _In_ WPARAM wParam,
61  _In_ LPARAM lParam
62  );
63 
65  _In_ HWND ParentWindowHandle,
66  _In_ PPH_PROCESS_ITEM ProcessItem
67  )
68 {
69  NTSTATUS status;
70  HANDLE processHandle;
71  HANDLE debugObjectHandle;
72 
74  &processHandle,
76  ProcessItem->ProcessId
77  )))
78  {
80  processHandle,
81  &debugObjectHandle
82  )))
83  {
84  if (PhShowMessage(
85  ParentWindowHandle,
86  MB_ICONWARNING | MB_YESNO,
87  L"The selected process is currently being debugged, which can prevent it from being terminated. "
88  L"Do you want to detach the process from its debugger?"
89  ) == IDYES)
90  {
91  ULONG flags;
92 
93  // Disable kill-on-close.
94  flags = 0;
96  debugObjectHandle,
98  &flags,
99  sizeof(ULONG),
100  NULL
101  );
102 
103  if (!NT_SUCCESS(status = NtRemoveProcessDebug(processHandle, debugObjectHandle)))
104  PhShowStatus(ParentWindowHandle, L"Unable to detach the process", status, 0);
105  }
106 
107  NtClose(debugObjectHandle);
108  }
109 
110  NtClose(processHandle);
111  }
112 
113  DialogBoxParam(
115  MAKEINTRESOURCE(IDD_TERMINATOR),
116  ParentWindowHandle,
118  (LPARAM)ProcessItem
119  );
120 }
121 
122 static PVOID GetExitProcessFunction(
123  VOID
124  )
125 {
126  // Vista and above export.
128  return PhGetModuleProcAddress(L"ntdll.dll", "RtlExitUserProcess");
129  else
130  return PhGetModuleProcAddress(L"kernel32.dll", "ExitProcess");
131 }
132 
133 static NTSTATUS NTAPI TerminatorTP1(
134  _In_ HANDLE ProcessId
135  )
136 {
137  NTSTATUS status;
138  HANDLE processHandle;
139 
140  if (NT_SUCCESS(status = PhOpenProcess(
141  &processHandle,
143  ProcessId
144  )))
145  {
146  // Don't use KPH.
147  status = NtTerminateProcess(processHandle, STATUS_SUCCESS);
148 
149  NtClose(processHandle);
150  }
151 
152  return status;
153 }
154 
155 static NTSTATUS NTAPI TerminatorTP2(
156  _In_ HANDLE ProcessId
157  )
158 {
159  NTSTATUS status;
160  HANDLE processHandle;
161 
162  if (NT_SUCCESS(status = PhOpenProcess(
163  &processHandle,
165  ProcessId
166  )))
167  {
168  status = RtlCreateUserThread(
169  processHandle,
170  NULL,
171  FALSE,
172  0,
173  0,
174  0,
175  (PUSER_THREAD_START_ROUTINE)GetExitProcessFunction(),
176  NULL,
177  NULL,
178  NULL
179  );
180 
181  NtClose(processHandle);
182  }
183 
184  return status;
185 }
186 
187 static NTSTATUS NTAPI TerminatorTTGeneric(
188  _In_ HANDLE ProcessId,
189  _In_ BOOLEAN UseKph,
190  _In_ BOOLEAN UseKphDangerous
191  )
192 {
193  NTSTATUS status;
194  PVOID processes;
196  ULONG i;
197 
198  if ((UseKph || UseKphDangerous) && !KphIsConnected())
199  return STATUS_NOT_SUPPORTED;
200 
201  if (!NT_SUCCESS(status = PhEnumProcesses(&processes)))
202  return status;
203 
204  process = PhFindProcessInformation(processes, ProcessId);
205 
206  if (!process)
207  {
208  PhFree(processes);
209  return STATUS_INVALID_CID;
210  }
211 
212  for (i = 0; i < process->NumberOfThreads; i++)
213  {
214  HANDLE threadHandle;
215 
217  &threadHandle,
218  THREAD_TERMINATE,
219  process->Threads[i].ClientId.UniqueThread
220  )))
221  {
222  if (UseKphDangerous)
223  KphTerminateThreadUnsafe(threadHandle, STATUS_SUCCESS);
224  else if (UseKph)
225  KphTerminateThread(threadHandle, STATUS_SUCCESS);
226  else
227  NtTerminateThread(threadHandle, STATUS_SUCCESS);
228 
229  NtClose(threadHandle);
230  }
231  }
232 
233  PhFree(processes);
234 
235  return STATUS_SUCCESS;
236 }
237 
238 static NTSTATUS NTAPI TerminatorTT1(
239  _In_ HANDLE ProcessId
240  )
241 {
242  return TerminatorTTGeneric(ProcessId, FALSE, FALSE);
243 }
244 
245 static NTSTATUS NTAPI TerminatorTT2(
246  _In_ HANDLE ProcessId
247  )
248 {
249  NTSTATUS status;
250  PVOID processes;
252  ULONG i;
253  CONTEXT context;
254  PVOID exitProcess;
255 
256  exitProcess = GetExitProcessFunction();
257 
258  if (!NT_SUCCESS(status = PhEnumProcesses(&processes)))
259  return status;
260 
261  process = PhFindProcessInformation(processes, ProcessId);
262 
263  if (!process)
264  {
265  PhFree(processes);
266  return STATUS_INVALID_CID;
267  }
268 
269  for (i = 0; i < process->NumberOfThreads; i++)
270  {
271  HANDLE threadHandle;
272 
274  &threadHandle,
275  THREAD_GET_CONTEXT | THREAD_SET_CONTEXT,
276  process->Threads[i].ClientId.UniqueThread
277  )))
278  {
279 #ifdef _M_IX86
280  context.ContextFlags = CONTEXT_CONTROL;
281  PhGetThreadContext(threadHandle, &context);
282  context.Eip = (ULONG)exitProcess;
283  PhSetThreadContext(threadHandle, &context);
284 #else
285  context.ContextFlags = CONTEXT_CONTROL;
286  PhGetThreadContext(threadHandle, &context);
287  context.Rip = (ULONG64)exitProcess;
288  PhSetThreadContext(threadHandle, &context);
289 #endif
290 
291  NtClose(threadHandle);
292  }
293  }
294 
295  PhFree(processes);
296 
297  return STATUS_SUCCESS;
298 }
299 
300 static NTSTATUS NTAPI TerminatorTP1a(
301  _In_ HANDLE ProcessId
302  )
303 {
304  NTSTATUS status;
305  _NtGetNextProcess ntGetNextProcess;
306  HANDLE processHandle = NtCurrentProcess();
307  ULONG i;
308 
309  ntGetNextProcess = PhGetModuleProcAddress(L"ntdll.dll", "NtGetNextProcess");
310 
311  if (!ntGetNextProcess)
312  return STATUS_NOT_SUPPORTED;
313 
314  if (!NT_SUCCESS(status = ntGetNextProcess(
315  NtCurrentProcess(),
317  0,
318  0,
319  &processHandle
320  )))
321  return status;
322 
323  for (i = 0; i < 1000; i++) // make sure we don't go into an infinite loop or something
324  {
325  HANDLE newProcessHandle;
326  PROCESS_BASIC_INFORMATION basicInfo;
327 
328  if (NT_SUCCESS(PhGetProcessBasicInformation(processHandle, &basicInfo)))
329  {
330  if (basicInfo.UniqueProcessId == ProcessId)
331  {
332  PhTerminateProcess(processHandle, STATUS_SUCCESS);
333  break;
334  }
335  }
336 
337  if (NT_SUCCESS(status = ntGetNextProcess(
338  processHandle,
340  0,
341  0,
342  &newProcessHandle
343  )))
344  {
345  NtClose(processHandle);
346  processHandle = newProcessHandle;
347  }
348  else
349  {
350  NtClose(processHandle);
351  break;
352  }
353  }
354 
355  return status;
356 }
357 
358 static NTSTATUS NTAPI TerminatorTT1a(
359  _In_ HANDLE ProcessId
360  )
361 {
362  NTSTATUS status;
363  _NtGetNextThread ntGetNextThread;
364  HANDLE processHandle;
365  HANDLE threadHandle;
366  ULONG i;
367 
368  ntGetNextThread = PhGetModuleProcAddress(L"ntdll.dll", "NtGetNextThread");
369 
370  if (!ntGetNextThread)
371  return STATUS_NOT_SUPPORTED;
372 
373  if (NT_SUCCESS(status = PhOpenProcess(
374  &processHandle,
375  PROCESS_QUERY_INFORMATION, // NtGetNextThread actually requires this access for some reason
376  ProcessId
377  )))
378  {
379  if (!NT_SUCCESS(status = ntGetNextThread(
380  processHandle,
381  NULL,
382  THREAD_TERMINATE,
383  0,
384  0,
385  &threadHandle
386  )))
387  {
388  NtClose(processHandle);
389  return status;
390  }
391 
392  for (i = 0; i < 1000; i++)
393  {
394  HANDLE newThreadHandle;
395 
396  PhTerminateThread(threadHandle, STATUS_SUCCESS);
397 
398  if (NT_SUCCESS(ntGetNextThread(
399  processHandle,
400  threadHandle,
401  THREAD_TERMINATE,
402  0,
403  0,
404  &newThreadHandle
405  )))
406  {
407  NtClose(threadHandle);
408  threadHandle = newThreadHandle;
409  }
410  else
411  {
412  NtClose(threadHandle);
413  break;
414  }
415  }
416 
417  NtClose(processHandle);
418  }
419 
420  return status;
421 }
422 
423 static NTSTATUS NTAPI TerminatorCH1(
424  _In_ HANDLE ProcessId
425  )
426 {
427  NTSTATUS status;
428  HANDLE processHandle;
429 
430  if (NT_SUCCESS(status = PhOpenProcess(
431  &processHandle,
432  PROCESS_DUP_HANDLE,
433  ProcessId
434  )))
435  {
436  ULONG i;
437 
438  for (i = 0; i < 0x1000; i += 4)
439  {
441  processHandle,
442  (HANDLE)i,
443  NULL,
444  NULL,
445  0,
446  0,
447  DUPLICATE_CLOSE_SOURCE
448  );
449  }
450 
451  NtClose(processHandle);
452  }
453 
454  return status;
455 }
456 
457 static BOOL CALLBACK DestroyProcessWindowsProc(
458  _In_ HWND hwnd,
459  _In_ LPARAM lParam
460  )
461 {
462  ULONG processId;
463 
464  GetWindowThreadProcessId(hwnd, &processId);
465 
466  if (processId == (ULONG)lParam)
467  {
468  PostMessage(hwnd, WM_DESTROY, 0, 0);
469  }
470 
471  return TRUE;
472 }
473 
474 static NTSTATUS NTAPI TerminatorW1(
475  _In_ HANDLE ProcessId
476  )
477 {
478  EnumWindows(DestroyProcessWindowsProc, (LPARAM)ProcessId);
479  return STATUS_SUCCESS;
480 }
481 
482 static BOOL CALLBACK QuitProcessWindowsProc(
483  _In_ HWND hwnd,
484  _In_ LPARAM lParam
485  )
486 {
487  ULONG processId;
488 
489  GetWindowThreadProcessId(hwnd, &processId);
490 
491  if (processId == (ULONG)lParam)
492  {
493  PostMessage(hwnd, WM_QUIT, 0, 0);
494  }
495 
496  return TRUE;
497 }
498 
499 static NTSTATUS NTAPI TerminatorW2(
500  _In_ HANDLE ProcessId
501  )
502 {
503  EnumWindows(QuitProcessWindowsProc, (LPARAM)ProcessId);
504  return STATUS_SUCCESS;
505 }
506 
507 static BOOL CALLBACK CloseProcessWindowsProc(
508  _In_ HWND hwnd,
509  _In_ LPARAM lParam
510  )
511 {
512  ULONG processId;
513 
514  GetWindowThreadProcessId(hwnd, &processId);
515 
516  if (processId == (ULONG)lParam)
517  {
518  PostMessage(hwnd, WM_CLOSE, 0, 0);
519  }
520 
521  return TRUE;
522 }
523 
524 static NTSTATUS NTAPI TerminatorW3(
525  _In_ HANDLE ProcessId
526  )
527 {
528  EnumWindows(CloseProcessWindowsProc, (LPARAM)ProcessId);
529  return STATUS_SUCCESS;
530 }
531 
532 static NTSTATUS NTAPI TerminatorTJ1(
533  _In_ HANDLE ProcessId
534  )
535 {
536  NTSTATUS status;
537  HANDLE processHandle;
538 
539  // TODO: Check if the process is already in a job.
540 
541  if (NT_SUCCESS(status = PhOpenProcess(
542  &processHandle,
544  ProcessId
545  )))
546  {
547  HANDLE jobHandle;
548 
549  status = NtCreateJobObject(&jobHandle, JOB_OBJECT_ALL_ACCESS, NULL);
550 
551  if (NT_SUCCESS(status))
552  {
553  status = NtAssignProcessToJobObject(jobHandle, processHandle);
554 
555  if (NT_SUCCESS(status))
556  status = NtTerminateJobObject(jobHandle, STATUS_SUCCESS);
557 
558  NtClose(jobHandle);
559  }
560 
561  NtClose(processHandle);
562  }
563 
564  return status;
565 }
566 
567 static NTSTATUS NTAPI TerminatorTD1(
568  _In_ HANDLE ProcessId
569  )
570 {
571  NTSTATUS status;
572  HANDLE processHandle;
573 
574  if (NT_SUCCESS(status = PhOpenProcess(
575  &processHandle,
577  ProcessId
578  )))
579  {
580  HANDLE debugObjectHandle;
581  OBJECT_ATTRIBUTES objectAttributes;
582 
584  &objectAttributes,
585  NULL,
586  0,
587  NULL,
588  NULL
589  );
590 
592  &debugObjectHandle,
594  &objectAttributes,
596  )))
597  {
598  NtDebugActiveProcess(processHandle, debugObjectHandle);
599  NtClose(debugObjectHandle);
600  }
601 
602  NtClose(processHandle);
603  }
604 
605  return status;
606 }
607 
608 static NTSTATUS NTAPI TerminatorTP3(
609  _In_ HANDLE ProcessId
610  )
611 {
612  NTSTATUS status;
613  HANDLE processHandle;
614 
615  if (!KphIsConnected())
616  return STATUS_NOT_SUPPORTED;
617 
618  if (NT_SUCCESS(status = PhOpenProcess(
619  &processHandle,
620  SYNCHRONIZE, // KPH doesn't require any access for this operation
621  ProcessId
622  )))
623  {
624  status = KphTerminateProcess(processHandle, STATUS_SUCCESS);
625 
626  NtClose(processHandle);
627  }
628 
629  return status;
630 }
631 
632 static NTSTATUS NTAPI TerminatorTT3(
633  _In_ HANDLE ProcessId
634  )
635 {
636  return TerminatorTTGeneric(ProcessId, TRUE, FALSE);
637 }
638 
639 static NTSTATUS NTAPI TerminatorTT4(
640  _In_ HANDLE ProcessId
641  )
642 {
643  return TerminatorTTGeneric(ProcessId, FALSE, TRUE);
644 }
645 
646 static NTSTATUS NTAPI TerminatorM1(
647  _In_ HANDLE ProcessId
648  )
649 {
650  NTSTATUS status;
651  HANDLE processHandle;
652 
653  if (NT_SUCCESS(status = PhOpenProcess(
654  &processHandle,
656  ProcessId
657  )))
658  {
659  PVOID pageOfGarbage;
660  SIZE_T pageSize;
661  PVOID baseAddress;
662  MEMORY_BASIC_INFORMATION basicInfo;
663 
664  pageOfGarbage = NULL;
665  pageSize = PAGE_SIZE;
666 
668  NtCurrentProcess(),
669  &pageOfGarbage,
670  0,
671  &pageSize,
672  MEM_COMMIT,
673  PAGE_READONLY
674  )))
675  {
676  NtClose(processHandle);
677  return STATUS_NO_MEMORY;
678  }
679 
680  baseAddress = (PVOID)0;
681 
683  processHandle,
684  baseAddress,
686  &basicInfo,
687  sizeof(MEMORY_BASIC_INFORMATION),
688  NULL
689  )))
690  {
691  ULONG i;
692 
693  // Make sure we don't write to views of mapped files. That
694  // could possibly corrupt files!
695  if (basicInfo.Type == MEM_PRIVATE)
696  {
697  for (i = 0; i < basicInfo.RegionSize; i += PAGE_SIZE)
698  {
700  processHandle,
701  PTR_ADD_OFFSET(baseAddress, i),
702  pageOfGarbage,
703  PAGE_SIZE,
704  NULL
705  );
706  }
707  }
708 
709  baseAddress = PTR_ADD_OFFSET(baseAddress, basicInfo.RegionSize);
710  }
711 
712  // Size needs to be zero if we're freeing.
713  pageSize = 0;
715  NtCurrentProcess(),
716  &pageOfGarbage,
717  &pageSize,
718  MEM_RELEASE
719  );
720 
721  NtClose(processHandle);
722  }
723 
724  return status;
725 }
726 
727 static NTSTATUS NTAPI TerminatorM2(
728  _In_ HANDLE ProcessId
729  )
730 {
731  NTSTATUS status;
732  HANDLE processHandle;
733 
734  if (NT_SUCCESS(status = PhOpenProcess(
735  &processHandle,
737  ProcessId
738  )))
739  {
740  PVOID baseAddress;
741  MEMORY_BASIC_INFORMATION basicInfo;
742  ULONG oldProtect;
743 
744  baseAddress = (PVOID)0;
745 
747  processHandle,
748  baseAddress,
750  &basicInfo,
751  sizeof(MEMORY_BASIC_INFORMATION),
752  NULL
753  )))
754  {
755  SIZE_T regionSize;
756 
757  regionSize = basicInfo.RegionSize;
759  processHandle,
760  &basicInfo.BaseAddress,
761  &regionSize,
762  PAGE_NOACCESS,
763  &oldProtect
764  );
765  baseAddress = PTR_ADD_OFFSET(baseAddress, basicInfo.RegionSize);
766  }
767 
768  NtClose(processHandle);
769  }
770 
771  return status;
772 }
773 
775 {
776  { L"TP1", L"Terminates the process using NtTerminateProcess", TerminatorTP1 },
777  { L"TP2", L"Creates a remote thread in the process which terminates the process", TerminatorTP2 },
778  { L"TT1", L"Terminates the process' threads", TerminatorTT1 },
779  { L"TT2", L"Modifies the process' threads with contexts which terminate the process", TerminatorTT2 },
780  { L"TP1a", L"Terminates the process using NtTerminateProcess (alternative method)", TerminatorTP1a },
781  { L"TT1a", L"Terminates the process' threads (alternative method)", TerminatorTT1a },
782  { L"CH1", L"Closes the process' handles", TerminatorCH1 },
783  { L"W1", L"Sends the WM_DESTROY message to the process' windows", TerminatorW1 },
784  { L"W2", L"Sends the WM_QUIT message to the process' windows", TerminatorW2 },
785  { L"W3", L"Sends the WM_CLOSE message to the process' windows", TerminatorW3 },
786  { L"TJ1", L"Assigns the process to a job object and terminates the job", TerminatorTJ1 },
787  { L"TD1", L"Debugs the process and closes the debug object", TerminatorTD1 },
788  { L"TP3", L"Terminates the process in kernel-mode", TerminatorTP3 },
789  { L"TT3", L"Terminates the process' threads in kernel-mode", TerminatorTT3 },
790  { L"TT4", L"Terminates the process' threads using a dangerous kernel-mode method", TerminatorTT4 },
791  { L"M1", L"Writes garbage to the process' memory regions", TerminatorM1 },
792  { L"M2", L"Sets the page protection of the process' memory regions to PAGE_NOACCESS", TerminatorM2 }
793 };
794 
795 static BOOLEAN PhpRunTerminatorTest(
796  _In_ HWND WindowHandle,
797  _In_ INT Index
798  )
799 {
800  NTSTATUS status;
801  PTEST_ITEM testItem;
802  PPH_PROCESS_ITEM processItem;
803  HWND lvHandle;
804  PVOID processes;
805  BOOLEAN success = FALSE;
806  LARGE_INTEGER interval;
807 
808  processItem = (PPH_PROCESS_ITEM)GetProp(WindowHandle, L"ProcessItem");
809  lvHandle = GetDlgItem(WindowHandle, IDC_TERMINATOR_LIST);
810 
812  lvHandle,
813  Index,
814  &testItem
815  ))
816  return FALSE;
817 
818  if (PhEqualStringZ(testItem->Id, L"TT4", FALSE))
819  {
821  WindowHandle,
822  L"run",
823  L"the TT4 test",
824  L"The TT4 test may cause the system to crash.",
825  TRUE
826  ))
827  return FALSE;
828  }
829 
830  status = testItem->TestProc(processItem->ProcessId);
831  interval.QuadPart = -1000 * PH_TIMEOUT_MS;
832  NtDelayExecution(FALSE, &interval);
833 
834  if (status == STATUS_NOT_SUPPORTED)
835  {
836  PPH_STRING concat;
837 
838  concat = PhConcatStrings2(L"(Not available) ", testItem->Description);
839  PhSetListViewSubItem(lvHandle, Index, 1, concat->Buffer);
840  PhDereferenceObject(concat);
841  }
842 
843  if (!NT_SUCCESS(PhEnumProcesses(&processes)))
844  return FALSE;
845 
846  // Check if the process exists.
847  if (!PhFindProcessInformation(processes, processItem->ProcessId))
848  {
849  PhSetListViewItemImageIndex(lvHandle, Index, TICK_INDEX);
850  SetDlgItemText(WindowHandle, IDC_TERMINATOR_TEXT, L"The process was terminated.");
851  success = TRUE;
852  }
853  else
854  {
855  PhSetListViewItemImageIndex(lvHandle, Index, CROSS_INDEX);
856  }
857 
858  PhFree(processes);
859 
860  UpdateWindow(WindowHandle);
861 
862  return success;
863 }
864 
865 static INT_PTR CALLBACK PhpProcessTerminatorDlgProc(
866  _In_ HWND hwndDlg,
867  _In_ UINT uMsg,
868  _In_ WPARAM wParam,
869  _In_ LPARAM lParam
870  )
871 {
872  switch (uMsg)
873  {
874  case WM_INITDIALOG:
875  {
876  PPH_PROCESS_ITEM processItem = (PPH_PROCESS_ITEM)lParam;
877  PPH_STRING title;
878  HWND lvHandle;
879  HIMAGELIST imageList;
880  ULONG i;
881 
882  PhCenterWindow(hwndDlg, GetParent(hwndDlg));
883 
884  title = PhFormatString(
885  L"Terminator - %s (%u)",
886  processItem->ProcessName->Buffer,
887  (ULONG)processItem->ProcessId
888  );
889  SetWindowText(hwndDlg, title->Buffer);
890  PhDereferenceObject(title);
891 
892  SetProp(hwndDlg, L"ProcessItem", (HANDLE)processItem);
893 
894  lvHandle = GetDlgItem(hwndDlg, IDC_TERMINATOR_LIST);
895  PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 70, L"ID");
896  PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 280, L"Description");
897  ListView_SetExtendedListViewStyleEx(lvHandle, LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP | LVS_EX_LABELTIP | LVS_EX_CHECKBOXES, -1);
898  PhSetControlTheme(lvHandle, L"explorer");
899 
900  imageList = ImageList_Create(16, 16, ILC_COLOR32, 0, 0);
901  ImageList_SetImageCount(imageList, 2);
902  PhSetImageListBitmap(imageList, CROSS_INDEX, PhInstanceHandle, MAKEINTRESOURCE(IDB_CROSS));
903  PhSetImageListBitmap(imageList, TICK_INDEX, PhInstanceHandle, MAKEINTRESOURCE(IDB_TICK));
904 
905  for (i = 0; i < sizeof(PhTerminatorTests) / sizeof(TEST_ITEM); i++)
906  {
907  INT itemIndex;
908  BOOLEAN check;
909 
910  itemIndex = PhAddListViewItem(
911  lvHandle,
912  MAXINT,
913  PhTerminatorTests[i].Id,
914  &PhTerminatorTests[i]
915  );
916  PhSetListViewSubItem(lvHandle, itemIndex, 1, PhTerminatorTests[i].Description);
917  PhSetListViewItemImageIndex(lvHandle, itemIndex, -1);
918 
919  check = TRUE;
920 
921  if (PhEqualStringZ(PhTerminatorTests[i].Id, L"TT4", FALSE) || PhEqualStringZ(PhTerminatorTests[i].Id, L"M1", FALSE))
922  check = FALSE;
923 
924  ListView_SetCheckState(lvHandle, itemIndex, check);
925  }
926 
927  ListView_SetImageList(lvHandle, imageList, LVSIL_SMALL);
928 
929  SetDlgItemText(
930  hwndDlg,
932  L"Double-click a termination method or click Run Selected."
933  );
934  }
935  break;
936  case WM_DESTROY:
937  {
938  RemoveProp(hwndDlg, L"ProcessItem");
939  }
940  break;
941  case WM_COMMAND:
942  {
943  INT id = LOWORD(wParam);
944 
945  switch (id)
946  {
947  case IDCANCEL: // Esc and X button to close
948  case IDOK:
949  EndDialog(hwndDlg, IDOK);
950  break;
951  case IDC_RUNSELECTED:
952  {
953  if (PhShowConfirmMessage(hwndDlg, L"run", L"the selected terminator tests", NULL, FALSE))
954  {
955  HWND lvHandle;
956  ULONG i;
957 
958  lvHandle = GetDlgItem(hwndDlg, IDC_TERMINATOR_LIST);
959 
960  for (i = 0; i < sizeof(PhTerminatorTests) / sizeof(TEST_ITEM); i++)
961  {
962  if (ListView_GetCheckState(lvHandle, i))
963  {
964  if (PhpRunTerminatorTest(
965  hwndDlg,
966  i
967  ))
968  break;
969  }
970  }
971  }
972  }
973  break;
974  }
975  }
976  break;
977  case WM_NOTIFY:
978  {
979  LPNMHDR header = (LPNMHDR)lParam;
980 
981  if (header->hwndFrom == GetDlgItem(hwndDlg, IDC_TERMINATOR_LIST))
982  {
983  if (header->code == NM_DBLCLK)
984  {
985  LPNMITEMACTIVATE itemActivate = (LPNMITEMACTIVATE)header;
986 
987  if (itemActivate->iItem != -1)
988  {
989  if (PhShowConfirmMessage(hwndDlg, L"run", L"the selected test", NULL, FALSE))
990  {
991  PhpRunTerminatorTest(
992  hwndDlg,
993  itemActivate->iItem
994  );
995  }
996  }
997  }
998  else if (header->code == LVN_ITEMCHANGED)
999  {
1000  ULONG i;
1001  BOOLEAN oneSelected;
1002 
1003  oneSelected = FALSE;
1004 
1005  for (i = 0; i < sizeof(PhTerminatorTests) / sizeof(TEST_ITEM); i++)
1006  {
1007  if (ListView_GetCheckState(header->hwndFrom, i))
1008  {
1009  oneSelected = TRUE;
1010  break;
1011  }
1012  }
1013 
1014  EnableWindow(GetDlgItem(hwndDlg, IDC_RUNSELECTED), oneSelected);
1015  }
1016  }
1017  }
1018  break;
1019  }
1020 
1021  return FALSE;
1022 }