Process Hacker
srvlist.c
Go to the documentation of this file.
1 /*
2  * Process Hacker -
3  * service list
4  *
5  * Copyright (C) 2010-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 <phapp.h>
24 #include <settings.h>
25 #include <extmgri.h>
26 #include <phplug.h>
27 #include <cpysave.h>
28 #include <emenu.h>
29 
31  _In_ PVOID Entry1,
32  _In_ PVOID Entry2
33  );
34 
36  _In_ PVOID Entry
37  );
38 
40  _In_ PPH_SERVICE_NODE ServiceNode
41  );
42 
44  _In_ LONG Result,
45  _In_ PVOID Node1,
46  _In_ PVOID Node2,
47  _In_ PH_SORT_ORDER SortOrder
48  );
49 
50 BOOLEAN NTAPI PhpServiceTreeNewCallback(
51  _In_ HWND hwnd,
52  _In_ PH_TREENEW_MESSAGE Message,
53  _In_opt_ PVOID Parameter1,
54  _In_opt_ PVOID Parameter2,
55  _In_opt_ PVOID Context
56  );
57 
58 static HWND ServiceTreeListHandle;
59 static ULONG ServiceTreeListSortColumn;
60 static PH_SORT_ORDER ServiceTreeListSortOrder;
61 static PH_CM_MANAGER ServiceTreeListCm;
62 
63 static PPH_HASHTABLE ServiceNodeHashtable; // hashtable of all nodes
64 static PPH_LIST ServiceNodeList; // list of all nodes
65 
66 static PH_TN_FILTER_SUPPORT FilterSupport;
67 
68 static HICON ServiceApplicationIcon;
69 static HICON ServiceApplicationGoIcon;
70 static HICON ServiceCogIcon;
71 static HICON ServiceCogGoIcon;
72 
74 static PPH_POINTER_LIST ServiceNodeStateList = NULL; // list of nodes which need to be processed
75 
77  VOID
78  )
79 {
80  ServiceNodeHashtable = PhCreateHashtable(
81  sizeof(PPH_SERVICE_NODE),
84  100
85  );
86  ServiceNodeList = PhCreateList(100);
87 }
88 
90  _In_ PVOID Entry1,
91  _In_ PVOID Entry2
92  )
93 {
94  PPH_SERVICE_NODE serviceNode1 = *(PPH_SERVICE_NODE *)Entry1;
95  PPH_SERVICE_NODE serviceNode2 = *(PPH_SERVICE_NODE *)Entry2;
96 
97  return serviceNode1->ServiceItem == serviceNode2->ServiceItem;
98 }
99 
101  _In_ PVOID Entry
102  )
103 {
104  return PhHashIntPtr((ULONG_PTR)(*(PPH_SERVICE_NODE *)Entry)->ServiceItem);
105 }
106 
108  _In_ HWND hwnd
109  )
110 {
111  ServiceApplicationIcon = PH_LOAD_SHARED_IMAGE(MAKEINTRESOURCE(IDI_PHAPPLICATION), IMAGE_ICON);
112  ServiceApplicationGoIcon = PH_LOAD_SHARED_IMAGE(MAKEINTRESOURCE(IDI_PHAPPLICATIONGO), IMAGE_ICON);
113  ServiceCogIcon = PH_LOAD_SHARED_IMAGE(MAKEINTRESOURCE(IDI_COG), IMAGE_ICON);
114  ServiceCogGoIcon = PH_LOAD_SHARED_IMAGE(MAKEINTRESOURCE(IDI_COGGO), IMAGE_ICON);
115 
116  ServiceTreeListHandle = hwnd;
117  PhSetControlTheme(ServiceTreeListHandle, L"explorer");
118  SendMessage(TreeNew_GetTooltips(ServiceTreeListHandle), TTM_SETDELAYTIME, TTDT_AUTOPOP, MAXSHORT);
119 
121 
122  TreeNew_SetRedraw(hwnd, FALSE);
123 
124  // Default columns
125  PhAddTreeNewColumn(hwnd, PHSVTLC_NAME, TRUE, L"Name", 140, PH_ALIGN_LEFT, 0, 0);
126  PhAddTreeNewColumn(hwnd, PHSVTLC_DISPLAYNAME, TRUE, L"Display Name", 220, PH_ALIGN_LEFT, 1, 0);
127  PhAddTreeNewColumn(hwnd, PHSVTLC_TYPE, TRUE, L"Type", 100, PH_ALIGN_LEFT, 2, 0);
128  PhAddTreeNewColumn(hwnd, PHSVTLC_STATUS, TRUE, L"Status", 70, PH_ALIGN_LEFT, 3, 0);
129  PhAddTreeNewColumn(hwnd, PHSVTLC_STARTTYPE, TRUE, L"Start Type", 130, PH_ALIGN_LEFT, 4, 0);
130  PhAddTreeNewColumn(hwnd, PHSVTLC_PID, TRUE, L"PID", 50, PH_ALIGN_RIGHT, 5, DT_RIGHT);
131 
132  PhAddTreeNewColumn(hwnd, PHSVTLC_BINARYPATH, FALSE, L"Binary Path", 180, PH_ALIGN_LEFT, -1, DT_PATH_ELLIPSIS);
133  PhAddTreeNewColumn(hwnd, PHSVTLC_ERRORCONTROL, FALSE, L"Error Control", 70, PH_ALIGN_LEFT, -1, 0);
134  PhAddTreeNewColumn(hwnd, PHSVTLC_GROUP, FALSE, L"Group", 100, PH_ALIGN_LEFT, -1, 0);
135  PhAddTreeNewColumn(hwnd, PHSVTLC_DESCRIPTION, FALSE, L"Description", 200, PH_ALIGN_LEFT, -1, 0);
136 
137  TreeNew_SetRedraw(hwnd, TRUE);
138 
140 
142 
143  if (PhPluginsEnabled)
144  {
145  PH_PLUGIN_TREENEW_INFORMATION treeNewInfo;
146 
147  treeNewInfo.TreeNewHandle = hwnd;
148  treeNewInfo.CmData = &ServiceTreeListCm;
150  }
151 
152  PhInitializeTreeNewFilterSupport(&FilterSupport, hwnd, ServiceNodeList);
153 }
154 
156  VOID
157  )
158 {
159  PPH_STRING settings;
160  PPH_STRING sortSettings;
161 
162  settings = PhGetStringSetting(L"ServiceTreeListColumns");
163  sortSettings = PhGetStringSetting(L"ServiceTreeListSort");
164  PhCmLoadSettingsEx(ServiceTreeListHandle, &ServiceTreeListCm, 0, &settings->sr, &sortSettings->sr);
165  PhDereferenceObject(settings);
166  PhDereferenceObject(sortSettings);
167 }
168 
170  VOID
171  )
172 {
173  PPH_STRING settings;
174  PPH_STRING sortSettings;
175 
176  settings = PhCmSaveSettingsEx(ServiceTreeListHandle, &ServiceTreeListCm, 0, &sortSettings);
177  PhSetStringSetting2(L"ServiceTreeListColumns", &settings->sr);
178  PhSetStringSetting2(L"ServiceTreeListSort", &sortSettings->sr);
179  PhDereferenceObject(settings);
180  PhDereferenceObject(sortSettings);
181 }
182 
184  VOID
185  )
186 {
187  return &FilterSupport;
188 }
189 
191  _In_ PPH_SERVICE_ITEM ServiceItem,
192  _In_ ULONG RunId
193  )
194 {
195  PPH_SERVICE_NODE serviceNode;
196 
197  serviceNode = PhAllocate(PhEmGetObjectSize(EmServiceNodeType, sizeof(PH_SERVICE_NODE)));
198  memset(serviceNode, 0, sizeof(PH_SERVICE_NODE));
199  PhInitializeTreeNewNode(&serviceNode->Node);
200 
201  if (PhServiceTreeListStateHighlighting && RunId != 1)
202  {
204  &serviceNode->Node,
205  &serviceNode->ShState,
206  &ServiceNodeStateList,
207  NewItemState,
208  PhCsColorNew,
209  NULL
210  );
211  }
212 
213  serviceNode->ServiceItem = ServiceItem;
214  PhReferenceObject(ServiceItem);
215 
216  memset(serviceNode->TextCache, 0, sizeof(PH_STRINGREF) * PHSVTLC_MAXIMUM);
217  serviceNode->Node.TextCache = serviceNode->TextCache;
218  serviceNode->Node.TextCacheSize = PHSVTLC_MAXIMUM;
219 
220  PhAddEntryHashtable(ServiceNodeHashtable, &serviceNode);
221  PhAddItemList(ServiceNodeList, serviceNode);
222 
223  if (FilterSupport.FilterList)
224  serviceNode->Node.Visible = PhApplyTreeNewFiltersToNode(&FilterSupport, &serviceNode->Node);
225 
227 
228  TreeNew_NodesStructured(ServiceTreeListHandle);
229 
230  return serviceNode;
231 }
232 
234  _In_ PPH_SERVICE_ITEM ServiceItem
235  )
236 {
237  PH_SERVICE_NODE lookupServiceNode;
238  PPH_SERVICE_NODE lookupServiceNodePtr = &lookupServiceNode;
239  PPH_SERVICE_NODE *serviceNode;
240 
241  lookupServiceNode.ServiceItem = ServiceItem;
242 
243  serviceNode = (PPH_SERVICE_NODE *)PhFindEntryHashtable(
244  ServiceNodeHashtable,
245  &lookupServiceNodePtr
246  );
247 
248  if (serviceNode)
249  return *serviceNode;
250  else
251  return NULL;
252 }
253 
255  _In_ PPH_SERVICE_NODE ServiceNode
256  )
257 {
258  // Remove from the hashtable here to avoid problems in case the key is re-used.
259  PhRemoveEntryHashtable(ServiceNodeHashtable, &ServiceNode);
260 
262  {
264  &ServiceNode->Node,
265  &ServiceNode->ShState,
266  &ServiceNodeStateList,
269  ServiceTreeListHandle
270  );
271  }
272  else
273  {
274  PhpRemoveServiceNode(ServiceNode);
275  }
276 }
277 
279  _In_ PPH_SERVICE_NODE ServiceNode
280  )
281 {
282  ULONG index;
283 
285 
286  // Remove from list and cleanup.
287 
288  if ((index = PhFindItemList(ServiceNodeList, ServiceNode)) != -1)
289  PhRemoveItemList(ServiceNodeList, index);
290 
291  if (ServiceNode->BinaryPath) PhDereferenceObject(ServiceNode->BinaryPath);
292  if (ServiceNode->LoadOrderGroup) PhDereferenceObject(ServiceNode->LoadOrderGroup);
293  if (ServiceNode->Description) PhDereferenceObject(ServiceNode->Description);
294 
295  if (ServiceNode->TooltipText) PhDereferenceObject(ServiceNode->TooltipText);
296 
297  PhDereferenceObject(ServiceNode->ServiceItem);
298 
299  PhFree(ServiceNode);
300 
301  TreeNew_NodesStructured(ServiceTreeListHandle);
302 }
303 
305  _In_ PPH_SERVICE_NODE ServiceNode
306  )
307 {
308  memset(ServiceNode->TextCache, 0, sizeof(PH_STRINGREF) * PHSVTLC_MAXIMUM);
309  PhClearReference(&ServiceNode->TooltipText);
310 
311  ServiceNode->ValidMask = 0;
312  PhInvalidateTreeNewNode(&ServiceNode->Node, TN_CACHE_ICON);
313  TreeNew_NodesStructured(ServiceTreeListHandle);
314 }
315 
317  VOID
318  )
319 {
320  if (ServiceTreeListSortOrder != NoSortOrder && ServiceTreeListSortColumn >= PHSVTLC_MAXIMUM)
321  {
322  // Sorting is on, but it's not one of our columns. Force a rebuild. (If it was one of our
323  // columns, the restructure would have been handled in PhUpdateServiceNode.)
324  TreeNew_NodesStructured(ServiceTreeListHandle);
325  }
326 
327  PH_TICK_SH_STATE_TN(PH_SERVICE_NODE, ShState, ServiceNodeStateList, PhpRemoveServiceNode, PhCsHighlightingDuration, ServiceTreeListHandle, TRUE, NULL);
328 }
329 
330 static VOID PhpUpdateServiceNodeConfig(
331  _Inout_ PPH_SERVICE_NODE ServiceNode
332  )
333 {
334  if (!(ServiceNode->ValidMask & PHSN_CONFIG))
335  {
336  SC_HANDLE serviceHandle;
337  LPQUERY_SERVICE_CONFIG serviceConfig;
338 
339  if (serviceHandle = PhOpenService(ServiceNode->ServiceItem->Name->Buffer, SERVICE_QUERY_CONFIG))
340  {
341  if (serviceConfig = PhGetServiceConfig(serviceHandle))
342  {
343  if (serviceConfig->lpBinaryPathName)
344  PhMoveReference(&ServiceNode->BinaryPath, PhCreateString(serviceConfig->lpBinaryPathName));
345  if (serviceConfig->lpLoadOrderGroup)
346  PhMoveReference(&ServiceNode->LoadOrderGroup, PhCreateString(serviceConfig->lpLoadOrderGroup));
347 
348  PhFree(serviceConfig);
349  }
350 
351  CloseServiceHandle(serviceHandle);
352  }
353 
354  ServiceNode->ValidMask |= PHSN_CONFIG;
355  }
356 }
357 
358 static VOID PhpUpdateServiceNodeDescription(
359  _Inout_ PPH_SERVICE_NODE ServiceNode
360  )
361 {
362  if (!(ServiceNode->ValidMask & PHSN_DESCRIPTION))
363  {
364  SC_HANDLE serviceHandle;
365 
366  if (serviceHandle = PhOpenService(ServiceNode->ServiceItem->Name->Buffer, SERVICE_QUERY_CONFIG))
367  {
368  PhMoveReference(&ServiceNode->Description, PhGetServiceDescription(serviceHandle));
369 
370  CloseServiceHandle(serviceHandle);
371  }
372 
373  ServiceNode->ValidMask |= PHSN_DESCRIPTION;
374  }
375 }
376 
377 #define SORT_FUNCTION(Column) PhpServiceTreeNewCompare##Column
378 
379 #define BEGIN_SORT_FUNCTION(Column) static int __cdecl PhpServiceTreeNewCompare##Column( \
380  _In_ const void *_elem1, \
381  _In_ const void *_elem2 \
382  ) \
383 { \
384  PPH_SERVICE_NODE node1 = *(PPH_SERVICE_NODE *)_elem1; \
385  PPH_SERVICE_NODE node2 = *(PPH_SERVICE_NODE *)_elem2; \
386  PPH_SERVICE_ITEM serviceItem1 = node1->ServiceItem; \
387  PPH_SERVICE_ITEM serviceItem2 = node2->ServiceItem; \
388  int sortResult = 0;
389 
390 #define END_SORT_FUNCTION \
391  /* if (sortResult == 0) */ \
392  /* sortResult = PhCompareString(serviceItem1->Name, serviceItem2->Name, TRUE); */ \
393  \
394  return PhModifySort(sortResult, ServiceTreeListSortOrder); \
395 }
396 
398  _In_ LONG Result,
399  _In_ PVOID Node1,
400  _In_ PVOID Node2,
401  _In_ PH_SORT_ORDER SortOrder
402  )
403 {
404  return PhModifySort(Result, SortOrder);
405 }
406 
408 {
409  sortResult = PhCompareString(serviceItem1->Name, serviceItem2->Name, TRUE);
410 }
412 
414 {
415  sortResult = PhCompareStringWithNull(serviceItem1->DisplayName, serviceItem2->DisplayName, TRUE);
416 }
418 
420 {
421  sortResult = intcmp(serviceItem1->Type, serviceItem2->Type);
422 }
424 
426 {
427  sortResult = intcmp(serviceItem1->State, serviceItem2->State);
428 }
430 
432 {
433  sortResult = intcmp(serviceItem1->StartType, serviceItem2->StartType);
434 
435  if (sortResult == 0)
436  sortResult = intcmp(serviceItem1->DelayedStart, serviceItem2->DelayedStart);
437  if (sortResult == 0)
438  sortResult = intcmp(serviceItem1->HasTriggers, serviceItem2->HasTriggers);
439 }
441 
443 {
444  sortResult = uintptrcmp((ULONG_PTR)serviceItem1->ProcessId, (ULONG_PTR)serviceItem2->ProcessId);
445 }
447 
449 {
450  PhpUpdateServiceNodeConfig(node1);
451  PhpUpdateServiceNodeConfig(node2);
452  sortResult = PhCompareStringWithNull(node1->BinaryPath, node2->BinaryPath, TRUE);
453 }
455 
456 BEGIN_SORT_FUNCTION(ErrorControl)
457 {
458  sortResult = intcmp(serviceItem1->ErrorControl, serviceItem2->ErrorControl);
459 }
461 
463 {
464  PhpUpdateServiceNodeConfig(node1);
465  PhpUpdateServiceNodeConfig(node2);
466  sortResult = PhCompareStringWithNull(node1->LoadOrderGroup, node2->LoadOrderGroup, TRUE);
467 }
469 
471 {
472  PhpUpdateServiceNodeDescription(node1);
473  PhpUpdateServiceNodeDescription(node2);
474  sortResult = PhCompareStringWithNull(node1->Description, node2->Description, TRUE);
475 }
477 
479  _In_ HWND hwnd,
480  _In_ PH_TREENEW_MESSAGE Message,
481  _In_opt_ PVOID Parameter1,
482  _In_opt_ PVOID Parameter2,
483  _In_opt_ PVOID Context
484  )
485 {
486  PPH_SERVICE_NODE node;
487 
488  if (PhCmForwardMessage(hwnd, Message, Parameter1, Parameter2, &ServiceTreeListCm))
489  return TRUE;
490 
491  switch (Message)
492  {
493  case TreeNewGetChildren:
494  {
495  PPH_TREENEW_GET_CHILDREN getChildren = Parameter1;
496 
497  if (!getChildren->Node)
498  {
499  static PVOID sortFunctions[] =
500  {
501  SORT_FUNCTION(Name),
502  SORT_FUNCTION(DisplayName),
503  SORT_FUNCTION(Type),
504  SORT_FUNCTION(Status),
505  SORT_FUNCTION(StartType),
506  SORT_FUNCTION(Pid),
507  SORT_FUNCTION(BinaryPath),
508  SORT_FUNCTION(ErrorControl),
509  SORT_FUNCTION(Group),
510  SORT_FUNCTION(Description)
511  };
512  int (__cdecl *sortFunction)(const void *, const void *);
513 
514  if (!PhCmForwardSort(
515  (PPH_TREENEW_NODE *)ServiceNodeList->Items,
516  ServiceNodeList->Count,
517  ServiceTreeListSortColumn,
518  ServiceTreeListSortOrder,
519  &ServiceTreeListCm
520  ))
521  {
522  if (ServiceTreeListSortColumn < PHSVTLC_MAXIMUM)
523  sortFunction = sortFunctions[ServiceTreeListSortColumn];
524  else
525  sortFunction = NULL;
526 
527  if (sortFunction)
528  {
529  qsort(ServiceNodeList->Items, ServiceNodeList->Count, sizeof(PVOID), sortFunction);
530  }
531  }
532 
533  getChildren->Children = (PPH_TREENEW_NODE *)ServiceNodeList->Items;
534  getChildren->NumberOfChildren = ServiceNodeList->Count;
535  }
536  }
537  return TRUE;
538  case TreeNewIsLeaf:
539  {
540  PPH_TREENEW_IS_LEAF isLeaf = Parameter1;
541 
542  isLeaf->IsLeaf = TRUE;
543  }
544  return TRUE;
545  case TreeNewGetCellText:
546  {
547  PPH_TREENEW_GET_CELL_TEXT getCellText = Parameter1;
548  PPH_SERVICE_ITEM serviceItem;
549 
550  node = (PPH_SERVICE_NODE)getCellText->Node;
551  serviceItem = node->ServiceItem;
552 
553  switch (getCellText->Id)
554  {
555  case PHSVTLC_NAME:
556  getCellText->Text = serviceItem->Name->sr;
557  break;
558  case PHSVTLC_DISPLAYNAME:
559  getCellText->Text = serviceItem->DisplayName->sr;
560  break;
561  case PHSVTLC_TYPE:
562  PhInitializeStringRefLongHint(&getCellText->Text, PhGetServiceTypeString(serviceItem->Type));
563  break;
564  case PHSVTLC_STATUS:
565  PhInitializeStringRefLongHint(&getCellText->Text, PhGetServiceStateString(serviceItem->State));
566  break;
567  case PHSVTLC_STARTTYPE:
568  {
569  PH_FORMAT format[2];
570  PWSTR additional = NULL;
571  SIZE_T returnLength;
572 
573  PhInitFormatS(&format[0], PhGetServiceStartTypeString(serviceItem->StartType));
574 
575  if (serviceItem->DelayedStart && serviceItem->HasTriggers)
576  additional = L" (Delayed, Trigger)";
577  else if (serviceItem->DelayedStart)
578  additional = L" (Delayed)";
579  else if (serviceItem->HasTriggers)
580  additional = L" (Trigger)";
581 
582  if (additional)
583  PhInitFormatS(&format[1], additional);
584 
585  if (PhFormatToBuffer(format, 1 + (additional ? 1 : 0), node->StartTypeText,
586  sizeof(node->StartTypeText), &returnLength))
587  {
588  getCellText->Text.Buffer = node->StartTypeText;
589  getCellText->Text.Length = returnLength - sizeof(WCHAR); // minus null terminator
590  }
591  }
592  break;
593  case PHSVTLC_PID:
594  PhInitializeStringRefLongHint(&getCellText->Text, serviceItem->ProcessIdString);
595  break;
596  case PHSVTLC_BINARYPATH:
597  PhpUpdateServiceNodeConfig(node);
598  getCellText->Text = PhGetStringRef(node->BinaryPath);
599  break;
602  break;
603  case PHSVTLC_GROUP:
604  PhpUpdateServiceNodeConfig(node);
605  getCellText->Text = PhGetStringRef(node->LoadOrderGroup);
606  break;
607  case PHSVTLC_DESCRIPTION:
608  PhpUpdateServiceNodeDescription(node);
609  getCellText->Text = PhGetStringRef(node->Description);
610  break;
611  default:
612  return FALSE;
613  }
614 
615  getCellText->Flags = TN_CACHE;
616  }
617  return TRUE;
618  case TreeNewGetNodeIcon:
619  {
620  PPH_TREENEW_GET_NODE_ICON getNodeIcon = Parameter1;
621 
622  node = (PPH_SERVICE_NODE)getNodeIcon->Node;
623 
624  if (node->ServiceItem->Type == SERVICE_KERNEL_DRIVER || node->ServiceItem->Type == SERVICE_FILE_SYSTEM_DRIVER)
625  {
626  if (node->ServiceItem->State == SERVICE_RUNNING)
627  getNodeIcon->Icon = ServiceCogGoIcon;
628  else
629  getNodeIcon->Icon = ServiceCogIcon;
630  }
631  else
632  {
633  if (node->ServiceItem->State == SERVICE_RUNNING)
634  getNodeIcon->Icon = ServiceApplicationGoIcon;
635  else
636  getNodeIcon->Icon = ServiceApplicationIcon;
637  }
638 
639  getNodeIcon->Flags = TN_CACHE;
640  }
641  return TRUE;
643  {
644  PPH_TREENEW_GET_CELL_TOOLTIP getCellTooltip = Parameter1;
645 
646  node = (PPH_SERVICE_NODE)getCellTooltip->Node;
647 
648  if (getCellTooltip->Column->Id != 0)
649  return FALSE;
650 
651  if (!node->TooltipText)
653 
655  {
656  getCellTooltip->Text = node->TooltipText->sr;
657  getCellTooltip->Unfolding = FALSE;
658  getCellTooltip->MaximumWidth = 550;
659  }
660  else
661  {
662  return FALSE;
663  }
664  }
665  return TRUE;
666  case TreeNewSortChanged:
667  {
668  TreeNew_GetSort(hwnd, &ServiceTreeListSortColumn, &ServiceTreeListSortOrder);
669  // Force a rebuild to sort the items.
671  }
672  return TRUE;
673  case TreeNewKeyDown:
674  {
675  PPH_TREENEW_KEY_EVENT keyEvent = Parameter1;
676 
677  switch (keyEvent->VirtualKey)
678  {
679  case 'C':
680  if (GetKeyState(VK_CONTROL) < 0)
681  SendMessage(PhMainWndHandle, WM_COMMAND, ID_SERVICE_COPY, 0);
682  break;
683  case 'A':
684  if (GetKeyState(VK_CONTROL) < 0)
685  TreeNew_SelectRange(ServiceTreeListHandle, 0, -1);
686  break;
687  case VK_DELETE:
688  SendMessage(PhMainWndHandle, WM_COMMAND, ID_SERVICE_DELETE, 0);
689  break;
690  case VK_RETURN:
691  if (GetKeyState(VK_CONTROL) >= 0)
692  SendMessage(PhMainWndHandle, WM_COMMAND, ID_SERVICE_PROPERTIES, 0);
693  else
694  SendMessage(PhMainWndHandle, WM_COMMAND, ID_SERVICE_OPENFILELOCATION, 0);
695  break;
696  }
697  }
698  return TRUE;
700  {
702 
703  data.TreeNewHandle = hwnd;
704  data.MouseEvent = Parameter1;
705  data.DefaultSortColumn = 0;
708 
713  }
714  return TRUE;
716  {
717  SendMessage(PhMainWndHandle, WM_COMMAND, ID_SERVICE_PROPERTIES, 0);
718  }
719  return TRUE;
720  case TreeNewContextMenu:
721  {
722  PPH_TREENEW_CONTEXT_MENU contextMenu = Parameter1;
723 
724  PhShowServiceContextMenu(contextMenu);
725  }
726  return TRUE;
727  }
728 
729  return FALSE;
730 }
731 
733  VOID
734  )
735 {
736  PPH_SERVICE_ITEM serviceItem = NULL;
737  ULONG i;
738 
739  for (i = 0; i < ServiceNodeList->Count; i++)
740  {
741  PPH_SERVICE_NODE node = ServiceNodeList->Items[i];
742 
743  if (node->Node.Selected)
744  {
745  serviceItem = node->ServiceItem;
746  break;
747  }
748  }
749 
750  return serviceItem;
751 }
752 
754  _Out_ PPH_SERVICE_ITEM **Services,
755  _Out_ PULONG NumberOfServices
756  )
757 {
758  PPH_LIST list;
759  ULONG i;
760 
761  list = PhCreateList(2);
762 
763  for (i = 0; i < ServiceNodeList->Count; i++)
764  {
765  PPH_SERVICE_NODE node = ServiceNodeList->Items[i];
766 
767  if (node->Node.Selected)
768  {
769  PhAddItemList(list, node->ServiceItem);
770  }
771  }
772 
773  *Services = PhAllocateCopy(list->Items, sizeof(PVOID) * list->Count);
774  *NumberOfServices = list->Count;
775 
776  PhDereferenceObject(list);
777 }
778 
780  VOID
781  )
782 {
783  TreeNew_DeselectRange(ServiceTreeListHandle, 0, -1);
784 }
785 
787  _In_ PPH_SERVICE_NODE ServiceNode
788  )
789 {
791 
792  if (!ServiceNode->Node.Visible)
793  return;
794 
795  TreeNew_SetFocusNode(ServiceTreeListHandle, &ServiceNode->Node);
796  TreeNew_SetMarkNode(ServiceTreeListHandle, &ServiceNode->Node);
797  TreeNew_SelectRange(ServiceTreeListHandle, ServiceNode->Node.Index, ServiceNode->Node.Index);
798  TreeNew_EnsureVisible(ServiceTreeListHandle, &ServiceNode->Node);
799 }
800 
802  VOID
803  )
804 {
805  PPH_STRING text;
806 
807  text = PhGetTreeNewText(ServiceTreeListHandle, 0);
808  PhSetClipboardString(ServiceTreeListHandle, &text->sr);
809  PhDereferenceObject(text);
810 }
811 
813  _Inout_ PPH_FILE_STREAM FileStream,
814  _In_ ULONG Mode
815  )
816 {
817  PPH_LIST lines;
818  ULONG i;
819 
820  lines = PhGetGenericTreeNewLines(ServiceTreeListHandle, Mode);
821 
822  for (i = 0; i < lines->Count; i++)
823  {
824  PPH_STRING line;
825 
826  line = lines->Items[i];
827  PhWriteStringAsUtf8FileStream(FileStream, &line->sr);
828  PhDereferenceObject(line);
829  PhWriteStringAsUtf8FileStream2(FileStream, L"\r\n");
830  }
831 
832  PhDereferenceObject(lines);
833 }