Process Hacker
main.c
Go to the documentation of this file.
1 /*
2  * Process Hacker User Notes -
3  * main program
4  *
5  * Copyright (C) 2011-2015 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 <phdk.h>
24 #include <phappresource.h>
25 #include <windowsx.h>
26 #include "db.h"
27 #include "resource.h"
28 
29 #define INTENT_PROCESS_COMMENT 0x1
30 #define INTENT_PROCESS_PRIORITY_CLASS 0x2
31 #define INTENT_PROCESS_IO_PRIORITY 0x4
32 
33 #define PROCESS_PRIORITY_SAVE_ID 1
34 #define PROCESS_PRIORITY_SAVE_FOR_THIS_COMMAND_LINE_ID 2
35 #define PROCESS_IO_PRIORITY_SAVE_ID 3
36 #define PROCESS_IO_PRIORITY_SAVE_FOR_THIS_COMMAND_LINE_ID 4
37 
38 #define COMMENT_COLUMN_ID 1
39 
40 typedef struct _PROCESS_EXTENSION
41 {
42  LIST_ENTRY ListEntry;
43  PPH_PROCESS_ITEM ProcessItem;
44  BOOLEAN Valid;
45  PPH_STRING Comment;
47 
48 typedef struct _PROCESS_COMMENT_PAGE_CONTEXT
49 {
50  PPH_STRING OriginalComment;
52 
53 typedef struct _SERVICE_EXTENSION
54 {
55  LIST_ENTRY ListEntry;
56  BOOLEAN Valid;
57  PPH_STRING Comment;
59 
60 typedef struct _SERVICE_COMMENT_PAGE_CONTEXT
61 {
62  PPH_SERVICE_ITEM ServiceItem;
63  PH_LAYOUT_MANAGER LayoutManager;
65 
66 INT_PTR CALLBACK OptionsDlgProc(
67  _In_ HWND hwndDlg,
68  _In_ UINT uMsg,
69  _In_ WPARAM wParam,
70  _In_ LPARAM lParam
71  );
72 
73 INT_PTR CALLBACK ProcessCommentPageDlgProc(
74  _In_ HWND hwndDlg,
75  _In_ UINT uMsg,
76  _In_ WPARAM wParam,
77  _In_ LPARAM lParam
78  );
79 
80 INT_PTR CALLBACK ServiceCommentPageDlgProc(
81  _In_ HWND hwndDlg,
82  _In_ UINT uMsg,
83  _In_ WPARAM wParam,
84  _In_ LPARAM lParam
85  );
86 
103 
107 
111 
113  _In_ PDB_OBJECT Object,
114  _In_ ULONG Intent
115  )
116 {
117  return (!(Intent & INTENT_PROCESS_COMMENT) || Object->Comment->Length != 0) &&
118  (!(Intent & INTENT_PROCESS_PRIORITY_CLASS) || Object->PriorityClass != 0) &&
119  (!(Intent & INTENT_PROCESS_IO_PRIORITY) || Object->IoPriorityPlusOne != 0);
120 }
121 
123  _In_ PPH_PROCESS_ITEM ProcessItem,
124  _In_ ULONG Intent
125  )
126 {
127  PDB_OBJECT object;
128 
129  if (
130  ProcessItem->CommandLine && (object = FindDbObject(COMMAND_LINE_TAG, &ProcessItem->CommandLine->sr)) &&
131  MatchDbObjectIntent(object, Intent)
132  )
133  return object;
134 
135  if ((object = FindDbObject(FILE_TAG, &ProcessItem->ProcessName->sr)) && MatchDbObjectIntent(object, Intent))
136  return object;
137 
138  return NULL;
139 }
140 
142  _In_ PDB_OBJECT Object
143  )
144 {
145  if (Object->Comment->Length == 0 && Object->PriorityClass == 0 && Object->IoPriorityPlusOne == 0)
146  {
147  DeleteDbObject(Object);
148  }
149 }
150 
152  _In_ HANDLE ProcessId
153  )
154 {
155  HANDLE processHandle;
156  ULONG ioPriority = -1;
157 
159  &processHandle,
161  ProcessId
162  )))
163  {
165  processHandle,
166  &ioPriority
167  )))
168  {
169  ioPriority = -1;
170  }
171 
172  NtClose(processHandle);
173  }
174 
175  return ioPriority;
176 }
177 
179  _In_ ULONG Id
180  )
181 {
182  switch (Id)
183  {
184  case PHAPP_ID_PRIORITY_REALTIME:
186  case PHAPP_ID_PRIORITY_HIGH:
188  case PHAPP_ID_PRIORITY_ABOVENORMAL:
190  case PHAPP_ID_PRIORITY_NORMAL:
192  case PHAPP_ID_PRIORITY_BELOWNORMAL:
194  case PHAPP_ID_PRIORITY_IDLE:
196  }
197 
198  return 0;
199 }
200 
202  _In_ ULONG Id
203  )
204 {
205  switch (Id)
206  {
207  case PHAPP_ID_I_0:
208  return 0;
209  case PHAPP_ID_I_1:
210  return 1;
211  case PHAPP_ID_I_2:
212  return 2;
213  case PHAPP_ID_I_3:
214  return 3;
215  }
216 
217  return -1;
218 }
219 
221  _In_opt_ PVOID Parameter,
222  _In_opt_ PVOID Context
223  )
224 {
225  PPH_STRING path;
226  PPH_STRING realPath;
227 
228  path = PhGetStringSetting(L"ProcessHacker.UserNotes.DatabasePath");
229  realPath = PhExpandEnvironmentStrings(&path->sr);
230  PhDereferenceObject(path);
231  path = realPath;
232 
234  {
235  PPH_STRING directory;
236 
237  directory = PhGetApplicationDirectory();
238  realPath = PhConcatStringRef2(&directory->sr, &path->sr);
239  PhDereferenceObject(directory);
240  PhDereferenceObject(path);
241  path = realPath;
242  }
243 
244  SetDbPath(path);
245  PhDereferenceObject(path);
246 
247  LoadDb();
248 }
249 
251  _In_opt_ PVOID Parameter,
252  _In_opt_ PVOID Context
253  )
254 {
255  SaveDb();
256 }
257 
259  _In_opt_ PVOID Parameter,
260  _In_opt_ PVOID Context
261  )
262 {
263  DialogBox(
264  PluginInstance->DllBase,
265  MAKEINTRESOURCE(IDD_OPTIONS),
266  (HWND)Parameter,
268  );
269 }
270 
272  _In_opt_ PVOID Parameter,
273  _In_opt_ PVOID Context
274  )
275 {
276  PPH_PLUGIN_MENU_ITEM menuItem = Parameter;
278  PDB_OBJECT object;
279 
280  if (!processItem)
281  return;
282 
283  switch (menuItem->Id)
284  {
286  {
287  LockDb();
288 
289  if ((object = FindDbObject(FILE_TAG, &processItem->ProcessName->sr)) && object->PriorityClass != 0)
290  {
291  object->PriorityClass = 0;
293  }
294  else
295  {
296  object = CreateDbObject(FILE_TAG, &processItem->ProcessName->sr, NULL);
297  object->PriorityClass = processItem->PriorityClass;
298  }
299 
300  UnlockDb();
301  SaveDb();
302  }
303  break;
305  {
306  if (processItem->CommandLine)
307  {
308  LockDb();
309 
310  if ((object = FindDbObject(COMMAND_LINE_TAG, &processItem->CommandLine->sr)) && object->PriorityClass != 0)
311  {
312  object->PriorityClass = 0;
314  }
315  else
316  {
317  object = CreateDbObject(COMMAND_LINE_TAG, &processItem->CommandLine->sr, NULL);
318  object->PriorityClass = processItem->PriorityClass;
319  }
320 
321  UnlockDb();
322  SaveDb();
323  }
324  }
325  break;
327  {
328  LockDb();
329 
330  if ((object = FindDbObject(FILE_TAG, &processItem->ProcessName->sr)) && object->IoPriorityPlusOne != 0)
331  {
332  object->IoPriorityPlusOne = 0;
334  }
335  else
336  {
337  object = CreateDbObject(FILE_TAG, &processItem->ProcessName->sr, NULL);
338  object->IoPriorityPlusOne = GetProcessIoPriority(processItem->ProcessId) + 1;
339  }
340 
341  UnlockDb();
342  SaveDb();
343  }
344  break;
346  {
347  if (processItem->CommandLine)
348  {
349  LockDb();
350 
351  if ((object = FindDbObject(COMMAND_LINE_TAG, &processItem->CommandLine->sr)) && object->IoPriorityPlusOne != 0)
352  {
353  object->IoPriorityPlusOne = 0;
355  }
356  else
357  {
358  object = CreateDbObject(COMMAND_LINE_TAG, &processItem->CommandLine->sr, NULL);
359  object->IoPriorityPlusOne = GetProcessIoPriority(processItem->ProcessId) + 1;
360  }
361 
362  UnlockDb();
363  SaveDb();
364  }
365  }
366  break;
367  }
368 }
369 
371  _In_opt_ PVOID Parameter,
372  _In_opt_ PVOID Context
373  )
374 {
375  PPH_PLUGIN_MENU_HOOK_INFORMATION menuHookInfo = Parameter;
376  ULONG id = menuHookInfo->SelectedItem->Id;
377 
378  switch (id)
379  {
380  case PHAPP_ID_PRIORITY_REALTIME:
381  case PHAPP_ID_PRIORITY_HIGH:
382  case PHAPP_ID_PRIORITY_ABOVENORMAL:
383  case PHAPP_ID_PRIORITY_NORMAL:
384  case PHAPP_ID_PRIORITY_BELOWNORMAL:
385  case PHAPP_ID_PRIORITY_IDLE:
386  {
387  BOOLEAN changed = FALSE;
388  PPH_PROCESS_ITEM *processes;
389  ULONG numberOfProcesses;
390  ULONG i;
391 
392  PhGetSelectedProcessItems(&processes, &numberOfProcesses);
393  LockDb();
394 
395  for (i = 0; i < numberOfProcesses; i++)
396  {
397  PDB_OBJECT object;
398 
399  if (object = FindDbObjectForProcess(processes[i], INTENT_PROCESS_PRIORITY_CLASS))
400  {
401  ULONG newPriorityClass = GetPriorityClassFromId(id);
402 
403  if (object->PriorityClass != newPriorityClass)
404  {
405  object->PriorityClass = newPriorityClass;
406  changed = TRUE;
407  }
408  }
409  }
410 
411  UnlockDb();
412  PhFree(processes);
413 
414  if (changed)
415  SaveDb();
416  }
417  break;
418  case PHAPP_ID_I_0:
419  case PHAPP_ID_I_1:
420  case PHAPP_ID_I_2:
421  case PHAPP_ID_I_3:
422  {
423  BOOLEAN changed = FALSE;
424  PPH_PROCESS_ITEM *processes;
425  ULONG numberOfProcesses;
426  ULONG i;
427 
428  PhGetSelectedProcessItems(&processes, &numberOfProcesses);
429  LockDb();
430 
431  for (i = 0; i < numberOfProcesses; i++)
432  {
433  PDB_OBJECT object;
434 
435  if (object = FindDbObjectForProcess(processes[i], INTENT_PROCESS_IO_PRIORITY))
436  {
437  ULONG newIoPriorityPlusOne = GetIoPriorityFromId(id) + 1;
438 
439  if (object->IoPriorityPlusOne != newIoPriorityPlusOne)
440  {
441  object->IoPriorityPlusOne = newIoPriorityPlusOne;
442  changed = TRUE;
443  }
444  }
445  }
446 
447  UnlockDb();
448  PhFree(processes);
449 
450  if (changed)
451  SaveDb();
452  }
453  break;
454  }
455 }
456 
458  VOID
459  )
460 {
461  PLIST_ENTRY listEntry;
462 
463  PhAcquireQueuedLockExclusive(&ProcessListLock);
464 
465  listEntry = ProcessListHead.Flink;
466 
467  while (listEntry != &ProcessListHead)
468  {
469  PPROCESS_EXTENSION extension;
470 
471  extension = CONTAINING_RECORD(listEntry, PROCESS_EXTENSION, ListEntry);
472 
473  extension->Valid = FALSE;
474 
475  listEntry = listEntry->Flink;
476  }
477 
478  PhReleaseQueuedLockExclusive(&ProcessListLock);
479 }
480 
482  _In_ PPH_PROCESS_NODE Node,
483  _In_ PPROCESS_EXTENSION Extension
484  )
485 {
486  if (!Extension->Valid)
487  {
488  PDB_OBJECT object;
489 
490  LockDb();
491 
492  if (object = FindDbObjectForProcess(Node->ProcessItem, INTENT_PROCESS_COMMENT))
493  {
494  PhSwapReference(&Extension->Comment, object->Comment);
495  }
496  else
497  {
498  PhClearReference(&Extension->Comment);
499  }
500 
501  UnlockDb();
502 
503  Extension->Valid = TRUE;
504  }
505 }
506 
508  VOID
509  )
510 {
511  PLIST_ENTRY listEntry;
512 
513  PhAcquireQueuedLockExclusive(&ServiceListLock);
514 
515  listEntry = ServiceListHead.Flink;
516 
517  while (listEntry != &ServiceListHead)
518  {
519  PSERVICE_EXTENSION extension;
520 
521  extension = CONTAINING_RECORD(listEntry, SERVICE_EXTENSION, ListEntry);
522 
523  extension->Valid = FALSE;
524 
525  listEntry = listEntry->Flink;
526  }
527 
528  PhReleaseQueuedLockExclusive(&ServiceListLock);
529 }
530 
532  _In_ PPH_SERVICE_NODE Node,
533  _In_ PSERVICE_EXTENSION Extension
534  )
535 {
536  if (!Extension->Valid)
537  {
538  PDB_OBJECT object;
539 
540  LockDb();
541 
542  if (object = FindDbObject(SERVICE_TAG, &Node->ServiceItem->Name->sr))
543  {
544  PhSwapReference(&Extension->Comment, object->Comment);
545  }
546  else
547  {
548  PhClearReference(&Extension->Comment);
549  }
550 
551  UnlockDb();
552 
553  Extension->Valid = TRUE;
554  }
555 }
556 
558  _In_opt_ PVOID Parameter,
559  _In_opt_ PVOID Context
560  )
561 {
562  PPH_PLUGIN_TREENEW_MESSAGE message = Parameter;
563 
564  switch (message->Message)
565  {
566  case TreeNewGetCellText:
567  {
568  PPH_TREENEW_GET_CELL_TEXT getCellText = message->Parameter1;
569 
570  if (message->TreeNewHandle == ProcessTreeNewHandle)
571  {
572  PPH_PROCESS_NODE node;
573 
574  node = (PPH_PROCESS_NODE)getCellText->Node;
575 
576  switch (message->SubId)
577  {
578  case COMMENT_COLUMN_ID:
579  {
580  PPROCESS_EXTENSION extension;
581 
582  extension = PhPluginGetObjectExtension(PluginInstance, node->ProcessItem, EmProcessItemType);
583  UpdateProcessComment(node, extension);
584  getCellText->Text = PhGetStringRef(extension->Comment);
585  }
586  break;
587  }
588  }
589  else if (message->TreeNewHandle == ServiceTreeNewHandle)
590  {
591  PPH_SERVICE_NODE node;
592 
593  node = (PPH_SERVICE_NODE)getCellText->Node;
594 
595  switch (message->SubId)
596  {
597  case COMMENT_COLUMN_ID:
598  {
599  PSERVICE_EXTENSION extension;
600 
601  extension = PhPluginGetObjectExtension(PluginInstance, node->ServiceItem, EmServiceItemType);
602  UpdateServiceComment(node, extension);
603  getCellText->Text = PhGetStringRef(extension->Comment);
604  }
605  break;
606  }
607  }
608  }
609  break;
610  }
611 }
612 
614  _In_opt_ PVOID Parameter,
615  _In_opt_ PVOID Context
616  )
617 {
618  NOTHING;
619 }
620 
622  _In_opt_ PVOID Parameter,
623  _In_opt_ PVOID Context
624  )
625 {
626  PPH_PLUGIN_PROCESS_PROPCONTEXT propContext = Parameter;
627 
629  propContext->PropContext,
631  );
632 }
633 
635  _In_ PPH_PLUGIN_MENU_INFORMATION MenuInfo,
636  _In_ PPH_PROCESS_ITEM ProcessItem,
637  _In_ BOOLEAN UseSelectionForHook
638  )
639 {
640  PPH_EMENU_ITEM priorityMenuItem;
641  PPH_EMENU_ITEM ioPriorityMenuItem;
642  PPH_EMENU_ITEM saveMenuItem;
643  PPH_EMENU_ITEM saveForCommandLineMenuItem;
644  PDB_OBJECT object;
645 
646  // Priority
647  if (priorityMenuItem = PhFindEMenuItem(MenuInfo->Menu, 0, L"Priority", 0))
648  {
649  PhInsertEMenuItem(priorityMenuItem, PhPluginCreateEMenuItem(PluginInstance, PH_EMENU_SEPARATOR, 0, L"", NULL), -1);
650  PhInsertEMenuItem(priorityMenuItem, saveMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, PROCESS_PRIORITY_SAVE_ID, PhaFormatString(L"Save for %s", ProcessItem->ProcessName->Buffer)->Buffer, NULL), -1);
651  PhInsertEMenuItem(priorityMenuItem, saveForCommandLineMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, PROCESS_PRIORITY_SAVE_FOR_THIS_COMMAND_LINE_ID, L"Save for this command line", NULL), -1);
652 
653  if (!ProcessItem->CommandLine)
654  saveForCommandLineMenuItem->Flags |= PH_EMENU_DISABLED;
655 
656  LockDb();
657 
658  if ((object = FindDbObject(FILE_TAG, &ProcessItem->ProcessName->sr)) && object->PriorityClass != 0)
659  saveMenuItem->Flags |= PH_EMENU_CHECKED;
660  if (ProcessItem->CommandLine && (object = FindDbObject(COMMAND_LINE_TAG, &ProcessItem->CommandLine->sr)) && object->PriorityClass != 0)
661  saveForCommandLineMenuItem->Flags |= PH_EMENU_CHECKED;
662 
663  UnlockDb();
664  }
665 
666  // I/O Priority
667  if (ioPriorityMenuItem = PhFindEMenuItem(MenuInfo->Menu, 0, L"I/O Priority", 0))
668  {
669  PhInsertEMenuItem(ioPriorityMenuItem, PhPluginCreateEMenuItem(PluginInstance, PH_EMENU_SEPARATOR, 0, L"", NULL), -1);
670  PhInsertEMenuItem(ioPriorityMenuItem, saveMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, PROCESS_IO_PRIORITY_SAVE_ID, PhaFormatString(L"Save for %s", ProcessItem->ProcessName->Buffer)->Buffer, NULL), -1);
671  PhInsertEMenuItem(ioPriorityMenuItem, saveForCommandLineMenuItem = PhPluginCreateEMenuItem(PluginInstance, 0, PROCESS_IO_PRIORITY_SAVE_FOR_THIS_COMMAND_LINE_ID, L"Save for this command line", NULL), -1);
672 
673  if (!ProcessItem->CommandLine)
674  saveForCommandLineMenuItem->Flags |= PH_EMENU_DISABLED;
675 
676  LockDb();
677 
678  if ((object = FindDbObject(FILE_TAG, &ProcessItem->ProcessName->sr)) && object->IoPriorityPlusOne != 0)
679  saveMenuItem->Flags |= PH_EMENU_CHECKED;
680  if (ProcessItem->CommandLine && (object = FindDbObject(COMMAND_LINE_TAG, &ProcessItem->CommandLine->sr)) && object->IoPriorityPlusOne != 0)
681  saveForCommandLineMenuItem->Flags |= PH_EMENU_CHECKED;
682 
683  UnlockDb();
684  }
685 
686  PhPluginAddMenuHook(MenuInfo, PluginInstance, UseSelectionForHook ? NULL : ProcessItem->ProcessId);
687 }
688 
690  _In_opt_ PVOID Parameter,
691  _In_opt_ PVOID Context
692  )
693 {
694  PPH_PLUGIN_MENU_INFORMATION menuInfo = Parameter;
695 
696  if (menuInfo->u.Process.NumberOfProcesses != 1)
697  return;
698 
699  AddSavePriorityMenuItemsAndHook(menuInfo, menuInfo->u.Process.Processes[0], TRUE);
700 }
701 
703  _In_ PVOID Node1,
704  _In_ PVOID Node2,
705  _In_ ULONG SubId,
706  _In_ PVOID Context
707  )
708 {
709  PPH_PROCESS_NODE node1 = Node1;
710  PPH_PROCESS_NODE node2 = Node2;
711  PPROCESS_EXTENSION extension1 = PhPluginGetObjectExtension(PluginInstance, node1->ProcessItem, EmProcessItemType);
712  PPROCESS_EXTENSION extension2 = PhPluginGetObjectExtension(PluginInstance, node2->ProcessItem, EmProcessItemType);
713 
714  UpdateProcessComment(node1, extension1);
715  UpdateProcessComment(node2, extension2);
716 
717  return PhCompareStringWithNull(extension1->Comment, extension2->Comment, TRUE);
718 }
719 
721  _In_opt_ PVOID Parameter,
722  _In_opt_ PVOID Context
723  )
724 {
725  PPH_PLUGIN_TREENEW_INFORMATION info = Parameter;
726  PH_TREENEW_COLUMN column;
727 
729 
730  memset(&column, 0, sizeof(PH_TREENEW_COLUMN));
731  column.Text = L"Comment";
732  column.Width = 120;
733  column.Alignment = PH_ALIGN_LEFT;
734 
735  PhPluginAddTreeNewColumn(PluginInstance, info->CmData, &column, COMMENT_COLUMN_ID, NULL, ProcessCommentSortFunction);
736 }
737 
739  _In_opt_ PVOID Parameter,
740  _In_opt_ PVOID Context
741  )
742 {
743  PPH_PLUGIN_OBJECT_PROPERTIES objectProperties = Parameter;
744  PROPSHEETPAGE propSheetPage;
745 
746  if (objectProperties->NumberOfPages < objectProperties->MaximumNumberOfPages)
747  {
748  memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE));
749  propSheetPage.dwSize = sizeof(PROPSHEETPAGE);
750  propSheetPage.dwFlags = PSP_USETITLE;
751  propSheetPage.hInstance = PluginInstance->DllBase;
752  propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_SRVCOMMENT);
753  propSheetPage.pszTitle = L"Comment";
754  propSheetPage.pfnDlgProc = ServiceCommentPageDlgProc;
755  propSheetPage.lParam = (LPARAM)objectProperties->Parameter;
756  objectProperties->Pages[objectProperties->NumberOfPages++] = CreatePropertySheetPage(&propSheetPage);
757  }
758 }
759 
761  _In_ PVOID Node1,
762  _In_ PVOID Node2,
763  _In_ ULONG SubId,
764  _In_ PVOID Context
765  )
766 {
767  PPH_SERVICE_NODE node1 = Node1;
768  PPH_SERVICE_NODE node2 = Node2;
769  PSERVICE_EXTENSION extension1 = PhPluginGetObjectExtension(PluginInstance, node1->ServiceItem, EmServiceItemType);
770  PSERVICE_EXTENSION extension2 = PhPluginGetObjectExtension(PluginInstance, node2->ServiceItem, EmServiceItemType);
771 
772  UpdateServiceComment(node1, extension1);
773  UpdateServiceComment(node2, extension2);
774 
775  return PhCompareStringWithNull(extension1->Comment, extension2->Comment, TRUE);
776 }
777 
779  _In_opt_ PVOID Parameter,
780  _In_opt_ PVOID Context
781  )
782 {
783  PPH_PLUGIN_TREENEW_INFORMATION info = Parameter;
784  PH_TREENEW_COLUMN column;
785 
787 
788  memset(&column, 0, sizeof(PH_TREENEW_COLUMN));
789  column.Text = L"Comment";
790  column.Width = 120;
791  column.Alignment = PH_ALIGN_LEFT;
792 
793  PhPluginAddTreeNewColumn(PluginInstance, info->CmData, &column, COMMENT_COLUMN_ID, NULL, ServiceCommentSortFunction);
794 }
795 
797  _In_opt_ PVOID Parameter,
798  _In_opt_ PVOID Context
799  )
800 {
801  PPH_PLUGIN_MENU_INFORMATION menuInfo = Parameter;
802 
803  AddSavePriorityMenuItemsAndHook(menuInfo, menuInfo->u.MiListSection.ProcessGroup->Representative, FALSE);
804 }
805 
807  _In_opt_ PVOID Parameter,
808  _In_opt_ PVOID Context
809  )
810 {
811  PPH_PROCESS_ITEM processItem = Parameter;
812  PPROCESS_EXTENSION extension = PhPluginGetObjectExtension(PluginInstance, processItem, EmProcessItemType);
813 
814  extension->Valid = FALSE;
815 }
816 
818  _In_opt_ PVOID Parameter,
819  _In_opt_ PVOID Context
820  )
821 {
822  PLIST_ENTRY listEntry;
823 
824  if (GetNumberOfDbObjects() == 0)
825  return;
826 
827  PhAcquireQueuedLockExclusive(&ProcessListLock);
828  LockDb();
829 
830  listEntry = ProcessListHead.Flink;
831 
832  while (listEntry != &ProcessListHead)
833  {
834  PPROCESS_EXTENSION extension;
835  PPH_PROCESS_ITEM processItem;
836  PDB_OBJECT object;
837 
838  extension = CONTAINING_RECORD(listEntry, PROCESS_EXTENSION, ListEntry);
839  processItem = extension->ProcessItem;
840 
841  if (object = FindDbObjectForProcess(processItem, INTENT_PROCESS_PRIORITY_CLASS))
842  {
843  if (processItem->PriorityClass != object->PriorityClass)
844  {
845  HANDLE processHandle;
846  PROCESS_PRIORITY_CLASS priorityClass;
847 
849  &processHandle,
851  processItem->ProcessId
852  )))
853  {
854  priorityClass.Foreground = FALSE;
855  priorityClass.PriorityClass = (UCHAR)object->PriorityClass;
856  NtSetInformationProcess(processHandle, ProcessPriorityClass, &priorityClass, sizeof(PROCESS_PRIORITY_CLASS));
857 
858  NtClose(processHandle);
859  }
860  }
861  }
862 
863  if (object = FindDbObjectForProcess(processItem, INTENT_PROCESS_IO_PRIORITY))
864  {
865  if (object->IoPriorityPlusOne != 0)
866  {
867  HANDLE processHandle;
868 
870  &processHandle,
872  processItem->ProcessId
873  )))
874  {
875  PhSetProcessIoPriority(processHandle, object->IoPriorityPlusOne - 1);
876  NtClose(processHandle);
877  }
878  }
879  }
880 
881  listEntry = listEntry->Flink;
882  }
883 
884  UnlockDb();
885  PhReleaseQueuedLockExclusive(&ProcessListLock);
886 }
887 
889  _In_ PVOID Object,
890  _In_ PH_EM_OBJECT_TYPE ObjectType,
891  _In_ PVOID Extension
892  )
893 {
894  PPH_PROCESS_ITEM processItem = Object;
895  PPROCESS_EXTENSION extension = Extension;
896 
897  memset(extension, 0, sizeof(PROCESS_EXTENSION));
898  extension->ProcessItem = processItem;
899 
900  PhAcquireQueuedLockExclusive(&ProcessListLock);
901  InsertTailList(&ProcessListHead, &extension->ListEntry);
902  PhReleaseQueuedLockExclusive(&ProcessListLock);
903 }
904 
906  _In_ PVOID Object,
907  _In_ PH_EM_OBJECT_TYPE ObjectType,
908  _In_ PVOID Extension
909  )
910 {
911  PPH_PROCESS_ITEM processItem = Object;
912  PPROCESS_EXTENSION extension = Extension;
913 
914  PhClearReference(&extension->Comment);
915  PhAcquireQueuedLockExclusive(&ProcessListLock);
916  RemoveEntryList(&extension->ListEntry);
917  PhReleaseQueuedLockExclusive(&ProcessListLock);
918 }
919 
921  _In_ PVOID Object,
922  _In_ PH_EM_OBJECT_TYPE ObjectType,
923  _In_ PVOID Extension
924  )
925 {
926  PPH_SERVICE_ITEM processItem = Object;
927  PSERVICE_EXTENSION extension = Extension;
928 
929  memset(extension, 0, sizeof(SERVICE_EXTENSION));
930  PhAcquireQueuedLockExclusive(&ServiceListLock);
931  InsertTailList(&ServiceListHead, &extension->ListEntry);
932  PhReleaseQueuedLockExclusive(&ServiceListLock);
933 }
934 
936  _In_ PVOID Object,
937  _In_ PH_EM_OBJECT_TYPE ObjectType,
938  _In_ PVOID Extension
939  )
940 {
941  PPH_SERVICE_ITEM processItem = Object;
942  PSERVICE_EXTENSION extension = Extension;
943 
944  PhClearReference(&extension->Comment);
945  PhAcquireQueuedLockExclusive(&ServiceListLock);
946  RemoveEntryList(&extension->ListEntry);
947  PhReleaseQueuedLockExclusive(&ServiceListLock);
948 }
949 
951  _In_ HINSTANCE Instance,
952  _In_ ULONG Reason,
953  _Reserved_ PVOID Reserved
954  )
955 {
956  if (Reason == DLL_PROCESS_ATTACH)
957  {
959 
960  PluginInstance = PhRegisterPlugin(L"ProcessHacker.UserNotes", Instance, &info);
961 
962  if (!PluginInstance)
963  return FALSE;
964 
965  info->DisplayName = L"User Notes";
966  info->Author = L"wj32";
967  info->Description = L"Allows the user to add comments for processes and services. Also allows the user to save process priority.";
968  info->Url = L"http://processhacker.sf.net/forums/viewtopic.php?t=1120";
969  info->HasOptions = TRUE;
970 
971  InitializeDb();
972 
974  LoadCallback, NULL, &PluginLoadCallbackRegistration);
976  UnloadCallback, NULL, &PluginUnloadCallbackRegistration);
978  ShowOptionsCallback, NULL, &PluginShowOptionsCallbackRegistration);
980  MenuItemCallback, NULL, &PluginMenuItemCallbackRegistration);
982  MenuHookCallback, NULL, &PluginMenuHookCallbackRegistration);
984  TreeNewMessageCallback, NULL, &TreeNewMessageCallbackRegistration);
986  MainWindowShowingCallback, NULL, &MainWindowShowingCallbackRegistration);
988  ProcessPropertiesInitializingCallback, NULL, &ProcessPropertiesInitializingCallbackRegistration);
990  ServicePropertiesInitializingCallback, NULL, &ServicePropertiesInitializingCallbackRegistration);
992  ProcessMenuInitializingCallback, NULL, &ProcessMenuInitializingCallbackRegistration);
994  ProcessTreeNewInitializingCallback, NULL, &ProcessTreeNewInitializingCallbackRegistration);
996  ServiceTreeNewInitializingCallback, NULL, &ServiceTreeNewInitializingCallbackRegistration);
998  MiListSectionMenuInitializingCallback, NULL, &MiListSectionMenuInitializingCallbackRegistration);
1000  ProcessModifiedCallback, NULL, &ProcessModifiedCallbackRegistration);
1002  ProcessesUpdatedCallback, NULL, &ProcessesUpdatedCallbackRegistration);
1003 
1008 
1009  {
1010  static PH_SETTING_CREATE settings[] =
1011  {
1012  { StringSettingType, L"ProcessHacker.UserNotes.DatabasePath", L"%APPDATA%\\Process Hacker 2\\usernotesdb.xml" }
1013  };
1014 
1015  PhAddSettings(settings, sizeof(settings) / sizeof(PH_SETTING_CREATE));
1016  }
1017  }
1018 
1019  return TRUE;
1020 }
1021 
1022 INT_PTR CALLBACK OptionsDlgProc(
1023  _In_ HWND hwndDlg,
1024  _In_ UINT uMsg,
1025  _In_ WPARAM wParam,
1026  _In_ LPARAM lParam
1027  )
1028 {
1029  switch (uMsg)
1030  {
1031  case WM_INITDIALOG:
1032  {
1033  PPH_STRING path;
1034 
1035  path = PhGetStringSetting(L"ProcessHacker.UserNotes.DatabasePath");
1036  SetDlgItemText(hwndDlg, IDC_DATABASE, path->Buffer);
1037  PhDereferenceObject(path);
1038  }
1039  break;
1040  case WM_COMMAND:
1041  {
1042  switch (LOWORD(wParam))
1043  {
1044  case IDCANCEL:
1045  EndDialog(hwndDlg, IDCANCEL);
1046  break;
1047  case IDOK:
1048  {
1049  PhSetStringSetting2(L"ProcessHacker.UserNotes.DatabasePath",
1050  &PhaGetDlgItemText(hwndDlg, IDC_DATABASE)->sr);
1051 
1052  EndDialog(hwndDlg, IDOK);
1053  }
1054  break;
1055  case IDC_BROWSE:
1056  {
1057  static PH_FILETYPE_FILTER filters[] =
1058  {
1059  { L"XML files (*.xml)", L"*.xml" },
1060  { L"All files (*.*)", L"*.*" }
1061  };
1062  PVOID fileDialog;
1063  PPH_STRING fileName;
1064 
1065  fileDialog = PhCreateOpenFileDialog();
1066  PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER));
1067 
1068  fileName = PhGetFileName(PhaGetDlgItemText(hwndDlg, IDC_DATABASE));
1069  PhSetFileDialogFileName(fileDialog, fileName->Buffer);
1070  PhDereferenceObject(fileName);
1071 
1072  if (PhShowFileDialog(hwndDlg, fileDialog))
1073  {
1074  fileName = PhGetFileDialogFileName(fileDialog);
1075  SetDlgItemText(hwndDlg, IDC_DATABASE, fileName->Buffer);
1076  PhDereferenceObject(fileName);
1077  }
1078 
1079  PhFreeFileDialog(fileDialog);
1080  }
1081  break;
1082  }
1083  }
1084  break;
1085  }
1086 
1087  return FALSE;
1088 }
1089 
1090 INT_PTR CALLBACK ProcessCommentPageDlgProc(
1091  _In_ HWND hwndDlg,
1092  _In_ UINT uMsg,
1093  _In_ WPARAM wParam,
1094  _In_ LPARAM lParam
1095  )
1096 {
1097  LPPROPSHEETPAGE propSheetPage;
1098  PPH_PROCESS_PROPPAGECONTEXT propPageContext;
1099  PPH_PROCESS_ITEM processItem;
1101 
1102  if (PhPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext, &processItem))
1103  {
1104  context = propPageContext->Context;
1105  }
1106  else
1107  {
1108  return FALSE;
1109  }
1110 
1111  switch (uMsg)
1112  {
1113  case WM_INITDIALOG:
1114  {
1115  PDB_OBJECT object;
1116  PPH_STRING comment;
1117 
1118  context = PhAllocate(sizeof(PROCESS_COMMENT_PAGE_CONTEXT));
1119  propPageContext->Context = context;
1120 
1121  // Load the comment.
1122 
1123  SendMessage(GetDlgItem(hwndDlg, IDC_COMMENT), EM_SETLIMITTEXT, UNICODE_STRING_MAX_CHARS, 0);
1124 
1125  LockDb();
1126 
1127  if (object = FindDbObjectForProcess(processItem, INTENT_PROCESS_COMMENT))
1128  {
1129  PhSetReference(&comment, object->Comment);
1130 
1131  if (processItem->CommandLine && (object = FindDbObject(COMMAND_LINE_TAG, &processItem->CommandLine->sr)) && object->Comment->Length != 0)
1132  {
1133  Button_SetCheck(GetDlgItem(hwndDlg, IDC_MATCHCOMMANDLINE), BST_CHECKED);
1134  }
1135  }
1136  else
1137  {
1138  comment = PhReferenceEmptyString();
1139  }
1140 
1141  UnlockDb();
1142 
1143  SetDlgItemText(hwndDlg, IDC_COMMENT, comment->Buffer);
1144  context->OriginalComment = comment;
1145 
1146  if (!processItem->CommandLine)
1147  EnableWindow(GetDlgItem(hwndDlg, IDC_MATCHCOMMANDLINE), FALSE);
1148  }
1149  break;
1150  case WM_DESTROY:
1151  {
1152  PDB_OBJECT object;
1153  PPH_STRING comment;
1154  BOOLEAN matchCommandLine;
1155  BOOLEAN done = FALSE;
1156 
1157  comment = PhGetWindowText(GetDlgItem(hwndDlg, IDC_COMMENT));
1158  matchCommandLine = Button_GetCheck(GetDlgItem(hwndDlg, IDC_MATCHCOMMANDLINE)) == BST_CHECKED;
1159 
1160  if (!processItem->CommandLine)
1161  matchCommandLine = FALSE;
1162 
1163  LockDb();
1164 
1165  if (processItem->CommandLine && !matchCommandLine)
1166  {
1167  PDB_OBJECT objectForProcessName;
1168  PPH_STRING message = NULL;
1169 
1170  object = FindDbObject(COMMAND_LINE_TAG, &processItem->CommandLine->sr);
1171  objectForProcessName = FindDbObject(FILE_TAG, &processItem->ProcessName->sr);
1172 
1173  if (object && objectForProcessName && object->Comment->Length != 0 && objectForProcessName->Comment->Length != 0 &&
1174  !PhEqualString(comment, objectForProcessName->Comment, FALSE))
1175  {
1176  message = PhFormatString(
1177  L"Do you want to replace the comment for %s which is currently\n \"%s\"\n"
1178  L"with\n \"%s\"?",
1179  processItem->ProcessName->Buffer,
1180  objectForProcessName->Comment->Buffer,
1181  comment->Buffer
1182  );
1183  }
1184 
1185  if (object)
1186  {
1189  }
1190 
1191  if (message)
1192  {
1193  // Prevent deadlocks.
1194  UnlockDb();
1195 
1196  if (MessageBox(hwndDlg, message->Buffer, L"Comment", MB_ICONQUESTION | MB_YESNO) == IDNO)
1197  {
1198  done = TRUE;
1199  }
1200 
1201  LockDb();
1202  PhDereferenceObject(message);
1203  }
1204  }
1205 
1206  if (!done)
1207  {
1208  if (comment->Length != 0)
1209  {
1210  if (matchCommandLine)
1211  CreateDbObject(COMMAND_LINE_TAG, &processItem->CommandLine->sr, comment);
1212  else
1213  CreateDbObject(FILE_TAG, &processItem->ProcessName->sr, comment);
1214  }
1215  else
1216  {
1217  if (
1218  (!matchCommandLine && (object = FindDbObject(FILE_TAG, &processItem->ProcessName->sr))) ||
1219  (matchCommandLine && (object = FindDbObject(COMMAND_LINE_TAG, &processItem->CommandLine->sr)))
1220  )
1221  {
1224  }
1225  }
1226  }
1227 
1228  UnlockDb();
1229 
1230  PhDereferenceObject(comment);
1231 
1232  PhDereferenceObject(context->OriginalComment);
1233  PhFree(context);
1234 
1235  PhPropPageDlgProcDestroy(hwndDlg);
1236 
1237  SaveDb();
1239  }
1240  break;
1241  case WM_SHOWWINDOW:
1242  {
1243  PPH_LAYOUT_ITEM dialogItem;
1244 
1245  if (dialogItem = PhBeginPropPageLayout(hwndDlg, propPageContext))
1246  {
1247  PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_COMMENT), dialogItem, PH_ANCHOR_ALL);
1248  PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_REVERT), dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_BOTTOM);
1249  PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_MATCHCOMMANDLINE), dialogItem, PH_ANCHOR_RIGHT | PH_ANCHOR_BOTTOM);
1250  PhEndPropPageLayout(hwndDlg, propPageContext);
1251  }
1252  }
1253  break;
1254  case WM_COMMAND:
1255  {
1256  switch (LOWORD(wParam))
1257  {
1258  case IDC_COMMENT:
1259  {
1260  if (HIWORD(wParam) == EN_CHANGE)
1261  EnableWindow(GetDlgItem(hwndDlg, IDC_REVERT), TRUE);
1262  }
1263  break;
1264  case IDC_REVERT:
1265  {
1266  SetDlgItemText(hwndDlg, IDC_COMMENT, context->OriginalComment->Buffer);
1267  SendMessage(GetDlgItem(hwndDlg, IDC_COMMENT), EM_SETSEL, 0, -1);
1268  SetFocus(GetDlgItem(hwndDlg, IDC_COMMENT));
1269  EnableWindow(GetDlgItem(hwndDlg, IDC_REVERT), FALSE);
1270  }
1271  break;
1272  }
1273  }
1274  break;
1275  }
1276 
1277  return FALSE;
1278 }
1279 
1280 INT_PTR CALLBACK ServiceCommentPageDlgProc(
1281  _In_ HWND hwndDlg,
1282  _In_ UINT uMsg,
1283  _In_ WPARAM wParam,
1284  _In_ LPARAM lParam
1285  )
1286 {
1288 
1289  if (uMsg == WM_INITDIALOG)
1290  {
1291  context = PhAllocate(sizeof(SERVICE_COMMENT_PAGE_CONTEXT));
1292  memset(context, 0, sizeof(SERVICE_COMMENT_PAGE_CONTEXT));
1293 
1294  SetProp(hwndDlg, L"Context", (HANDLE)context);
1295  }
1296  else
1297  {
1298  context = (PSERVICE_COMMENT_PAGE_CONTEXT)GetProp(hwndDlg, L"Context");
1299 
1300  if (uMsg == WM_DESTROY)
1301  RemoveProp(hwndDlg, L"Context");
1302  }
1303 
1304  if (!context)
1305  return FALSE;
1306 
1307  switch (uMsg)
1308  {
1309  case WM_INITDIALOG:
1310  {
1311  LPPROPSHEETPAGE propSheetPage = (LPPROPSHEETPAGE)lParam;
1312  PPH_SERVICE_ITEM serviceItem = (PPH_SERVICE_ITEM)propSheetPage->lParam;
1313  PDB_OBJECT object;
1314  PPH_STRING comment;
1315 
1316  context->ServiceItem = serviceItem;
1317 
1318  PhInitializeLayoutManager(&context->LayoutManager, hwndDlg);
1319  PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_COMMENT), NULL, PH_ANCHOR_ALL);
1320 
1321  // Load the comment.
1322 
1323  SendMessage(GetDlgItem(hwndDlg, IDC_COMMENT), EM_SETLIMITTEXT, UNICODE_STRING_MAX_CHARS, 0);
1324 
1325  LockDb();
1326 
1327  if (object = FindDbObject(SERVICE_TAG, &serviceItem->Name->sr))
1328  PhSetReference(&comment, object->Comment);
1329  else
1330  comment = PhReferenceEmptyString();
1331 
1332  UnlockDb();
1333 
1334  SetDlgItemText(hwndDlg, IDC_COMMENT, comment->Buffer);
1335  PhDereferenceObject(comment);
1336  }
1337  break;
1338  case WM_DESTROY:
1339  {
1340  PhDeleteLayoutManager(&context->LayoutManager);
1341  PhFree(context);
1342  }
1343  break;
1344  case WM_SIZE:
1345  {
1346  PhLayoutManagerLayout(&context->LayoutManager);
1347  }
1348  break;
1349  case WM_COMMAND:
1350  {
1351  switch (LOWORD(wParam))
1352  {
1353  case IDC_COMMENT:
1354  {
1355  if (HIWORD(wParam) == EN_CHANGE)
1356  EnableWindow(GetDlgItem(hwndDlg, IDC_REVERT), TRUE);
1357  }
1358  break;
1359  }
1360  }
1361  break;
1362  case WM_NOTIFY:
1363  {
1364  LPNMHDR header = (LPNMHDR)lParam;
1365 
1366  switch (header->code)
1367  {
1368  case PSN_KILLACTIVE:
1369  {
1370  SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, FALSE);
1371  }
1372  return TRUE;
1373  case PSN_APPLY:
1374  {
1375  PDB_OBJECT object;
1376  PPH_STRING comment;
1377 
1378  comment = PhGetWindowText(GetDlgItem(hwndDlg, IDC_COMMENT));
1379 
1380  LockDb();
1381 
1382  if (comment->Length != 0)
1383  {
1384  CreateDbObject(SERVICE_TAG, &context->ServiceItem->Name->sr, comment);
1385  }
1386  else
1387  {
1388  if (object = FindDbObject(SERVICE_TAG, &context->ServiceItem->Name->sr))
1389  DeleteDbObject(object);
1390  }
1391 
1392  UnlockDb();
1393 
1394  PhDereferenceObject(comment);
1395 
1396  SaveDb();
1398 
1399  SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR);
1400  }
1401  return TRUE;
1402  }
1403  }
1404  break;
1405  }
1406 
1407  return FALSE;
1408 }