Process Hacker
appsup.c
Go to the documentation of this file.
1 /*
2  * Process Hacker -
3  * application support functions
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 <symprv.h>
26 #include <cpysave.h>
27 #include <phappres.h>
28 #include <emenu.h>
29 #include <phsvccl.h>
30 #include "mxml/mxml.h"
31 #include <winsta.h>
32 #include <dbghelp.h>
33 #include <appmodel.h>
34 
35 typedef LONG (WINAPI *_GetPackageFullName)(
36  _In_ HANDLE hProcess,
37  _Inout_ UINT32 *packageFullNameLength,
38  _Out_opt_ PWSTR packageFullName
39  );
40 
41 typedef LONG (WINAPI *_GetPackagePath)(
42  _In_ PACKAGE_ID *packageId,
43  _Reserved_ UINT32 reserved,
44  _Inout_ UINT32 *pathLength,
45  _Out_opt_ PWSTR path
46  );
47 
48 typedef LONG (WINAPI *_PackageIdFromFullName)(
49  _In_ PCWSTR packageFullName,
50  _In_ UINT32 flags,
51  _Inout_ UINT32 *bufferLength,
52  _Out_opt_ BYTE *buffer
53  );
54 
55 GUID XP_CONTEXT_GUID = { 0xbeb1b341, 0x6837, 0x4c83, { 0x83, 0x66, 0x2b, 0x45, 0x1e, 0x7c, 0xe6, 0x9b } };
56 GUID VISTA_CONTEXT_GUID = { 0xe2011457, 0x1546, 0x43c5, { 0xa5, 0xfe, 0x00, 0x8d, 0xee, 0xe3, 0xd3, 0xf0 } };
57 GUID WIN7_CONTEXT_GUID = { 0x35138b9a, 0x5d96, 0x4fbd, { 0x8e, 0x2d, 0xa2, 0x44, 0x02, 0x25, 0xf9, 0x3a } };
58 GUID WIN8_CONTEXT_GUID = { 0x4a2f28e3, 0x53b9, 0x4441, { 0xba, 0x9c, 0xd6, 0x9d, 0x4a, 0x4a, 0x6e, 0x38 } };
59 GUID WINBLUE_CONTEXT_GUID = { 0x1f676c76, 0x80e1, 0x4239, { 0x95, 0xbb, 0x83, 0xd0, 0xf6, 0xd0, 0xda, 0x78 } };
60 
68  _In_ PSYSTEM_PROCESS_INFORMATION Process
69  )
70 {
71  ULONG i;
72 
73  for (i = 0; i < Process->NumberOfThreads; i++)
74  {
75  if (
76  Process->Threads[i].ThreadState != Waiting ||
77  Process->Threads[i].WaitReason != Suspended
78  )
79  return FALSE;
80  }
81 
82  return Process->NumberOfThreads != 0;
83 }
84 
93  _In_ HANDLE ProcessHandle,
94  _Out_ PGUID Guid
95  )
96 {
97  NTSTATUS status;
98  PROCESS_BASIC_INFORMATION basicInfo;
99 #ifdef _WIN64
100  PVOID peb32;
101  ULONG data32;
102 #endif
103  PVOID data;
104 
105  // Reverse-engineered from WdcGetProcessSwitchContext (wdc.dll).
106  // On Windows 8, the function is now SdbGetAppCompatData (apphelp.dll).
107 
108 #ifdef _WIN64
109  if (NT_SUCCESS(PhGetProcessPeb32(ProcessHandle, &peb32)) && peb32)
110  {
111  if (WindowsVersion >= WINDOWS_8)
112  {
113  if (!NT_SUCCESS(status = PhReadVirtualMemory(
114  ProcessHandle,
115  PTR_ADD_OFFSET(peb32, FIELD_OFFSET(PEB32, pShimData)),
116  &data32,
117  sizeof(ULONG),
118  NULL
119  )))
120  return status;
121  }
122  else
123  {
124  if (!NT_SUCCESS(status = PhReadVirtualMemory(
125  ProcessHandle,
126  PTR_ADD_OFFSET(peb32, FIELD_OFFSET(PEB32, pContextData)),
127  &data32,
128  sizeof(ULONG),
129  NULL
130  )))
131  return status;
132  }
133 
134  data = UlongToPtr(data32);
135  }
136  else
137  {
138 #endif
139  if (!NT_SUCCESS(status = PhGetProcessBasicInformation(ProcessHandle, &basicInfo)))
140  return status;
141 
142  if (WindowsVersion >= WINDOWS_8)
143  {
144  if (!NT_SUCCESS(status = PhReadVirtualMemory(
145  ProcessHandle,
146  PTR_ADD_OFFSET(basicInfo.PebBaseAddress, FIELD_OFFSET(PEB, pShimData)),
147  &data,
148  sizeof(PVOID),
149  NULL
150  )))
151  return status;
152  }
153  else
154  {
155  if (!NT_SUCCESS(status = PhReadVirtualMemory(
156  ProcessHandle,
157  PTR_ADD_OFFSET(basicInfo.PebBaseAddress, FIELD_OFFSET(PEB, pContextData)),
158  &data,
159  sizeof(PVOID),
160  NULL
161  )))
162  return status;
163  }
164 #ifdef _WIN64
165  }
166 #endif
167 
168  if (!data)
169  return STATUS_UNSUCCESSFUL; // no compatibility context data
170 
172  {
173  if (!NT_SUCCESS(status = PhReadVirtualMemory(
174  ProcessHandle,
175  PTR_ADD_OFFSET(data, 2040 + 16), // Magic value from SbReadProcContextByHandle
176  Guid,
177  sizeof(GUID),
178  NULL
179  )))
180  return status;
181  }
182  else if (WindowsVersion >= WINDOWS_8)
183  {
184  if (!NT_SUCCESS(status = PhReadVirtualMemory(
185  ProcessHandle,
186  PTR_ADD_OFFSET(data, 2040), // Magic value from SbReadProcContextByHandle
187  Guid,
188  sizeof(GUID),
189  NULL
190  )))
191  return status;
192  }
193  else
194  {
195  if (!NT_SUCCESS(status = PhReadVirtualMemory(
196  ProcessHandle,
197  PTR_ADD_OFFSET(data, 32), // Magic value from WdcGetProcessSwitchContext
198  Guid,
199  sizeof(GUID),
200  NULL
201  )))
202  return status;
203  }
204 
205  return STATUS_SUCCESS;
206 }
207 
209  _In_ HANDLE ProcessHandle
210  )
211 {
212  static _GetPackageFullName getPackageFullName = NULL;
213 
214  LONG result;
215  PPH_STRING name;
216  ULONG nameLength;
217 
218  if (!getPackageFullName)
219  getPackageFullName = PhGetModuleProcAddress(L"kernel32.dll", "GetPackageFullName");
220  if (!getPackageFullName)
221  return NULL;
222 
223  nameLength = 101;
224  name = PhCreateStringEx(NULL, (nameLength - 1) * 2);
225 
226  result = getPackageFullName(ProcessHandle, &nameLength, name->Buffer);
227 
228  if (result == ERROR_INSUFFICIENT_BUFFER)
229  {
230  PhDereferenceObject(name);
231  name = PhCreateStringEx(NULL, (nameLength - 1) * 2);
232 
233  result = getPackageFullName(ProcessHandle, &nameLength, name->Buffer);
234  }
235 
236  if (result == ERROR_SUCCESS)
237  {
239  return name;
240  }
241  else
242  {
243  PhDereferenceObject(name);
244  return NULL;
245  }
246 }
247 
249  _In_ PWSTR PackageFullName
250  )
251 {
252  static _PackageIdFromFullName packageIdFromFullName = NULL;
253 
254  LONG result;
255  PVOID packageIdBuffer;
256  ULONG packageIdBufferSize;
257 
258  if (!packageIdFromFullName)
259  packageIdFromFullName = PhGetModuleProcAddress(L"kernel32.dll", "PackageIdFromFullName");
260  if (!packageIdFromFullName)
261  return NULL;
262 
263  packageIdBufferSize = 100;
264  packageIdBuffer = PhAllocate(packageIdBufferSize);
265 
266  result = packageIdFromFullName(PackageFullName, PACKAGE_INFORMATION_BASIC, &packageIdBufferSize, (PBYTE)packageIdBuffer);
267 
268  if (result == ERROR_INSUFFICIENT_BUFFER)
269  {
270  PhFree(packageIdBuffer);
271  packageIdBuffer = PhAllocate(packageIdBufferSize);
272 
273  result = packageIdFromFullName(PackageFullName, PACKAGE_INFORMATION_BASIC, &packageIdBufferSize, (PBYTE)packageIdBuffer);
274  }
275 
276  if (result == ERROR_SUCCESS)
277  {
278  return packageIdBuffer;
279  }
280  else
281  {
282  PhFree(packageIdBuffer);
283  return NULL;
284  }
285 }
286 
288  _In_ PACKAGE_ID *PackageId
289  )
290 {
291  static _GetPackagePath getPackagePath = NULL;
292 
293  LONG result;
294  PPH_STRING path;
295  ULONG pathLength;
296 
297  if (!getPackagePath)
298  getPackagePath = PhGetModuleProcAddress(L"kernel32.dll", "GetPackagePath");
299  if (!getPackagePath)
300  return NULL;
301 
302  pathLength = 101;
303  path = PhCreateStringEx(NULL, (pathLength - 1) * 2);
304 
305  result = getPackagePath(PackageId, 0, &pathLength, path->Buffer);
306 
307  if (result == ERROR_INSUFFICIENT_BUFFER)
308  {
309  PhDereferenceObject(path);
310  path = PhCreateStringEx(NULL, (pathLength - 1) * 2);
311 
312  result = getPackagePath(PackageId, 0, &pathLength, path->Buffer);
313  }
314 
315  if (result == ERROR_SUCCESS)
316  {
318  return path;
319  }
320  else
321  {
322  PhDereferenceObject(path);
323  return NULL;
324  }
325 }
326 
335  _In_ HANDLE ProcessHandle,
336  _Out_ PH_KNOWN_PROCESS_TYPE *KnownProcessType
337  )
338 {
339  NTSTATUS status;
340  PH_KNOWN_PROCESS_TYPE knownProcessType;
341  PROCESS_BASIC_INFORMATION basicInfo;
342  PH_STRINGREF systemRootPrefix;
343  PPH_STRING fileName;
344  PPH_STRING newFileName;
345  PH_STRINGREF name;
346 #ifdef _WIN64
347  BOOLEAN isWow64 = FALSE;
348 #endif
349 
351  ProcessHandle,
352  &basicInfo
353  )))
354  return status;
355 
356  if (basicInfo.UniqueProcessId == SYSTEM_PROCESS_ID)
357  {
358  *KnownProcessType = SystemProcessType;
359  return STATUS_SUCCESS;
360  }
361 
362  PhGetSystemRoot(&systemRootPrefix);
363 
365  ProcessHandle,
366  &fileName
367  )))
368  {
369  return status;
370  }
371 
372  newFileName = PhGetFileName(fileName);
373  PhDereferenceObject(fileName);
374  name = newFileName->sr;
375 
376  knownProcessType = UnknownProcessType;
377 
378  if (PhStartsWithStringRef(&name, &systemRootPrefix, TRUE))
379  {
380  // Skip the system root, and we now have three cases:
381  // 1. \\xyz.exe - Windows executable.
382  // 2. \\System32\\xyz.exe - system32 executable.
383  // 3. \\SysWow64\\xyz.exe - system32 executable + WOW64.
384  PhSkipStringRef(&name, systemRootPrefix.Length);
385 
386  if (PhEqualStringRef2(&name, L"\\explorer.exe", TRUE))
387  {
388  knownProcessType = ExplorerProcessType;
389  }
390  else if (
391  PhStartsWithStringRef2(&name, L"\\System32", TRUE)
392 #ifdef _WIN64
393  || (PhStartsWithStringRef2(&name, L"\\SysWow64", TRUE) && (isWow64 = TRUE, TRUE)) // ugly but necessary
394 #endif
395  )
396  {
397  // SysTem32 and SysWow64 are both 8 characters long.
398  PhSkipStringRef(&name, 9 * sizeof(WCHAR));
399 
400  if (FALSE)
401  ; // Dummy
402  else if (PhEqualStringRef2(&name, L"\\smss.exe", TRUE))
403  knownProcessType = SessionManagerProcessType;
404  else if (PhEqualStringRef2(&name, L"\\csrss.exe", TRUE))
405  knownProcessType = WindowsSubsystemProcessType;
406  else if (PhEqualStringRef2(&name, L"\\wininit.exe", TRUE))
407  knownProcessType = WindowsStartupProcessType;
408  else if (PhEqualStringRef2(&name, L"\\services.exe", TRUE))
409  knownProcessType = ServiceControlManagerProcessType;
410  else if (PhEqualStringRef2(&name, L"\\lsass.exe", TRUE))
411  knownProcessType = LocalSecurityAuthorityProcessType;
412  else if (PhEqualStringRef2(&name, L"\\lsm.exe", TRUE))
413  knownProcessType = LocalSessionManagerProcessType;
414  else if (PhEqualStringRef2(&name, L"\\winlogon.exe", TRUE))
415  knownProcessType = WindowsLogonProcessType;
416  else if (PhEqualStringRef2(&name, L"\\svchost.exe", TRUE))
417  knownProcessType = ServiceHostProcessType;
418  else if (PhEqualStringRef2(&name, L"\\rundll32.exe", TRUE))
419  knownProcessType = RunDllAsAppProcessType;
420  else if (PhEqualStringRef2(&name, L"\\dllhost.exe", TRUE))
421  knownProcessType = ComSurrogateProcessType;
422  else if (PhEqualStringRef2(&name, L"\\taskeng.exe", TRUE))
423  knownProcessType = TaskHostProcessType;
424  else if (PhEqualStringRef2(&name, L"\\taskhost.exe", TRUE))
425  knownProcessType = TaskHostProcessType;
426  else if (PhEqualStringRef2(&name, L"\\taskhostex.exe", TRUE))
427  knownProcessType = TaskHostProcessType;
428  else if (PhEqualStringRef2(&name, L"\\wudfhost.exe", TRUE))
429  knownProcessType = UmdfHostProcessType;
430  }
431  }
432 
433  PhDereferenceObject(newFileName);
434 
435 #ifdef _WIN64
436  if (isWow64)
437  knownProcessType |= KnownProcessWow64;
438 #endif
439 
440  *KnownProcessType = knownProcessType;
441 
442  return status;
443 }
444 
445 static BOOLEAN NTAPI PhpSvchostCommandLineCallback(
446  _In_opt_ PPH_COMMAND_LINE_OPTION Option,
447  _In_opt_ PPH_STRING Value,
448  _In_opt_ PVOID Context
449  )
450 {
451  PPH_KNOWN_PROCESS_COMMAND_LINE knownCommandLine = Context;
452 
453  if (Option && Option->Id == 1)
454  {
455  PhSwapReference(&knownCommandLine->ServiceHost.GroupName, Value);
456  }
457 
458  return TRUE;
459 }
460 
462  _In_ PPH_STRING CommandLine,
463  _In_ PH_KNOWN_PROCESS_TYPE KnownProcessType,
464  _Out_ PPH_KNOWN_PROCESS_COMMAND_LINE KnownCommandLine
465  )
466 {
467  switch (KnownProcessType & KnownProcessTypeMask)
468  {
470  {
471  // svchost.exe -k <GroupName>
472 
473  static PH_COMMAND_LINE_OPTION options[] =
474  {
475  { 1, L"k", MandatoryArgumentType }
476  };
477 
478  KnownCommandLine->ServiceHost.GroupName = NULL;
479 
481  &CommandLine->sr,
482  options,
483  sizeof(options) / sizeof(PH_COMMAND_LINE_OPTION),
485  PhpSvchostCommandLineCallback,
486  KnownCommandLine
487  );
488 
489  if (KnownCommandLine->ServiceHost.GroupName)
490  {
491  PhAutoDereferenceObject(KnownCommandLine->ServiceHost.GroupName);
492  return TRUE;
493  }
494  else
495  {
496  return FALSE;
497  }
498  }
499  break;
501  {
502  // rundll32.exe <DllName>,<ProcedureName> ...
503 
504  SIZE_T i;
505  PH_STRINGREF dllNamePart;
506  PH_STRINGREF procedureNamePart;
507  PPH_STRING dllName;
508  PPH_STRING procedureName;
509 
510  i = 0;
511 
512  // Get the rundll32.exe part.
513 
514  dllName = PhParseCommandLinePart(&CommandLine->sr, &i);
515 
516  if (!dllName)
517  return FALSE;
518 
519  PhDereferenceObject(dllName);
520 
521  // Get the DLL name part.
522 
523  while (i < CommandLine->Length / 2 && CommandLine->Buffer[i] == ' ')
524  i++;
525 
526  dllName = PhParseCommandLinePart(&CommandLine->sr, &i);
527 
528  if (!dllName)
529  return FALSE;
530 
531  PhAutoDereferenceObject(dllName);
532 
533  // The procedure name begins after the last comma.
534 
535  if (!PhSplitStringRefAtLastChar(&dllName->sr, ',', &dllNamePart, &procedureNamePart))
536  return FALSE;
537 
538  dllName = PhAutoDereferenceObject(PhCreateString2(&dllNamePart));
539  procedureName = PhAutoDereferenceObject(PhCreateString2(&procedureNamePart));
540 
541  // If the DLL name isn't an absolute path, assume it's in system32.
542  // TODO: Use a proper search function.
543 
545  {
546  dllName = PhaConcatStrings(
547  3,
549  L"\\",
550  dllName->Buffer
551  );
552  }
553 
554  KnownCommandLine->RunDllAsApp.FileName = dllName;
555  KnownCommandLine->RunDllAsApp.ProcedureName = procedureName;
556  }
557  break;
559  {
560  // dllhost.exe /processid:<Guid>
561 
562  static PH_STRINGREF inprocServer32Name = PH_STRINGREF_INIT(L"InprocServer32");
563 
564  SIZE_T i;
565  ULONG_PTR indexOfProcessId;
566  PPH_STRING argPart;
567  PPH_STRING guidString;
568  UNICODE_STRING guidStringUs;
569  GUID guid;
570  HANDLE clsidKeyHandle;
571  HANDLE inprocServer32KeyHandle;
572  PPH_STRING fileName;
573 
574  i = 0;
575 
576  // Get the dllhost.exe part.
577 
578  argPart = PhParseCommandLinePart(&CommandLine->sr, &i);
579 
580  if (!argPart)
581  return FALSE;
582 
583  PhDereferenceObject(argPart);
584 
585  // Get the argument part.
586 
587  while (i < (ULONG)CommandLine->Length / 2 && CommandLine->Buffer[i] == ' ')
588  i++;
589 
590  argPart = PhParseCommandLinePart(&CommandLine->sr, &i);
591 
592  if (!argPart)
593  return FALSE;
594 
595  PhAutoDereferenceObject(argPart);
596 
597  // Find "/processid:"; the GUID is just after that.
598 
599  PhUpperString(argPart);
600  indexOfProcessId = PhFindStringInString(argPart, 0, L"/PROCESSID:");
601 
602  if (indexOfProcessId == -1)
603  return FALSE;
604 
605  guidString = PhaSubstring(
606  argPart,
607  indexOfProcessId + 11,
608  (ULONG)argPart->Length / 2 - indexOfProcessId - 11
609  );
610  PhStringRefToUnicodeString(&guidString->sr, &guidStringUs);
611 
613  &guidStringUs,
614  &guid
615  )))
616  return FALSE;
617 
618  KnownCommandLine->ComSurrogate.Guid = guid;
619  KnownCommandLine->ComSurrogate.Name = NULL;
620  KnownCommandLine->ComSurrogate.FileName = NULL;
621 
622  // Lookup the GUID in the registry to determine the name and file name.
623 
624  if (NT_SUCCESS(PhOpenKey(
625  &clsidKeyHandle,
626  KEY_READ,
628  &PhaConcatStrings2(L"CLSID\\", guidString->Buffer)->sr,
629  0
630  )))
631  {
632  KnownCommandLine->ComSurrogate.Name =
633  PhAutoDereferenceObject(PhQueryRegistryString(clsidKeyHandle, NULL));
634 
635  if (NT_SUCCESS(PhOpenKey(
636  &inprocServer32KeyHandle,
637  KEY_READ,
638  clsidKeyHandle,
639  &inprocServer32Name,
640  0
641  )))
642  {
643  KnownCommandLine->ComSurrogate.FileName =
644  PhAutoDereferenceObject(PhQueryRegistryString(inprocServer32KeyHandle, NULL));
645 
647  &KnownCommandLine->ComSurrogate.FileName->sr
648  )))
649  {
650  KnownCommandLine->ComSurrogate.FileName = fileName;
651  }
652 
653  NtClose(inprocServer32KeyHandle);
654  }
655 
656  NtClose(clsidKeyHandle);
657  }
658  }
659  break;
660  default:
661  return FALSE;
662  }
663 
664  return TRUE;
665 }
666 
668  _In_opt_ HWND hWnd,
669  _In_ ULONG Limit,
670  _In_ WNDENUMPROC Callback,
671  _In_ LPARAM lParam
672  )
673 {
674  HWND childWindow = NULL;
675  ULONG i = 0;
676 
677  while (i < Limit && (childWindow = FindWindowEx(hWnd, childWindow, NULL, NULL)))
678  {
679  if (!Callback(childWindow, lParam))
680  return;
681 
682  i++;
683  }
684 }
685 
686 typedef struct _GET_PROCESS_MAIN_WINDOW_CONTEXT
687 {
688  HWND Window;
689  HWND ImmersiveWindow;
690  HANDLE ProcessId;
691  BOOLEAN IsImmersive;
693 
695  _In_ HWND hwnd,
696  _In_ LPARAM lParam
697  )
698 {
699  PGET_PROCESS_MAIN_WINDOW_CONTEXT context = (PGET_PROCESS_MAIN_WINDOW_CONTEXT)lParam;
700  ULONG processId;
701  HWND parentWindow;
702  WINDOWINFO windowInfo;
703 
704  if (!IsWindowVisible(hwnd))
705  return TRUE;
706 
707  GetWindowThreadProcessId(hwnd, &processId);
708 
709  if (UlongToHandle(processId) == context->ProcessId &&
710  !((parentWindow = GetParent(hwnd)) && IsWindowVisible(parentWindow)) && // skip windows with a visible parent
711  PhGetWindowTextEx(hwnd, PH_GET_WINDOW_TEXT_INTERNAL | PH_GET_WINDOW_TEXT_LENGTH_ONLY, NULL) != 0) // skip windows with no title
712  {
713  if (!context->ImmersiveWindow && context->IsImmersive &&
714  GetProp(hwnd, L"Windows.ImmersiveShell.IdentifyAsMainCoreWindow"))
715  {
716  context->ImmersiveWindow = hwnd;
717  }
718 
719  windowInfo.cbSize = sizeof(WINDOWINFO);
720 
721  if (!context->Window && GetWindowInfo(hwnd, &windowInfo) && (windowInfo.dwStyle & WS_DLGFRAME))
722  {
723  context->Window = hwnd;
724 
725  // If we're not looking at an immersive process, there's no need to search any more windows.
726  if (!context->IsImmersive)
727  return FALSE;
728  }
729  }
730 
731  return TRUE;
732 }
733 
735  _In_ HANDLE ProcessId,
736  _In_opt_ HANDLE ProcessHandle
737  )
738 {
740  HANDLE processHandle = NULL;
741 
742  memset(&context, 0, sizeof(GET_PROCESS_MAIN_WINDOW_CONTEXT));
743  context.ProcessId = ProcessId;
744 
745  if (ProcessHandle)
746  processHandle = ProcessHandle;
747  else
748  PhOpenProcess(&processHandle, ProcessQueryAccess, ProcessId);
749 
750  if (processHandle && IsImmersiveProcess_I)
751  context.IsImmersive = IsImmersiveProcess_I(processHandle);
752 
753  PhEnumChildWindows(NULL, 0x800, PhpGetProcessMainWindowEnumWindowsProc, (LPARAM)&context);
754 
755  if (!ProcessHandle && processHandle)
756  NtClose(processHandle);
757 
758  return context.ImmersiveWindow ? context.ImmersiveWindow : context.Window;
759 }
760 
763  _In_ SC_HANDLE ServiceHandle
764  )
765 {
766  PPH_STRING fileName = NULL;
767  LPQUERY_SERVICE_CONFIG config;
768 
769  if (config = PhGetServiceConfig(ServiceHandle))
770  {
771  PhGetServiceDllParameter(ServiceName, &fileName);
772 
773  if (!fileName)
774  {
775  PPH_STRING commandLine;
776 
777  commandLine = PhCreateString(config->lpBinaryPathName);
778 
779  if (config->dwServiceType & SERVICE_WIN32)
780  {
781  PH_STRINGREF dummyFileName;
782  PH_STRINGREF dummyArguments;
783 
784  PhParseCommandLineFuzzy(&commandLine->sr, &dummyFileName, &dummyArguments, &fileName);
785 
786  if (!fileName)
787  PhSwapReference(&fileName, commandLine);
788  }
789  else
790  {
791  fileName = PhGetFileName(commandLine);
792  }
793 
794  PhDereferenceObject(commandLine);
795  }
796 
797  PhFree(config);
798  }
799 
800  return fileName;
801 }
802 
804  _In_ PPH_STRING String,
805  _In_ WCHAR Delimiter
806  )
807 {
808  PH_STRING_BUILDER stringBuilder;
809  SIZE_T length;
810  SIZE_T i;
811  WCHAR temp[2];
812 
813  length = String->Length / 2;
814  PhInitializeStringBuilder(&stringBuilder, String->Length / 2 * 3);
815 
816  temp[0] = '\\';
817 
818  for (i = 0; i < length; i++)
819  {
820  if (String->Buffer[i] == '\\' || String->Buffer[i] == Delimiter)
821  {
822  temp[1] = String->Buffer[i];
823  PhAppendStringBuilderEx(&stringBuilder, temp, 4);
824  }
825  else
826  {
827  PhAppendCharStringBuilder(&stringBuilder, String->Buffer[i]);
828  }
829  }
830 
831  return PhFinalStringBuilderString(&stringBuilder);
832 }
833 
835  _In_ PPH_STRING String,
836  _In_ WCHAR Delimiter
837  )
838 {
839  PH_STRING_BUILDER stringBuilder;
840  SIZE_T length;
841  SIZE_T i;
842 
843  length = String->Length / 2;
844  PhInitializeStringBuilder(&stringBuilder, String->Length / 2 * 3);
845 
846  for (i = 0; i < length; i++)
847  {
848  if (String->Buffer[i] == '\\')
849  {
850  if (i != length - 1)
851  {
852  PhAppendCharStringBuilder(&stringBuilder, String->Buffer[i + 1]);
853  i++;
854  }
855  else
856  {
857  // Trailing backslash. Just ignore it.
858  break;
859  }
860  }
861  else
862  {
863  PhAppendCharStringBuilder(&stringBuilder, String->Buffer[i]);
864  }
865  }
866 
867  return PhFinalStringBuilderString(&stringBuilder);
868 }
869 
871  _In_ mxml_node_t *node
872  )
873 {
874  if (node->child && node->child->type == MXML_OPAQUE && node->child->value.opaque)
875  {
876  return PhConvertUtf8ToUtf16(node->child->value.opaque);
877  }
878  else
879  {
880  return PhReferenceEmptyString();
881  }
882 }
883 
885  _In_ HWND hWnd,
886  _In_ PWSTR String
887  )
888 {
889  PhShellExecuteUserString(hWnd, L"SearchEngine", String, TRUE, NULL);
890 }
891 
893  _In_ HWND hWnd,
894  _In_ PWSTR Setting,
895  _In_ PWSTR String,
896  _In_ BOOLEAN UseShellExecute,
897  _In_opt_ PWSTR ErrorMessage
898  )
899 {
900  static PH_STRINGREF replacementToken = PH_STRINGREF_INIT(L"%s");
901 
902  PPH_STRING executeString;
903  PH_STRINGREF stringBefore;
904  PH_STRINGREF stringMiddle;
905  PH_STRINGREF stringAfter;
906  PPH_STRING newString;
907  PPH_STRING ntMessage;
908 
909  executeString = PhGetStringSetting(Setting);
910 
911  // Make sure the user executable string is absolute.
912  // We can't use RtlDetermineDosPathNameType_U here because the string
913  // may be a URL.
914  if (PhFindCharInString(executeString, 0, ':') == -1)
915  {
916  newString = PhConcatStringRef2(&PhApplicationDirectory->sr, &executeString->sr);
917  PhDereferenceObject(executeString);
918  executeString = newString;
919  }
920 
921  // Replace "%s" with the string, or use the original string if "%s" is not present.
922  if (PhSplitStringRefAtString(&executeString->sr, &replacementToken, FALSE, &stringBefore, &stringAfter))
923  {
924  PhInitializeStringRef(&stringMiddle, String);
925  newString = PhConcatStringRef3(&stringBefore, &stringMiddle, &stringAfter);
926  }
927  else
928  {
929  PhSetReference(&newString, executeString);
930  }
931 
932  PhDereferenceObject(executeString);
933 
934  if (UseShellExecute)
935  {
936  PhShellExecute(hWnd, newString->Buffer, NULL);
937  }
938  else
939  {
940  NTSTATUS status;
941 
942  status = PhCreateProcessWin32(NULL, newString->Buffer, NULL, NULL, 0, NULL, NULL, NULL);
943 
944  if (!NT_SUCCESS(status))
945  {
946  if (ErrorMessage)
947  {
948  ntMessage = PhGetNtMessage(status);
949  PhShowError(hWnd, L"Unable to execute the command: %s\n%s", PhGetStringOrDefault(ntMessage, L"An unknown error occurred."), ErrorMessage);
950  PhDereferenceObject(ntMessage);
951  }
952  else
953  {
954  PhShowStatus(hWnd, L"Unable to execute the command", status, 0);
955  }
956  }
957  }
958 
959  PhDereferenceObject(newString);
960 }
961 
963  _Inout_ PPH_SYMBOL_PROVIDER SymbolProvider
964  )
965 {
966  PPH_STRING searchPath;
967 
969  SYMOPT_UNDNAME,
970  PhGetIntegerSetting(L"DbgHelpUndecorate") ? SYMOPT_UNDNAME : 0
971  );
972 
973  searchPath = PhGetStringSetting(L"DbgHelpSearchPath");
974 
975  if (searchPath->Length != 0)
976  PhSetSearchPathSymbolProvider(SymbolProvider, searchPath->Buffer);
977 
978  PhDereferenceObject(searchPath);
979 }
980 
982  VOID
983  )
984 {
985  PH_DEFINE_MAKE_ATOM(L"PH2_Context");
986 }
987 
997  _Inout_ LPNMLVGETINFOTIP GetInfoTip,
998  _In_ PPH_STRINGREF Tip
999  )
1000 {
1001  ULONG copyIndex;
1002  ULONG bufferRemaining;
1003  ULONG copyLength;
1004 
1005  if (GetInfoTip->dwFlags == 0)
1006  {
1007  copyIndex = (ULONG)PhCountStringZ(GetInfoTip->pszText) + 1; // plus one for newline
1008 
1009  if (GetInfoTip->cchTextMax - copyIndex < 2) // need at least two bytes
1010  return;
1011 
1012  bufferRemaining = GetInfoTip->cchTextMax - copyIndex - 1;
1013  GetInfoTip->pszText[copyIndex - 1] = '\n';
1014  }
1015  else
1016  {
1017  copyIndex = 0;
1018  bufferRemaining = GetInfoTip->cchTextMax;
1019  }
1020 
1021  copyLength = min((ULONG)Tip->Length / 2, bufferRemaining - 1);
1022  memcpy(
1023  &GetInfoTip->pszText[copyIndex],
1024  Tip->Buffer,
1025  copyLength * 2
1026  );
1027  GetInfoTip->pszText[copyIndex + copyLength] = 0;
1028 }
1029 
1031  _In_ HWND ListViewHandle
1032  )
1033 {
1034  PPH_STRING text;
1035 
1036  text = PhGetListViewText(ListViewHandle);
1037  PhSetClipboardString(ListViewHandle, &text->sr);
1038  PhDereferenceObject(text);
1039 }
1040 
1042  _In_ LPARAM lParam,
1043  _In_ HWND ListViewHandle
1044  )
1045 {
1047 }
1048 
1050  _In_ LPARAM lParam,
1051  _In_ HWND ListViewHandle,
1052  _In_ ULONG Behaviors
1053  )
1054 {
1055  if (((LPNMHDR)lParam)->hwndFrom == ListViewHandle && ((LPNMHDR)lParam)->code == LVN_KEYDOWN)
1056  {
1057  LPNMLVKEYDOWN keyDown = (LPNMLVKEYDOWN)lParam;
1058 
1059  switch (keyDown->wVKey)
1060  {
1061  case 'C':
1062  if (Behaviors & PH_LIST_VIEW_CTRL_C_BEHAVIOR)
1063  {
1064  if (GetKeyState(VK_CONTROL) < 0)
1065  PhCopyListView(ListViewHandle);
1066  }
1067  break;
1068  case 'A':
1069  if (Behaviors & PH_LIST_VIEW_CTRL_A_BEHAVIOR)
1070  {
1071  if (GetKeyState(VK_CONTROL) < 0)
1072  PhSetStateAllListViewItems(ListViewHandle, LVIS_SELECTED, LVIS_SELECTED);
1073  }
1074  break;
1075  }
1076  }
1077 }
1078 
1080  _In_ HWND ListViewHandle,
1081  _Out_ PPOINT Point
1082  )
1083 {
1084  INT selectedIndex;
1085  RECT bounds;
1086  RECT clientRect;
1087 
1088  // The user pressed a key to display the context menu.
1089  // Suggest where the context menu should display.
1090 
1091  if ((selectedIndex = ListView_GetNextItem(ListViewHandle, -1, LVNI_SELECTED)) != -1)
1092  {
1093  if (ListView_GetItemRect(ListViewHandle, selectedIndex, &bounds, LVIR_BOUNDS))
1094  {
1095  Point->x = bounds.left + PhSmallIconSize.X / 2;
1096  Point->y = bounds.top + PhSmallIconSize.Y / 2;
1097 
1098  GetClientRect(ListViewHandle, &clientRect);
1099 
1100  if (Point->x < 0 || Point->y < 0 || Point->x >= clientRect.right || Point->y >= clientRect.bottom)
1101  {
1102  // The menu is going to be outside of the control. Just put it at the top-left.
1103  Point->x = 0;
1104  Point->y = 0;
1105  }
1106 
1107  ClientToScreen(ListViewHandle, Point);
1108 
1109  return TRUE;
1110  }
1111  }
1112 
1113  Point->x = 0;
1114  Point->y = 0;
1115  ClientToScreen(ListViewHandle, Point);
1116 
1117  return FALSE;
1118 }
1119 
1121  _In_ HFONT Font,
1122  _In_ LONG NewWeight
1123  )
1124 {
1125  LOGFONT logFont;
1126 
1127  if (GetObject(Font, sizeof(LOGFONT), &logFont))
1128  {
1129  logFont.lfWeight = NewWeight;
1130  return CreateFontIndirect(&logFont);
1131  }
1132  else
1133  {
1134  return NULL;
1135  }
1136 }
1137 
1139  _In_ HWND WindowHandle,
1140  _In_ ULONG OpacityPercent
1141  )
1142 {
1143  if (OpacityPercent == 0)
1144  {
1145  // Make things a bit faster by removing the WS_EX_LAYERED bit.
1146  PhSetWindowExStyle(WindowHandle, WS_EX_LAYERED, 0);
1147  RedrawWindow(WindowHandle, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN);
1148  return;
1149  }
1150 
1151  PhSetWindowExStyle(WindowHandle, WS_EX_LAYERED, WS_EX_LAYERED);
1152 
1153  // Disallow opacity values of less than 10%.
1154  OpacityPercent = min(OpacityPercent, 90);
1155 
1156  // The opacity value is backwards - 0 means opaque, 100 means transparent.
1157  SetLayeredWindowAttributes(
1158  WindowHandle,
1159  0,
1160  (BYTE)(255 * (100 - OpacityPercent) / 100),
1161  LWA_ALPHA
1162  );
1163 }
1164 
1166  _In_opt_ PWSTR PositionSettingName,
1167  _In_opt_ PWSTR SizeSettingName,
1168  _In_ HWND WindowHandle
1169  )
1170 {
1171  PH_RECTANGLE windowRectangle;
1172 
1173  if (PositionSettingName && SizeSettingName)
1174  {
1175  RECT rectForAdjust;
1176 
1177  windowRectangle.Position = PhGetIntegerPairSetting(PositionSettingName);
1178  windowRectangle.Size = PhGetIntegerPairSetting(SizeSettingName);
1179 
1181  WindowHandle,
1182  &windowRectangle
1183  );
1184 
1185  // Let the window adjust for the minimum size if needed.
1186  rectForAdjust = PhRectangleToRect(windowRectangle);
1187  SendMessage(WindowHandle, WM_SIZING, WMSZ_BOTTOMRIGHT, (LPARAM)&rectForAdjust);
1188  windowRectangle = PhRectToRectangle(rectForAdjust);
1189 
1190  MoveWindow(WindowHandle, windowRectangle.Left, windowRectangle.Top,
1191  windowRectangle.Width, windowRectangle.Height, FALSE);
1192  }
1193  else
1194  {
1195  PH_INTEGER_PAIR position;
1196  PH_INTEGER_PAIR size;
1197  ULONG flags;
1198 
1199  flags = SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOREDRAW | SWP_NOSIZE | SWP_NOZORDER;
1200 
1201  if (PositionSettingName)
1202  {
1203  position = PhGetIntegerPairSetting(PositionSettingName);
1204  flags &= ~SWP_NOMOVE;
1205  }
1206  else
1207  {
1208  position.X = 0;
1209  position.Y = 0;
1210  }
1211 
1212  if (SizeSettingName)
1213  {
1214  size = PhGetIntegerPairSetting(SizeSettingName);
1215  flags &= ~SWP_NOSIZE;
1216  }
1217  else
1218  {
1219  size.X = 16;
1220  size.Y = 16;
1221  }
1222 
1223  SetWindowPos(WindowHandle, NULL, position.X, position.Y, size.X, size.Y, flags);
1224  }
1225 }
1226 
1228  _In_opt_ PWSTR PositionSettingName,
1229  _In_opt_ PWSTR SizeSettingName,
1230  _In_ HWND WindowHandle
1231  )
1232 {
1233  WINDOWPLACEMENT placement = { sizeof(placement) };
1234  PH_RECTANGLE windowRectangle;
1235  MONITORINFO monitorInfo = { sizeof(MONITORINFO) };
1236 
1237  GetWindowPlacement(WindowHandle, &placement);
1238  windowRectangle = PhRectToRectangle(placement.rcNormalPosition);
1239 
1240  // The rectangle is in workspace coordinates. Convert the values back to screen coordinates.
1241  if (GetMonitorInfo(MonitorFromRect(&placement.rcNormalPosition, MONITOR_DEFAULTTOPRIMARY), &monitorInfo))
1242  {
1243  windowRectangle.Left += monitorInfo.rcWork.left - monitorInfo.rcMonitor.left;
1244  windowRectangle.Top += monitorInfo.rcWork.top - monitorInfo.rcMonitor.top;
1245  }
1246 
1247  if (PositionSettingName)
1248  PhSetIntegerPairSetting(PositionSettingName, windowRectangle.Position);
1249  if (SizeSettingName)
1250  PhSetIntegerPairSetting(SizeSettingName, windowRectangle.Size);
1251 }
1252 
1254  _In_ PWSTR Name,
1255  _In_ HWND ListViewHandle
1256  )
1257 {
1258  PPH_STRING string;
1259 
1260  string = PhGetStringSetting(Name);
1261  PhLoadListViewColumnSettings(ListViewHandle, string);
1262  PhDereferenceObject(string);
1263 }
1264 
1266  _In_ PWSTR Name,
1267  _In_ HWND ListViewHandle
1268  )
1269 {
1270  PPH_STRING string;
1271 
1272  string = PhSaveListViewColumnSettings(ListViewHandle);
1273  PhSetStringSetting2(Name, &string->sr);
1274  PhDereferenceObject(string);
1275 }
1276 
1278  VOID
1279  )
1280 {
1281  PH_FORMAT format[3];
1282 
1283  PhInitFormatU(&format[0], PHAPP_VERSION_MAJOR);
1284  PhInitFormatC(&format[1], '.');
1285  PhInitFormatU(&format[2], PHAPP_VERSION_MINOR);
1286 
1287  return PhFormat(format, 3, 16);
1288 }
1289 
1291  _Out_opt_ PULONG MajorVersion,
1292  _Out_opt_ PULONG MinorVersion,
1293  _Reserved_ PULONG Reserved,
1294  _Out_opt_ PULONG RevisionNumber
1295  )
1296 {
1297  if (MajorVersion)
1298  *MajorVersion = PHAPP_VERSION_MAJOR;
1299  if (MinorVersion)
1300  *MinorVersion = PHAPP_VERSION_MINOR;
1301  if (RevisionNumber)
1302  *RevisionNumber = PHAPP_VERSION_REVISION;
1303 }
1304 
1306  _Inout_ PPH_FILE_STREAM FileStream
1307  )
1308 {
1309  PPH_STRING version;
1310  LARGE_INTEGER time;
1311  SYSTEMTIME systemTime;
1312  PPH_STRING dateString;
1313  PPH_STRING timeString;
1314 
1315  PhWriteStringAsUtf8FileStream2(FileStream, L"Process Hacker ");
1316 
1317  if (version = PhGetPhVersion())
1318  {
1319  PhWriteStringAsUtf8FileStream(FileStream, &version->sr);
1320  PhDereferenceObject(version);
1321  }
1322 
1323  PhWriteStringFormatAsUtf8FileStream(FileStream, L"\r\nWindows NT %u.%u", PhOsVersion.dwMajorVersion, PhOsVersion.dwMinorVersion);
1324 
1325  if (PhOsVersion.szCSDVersion[0] != 0)
1326  PhWriteStringFormatAsUtf8FileStream(FileStream, L" %s", PhOsVersion.szCSDVersion);
1327 
1328 #ifdef _WIN64
1329  PhWriteStringAsUtf8FileStream2(FileStream, L" (64-bit)");
1330 #else
1331  PhWriteStringAsUtf8FileStream2(FileStream, L" (32-bit)");
1332 #endif
1333 
1334  PhQuerySystemTime(&time);
1335  PhLargeIntegerToLocalSystemTime(&systemTime, &time);
1336 
1337  dateString = PhFormatDate(&systemTime, NULL);
1338  timeString = PhFormatTime(&systemTime, NULL);
1339  PhWriteStringFormatAsUtf8FileStream(FileStream, L"\r\n%s %s\r\n\r\n", dateString->Buffer, timeString->Buffer);
1340  PhDereferenceObject(dateString);
1341  PhDereferenceObject(timeString);
1342 }
1343 
1345  _In_opt_ HWND hWnd,
1346  _In_opt_ PWSTR Parameters,
1347  _In_ ULONG ShowWindowType,
1348  _In_ ULONG Flags,
1349  _In_ ULONG AppFlags,
1350  _In_opt_ ULONG Timeout,
1351  _Out_opt_ PHANDLE ProcessHandle
1352  )
1353 {
1354  return PhShellProcessHackerEx(
1355  hWnd,
1356  NULL,
1357  Parameters,
1358  ShowWindowType,
1359  Flags,
1360  AppFlags,
1361  Timeout,
1362  ProcessHandle
1363  );
1364 }
1365 
1367  _In_opt_ HWND hWnd,
1368  _In_opt_ PWSTR FileName,
1369  _In_opt_ PWSTR Parameters,
1370  _In_ ULONG ShowWindowType,
1371  _In_ ULONG Flags,
1372  _In_ ULONG AppFlags,
1373  _In_opt_ ULONG Timeout,
1374  _Out_opt_ PHANDLE ProcessHandle
1375  )
1376 {
1377  BOOLEAN result;
1378  PH_STRING_BUILDER sb;
1379  PWSTR parameters;
1380  PPH_STRING temp;
1381 
1382  if (AppFlags & PH_SHELL_APP_PROPAGATE_PARAMETERS)
1383  {
1384  PhInitializeStringBuilder(&sb, 128);
1385 
1386  // Propagate parameters.
1387 
1389  {
1390  PhAppendStringBuilder2(&sb, L" -nosettings");
1391  }
1393  {
1394  PhAppendStringBuilder2(&sb, L" -settings \"");
1395 
1396  if (PhSettingsFileName)
1398  else
1400 
1401  PhAppendStringBuilder(&sb, &temp->sr);
1402  PhDereferenceObject(temp);
1403  PhAppendCharStringBuilder(&sb, '\"');
1404  }
1405 
1407  {
1408  PhAppendStringBuilder2(&sb, L" -nokph");
1409  }
1410 
1412  {
1413  PhAppendStringBuilder2(&sb, L" -noplugins");
1414  }
1415 
1417  {
1418  PhAppendStringBuilder2(&sb, L" -newinstance");
1419  }
1420 
1422  {
1423  PhAppendStringBuilder2(&sb, L" -selecttab \"");
1425  PhAppendStringBuilder(&sb, &temp->sr);
1426  PhDereferenceObject(temp);
1427  PhAppendCharStringBuilder(&sb, '\"');
1428  }
1429 
1431  {
1433  {
1434  PhAppendStringBuilder2(&sb, L" -v");
1435  }
1436 
1438  {
1439  PhAppendStringBuilder2(&sb, L" -hide");
1440  }
1441  }
1442 
1443  // Add user-specified parameters last so they can override the propagated parameters.
1444  if (Parameters)
1445  {
1446  PhAppendCharStringBuilder(&sb, ' ');
1447  PhAppendStringBuilder2(&sb, Parameters);
1448  }
1449 
1450  if (sb.String->Length != 0 && sb.String->Buffer[0] == ' ')
1451  parameters = sb.String->Buffer + 1;
1452  else
1453  parameters = sb.String->Buffer;
1454  }
1455  else
1456  {
1457  parameters = Parameters;
1458  }
1459 
1460  result = PhShellExecuteEx(
1461  hWnd,
1462  FileName ? FileName : PhApplicationFileName->Buffer,
1463  parameters,
1464  ShowWindowType,
1465  Flags,
1466  Timeout,
1467  ProcessHandle
1468  );
1469 
1470  if (AppFlags & PH_SHELL_APP_PROPAGATE_PARAMETERS)
1471  PhDeleteStringBuilder(&sb);
1472 
1473  return result;
1474 }
1475 
1477  _In_ PWSTR FileName
1478  )
1479 {
1480  BOOLEAN result;
1481  BOOL (NTAPI *debugSetProcessKillOnExit)(BOOL);
1482  BOOL (NTAPI *debugActiveProcessStop)(DWORD);
1483  BOOLEAN originalValue;
1484  STARTUPINFO startupInfo;
1485  PROCESS_INFORMATION processInfo;
1486 
1487  if (!(debugSetProcessKillOnExit = PhGetModuleProcAddress(L"kernel32.dll", "DebugSetProcessKillOnExit")) ||
1488  !(debugActiveProcessStop = PhGetModuleProcAddress(L"kernel32.dll", "DebugActiveProcessStop")))
1489  return FALSE;
1490 
1491  result = FALSE;
1492 
1493  // This is NOT thread-safe.
1494  originalValue = NtCurrentPeb()->ReadImageFileExecOptions;
1495  NtCurrentPeb()->ReadImageFileExecOptions = FALSE;
1496 
1497  memset(&startupInfo, 0, sizeof(STARTUPINFO));
1498  startupInfo.cb = sizeof(STARTUPINFO);
1499  memset(&processInfo, 0, sizeof(PROCESS_INFORMATION));
1500 
1501  // The combination of ReadImageFileExecOptions = FALSE and the DEBUG_PROCESS flag
1502  // allows us to skip the Debugger IFEO value.
1503  if (CreateProcess(FileName, NULL, NULL, NULL, FALSE, DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS, NULL, NULL, &startupInfo, &processInfo))
1504  {
1505  // Stop debugging taskmgr.exe now.
1506  debugSetProcessKillOnExit(FALSE);
1507  debugActiveProcessStop(processInfo.dwProcessId);
1508  result = TRUE;
1509  }
1510 
1511  if (processInfo.hProcess)
1512  NtClose(processInfo.hProcess);
1513  if (processInfo.hThread)
1514  NtClose(processInfo.hThread);
1515 
1516  NtCurrentPeb()->ReadImageFileExecOptions = originalValue;
1517 
1518  return result;
1519 }
1520 
1522  _Inout_ PPH_TN_COLUMN_MENU_DATA Data
1523  )
1524 {
1526 }
1527 
1529  _Inout_ PPH_TN_COLUMN_MENU_DATA Data,
1530  _In_ ULONG Flags
1531  )
1532 {
1533  PPH_EMENU_ITEM resetSortMenuItem = NULL;
1534  PPH_EMENU_ITEM sizeColumnToFitMenuItem;
1535  PPH_EMENU_ITEM sizeAllColumnsToFitMenuItem;
1536  PPH_EMENU_ITEM hideColumnMenuItem;
1537  PPH_EMENU_ITEM chooseColumnsMenuItem;
1538  ULONG minimumNumberOfColumns;
1539 
1540  Data->Menu = PhCreateEMenu();
1541  Data->Selection = NULL;
1542  Data->ProcessedId = 0;
1543 
1544  sizeColumnToFitMenuItem = PhCreateEMenuItem(0, PH_TN_COLUMN_MENU_SIZE_COLUMN_TO_FIT_ID, L"Size Column to Fit", NULL, NULL);
1545  sizeAllColumnsToFitMenuItem = PhCreateEMenuItem(0, PH_TN_COLUMN_MENU_SIZE_ALL_COLUMNS_TO_FIT_ID, L"Size All Columns to Fit", NULL, NULL);
1546 
1547  if (!(Flags & PH_TN_COLUMN_MENU_NO_VISIBILITY))
1548  {
1549  hideColumnMenuItem = PhCreateEMenuItem(0, PH_TN_COLUMN_MENU_HIDE_COLUMN_ID, L"Hide Column", NULL, NULL);
1550  chooseColumnsMenuItem = PhCreateEMenuItem(0, PH_TN_COLUMN_MENU_CHOOSE_COLUMNS_ID, L"Choose Columns...", NULL, NULL);
1551  }
1552 
1554  {
1555  ULONG sortColumn;
1556  PH_SORT_ORDER sortOrder;
1557 
1558  TreeNew_GetSort(Data->TreeNewHandle, &sortColumn, &sortOrder);
1559 
1560  if (sortColumn != Data->DefaultSortColumn || sortOrder != Data->DefaultSortOrder)
1561  resetSortMenuItem = PhCreateEMenuItem(0, PH_TN_COLUMN_MENU_RESET_SORT_ID, L"Reset Sort", NULL, NULL);
1562  }
1563 
1564  PhInsertEMenuItem(Data->Menu, sizeColumnToFitMenuItem, -1);
1565  PhInsertEMenuItem(Data->Menu, sizeAllColumnsToFitMenuItem, -1);
1566 
1567  if (!(Flags & PH_TN_COLUMN_MENU_NO_VISIBILITY))
1568  {
1569  PhInsertEMenuItem(Data->Menu, hideColumnMenuItem, -1);
1570 
1571  if (resetSortMenuItem)
1572  PhInsertEMenuItem(Data->Menu, resetSortMenuItem, -1);
1573 
1574  PhInsertEMenuItem(Data->Menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, L"", NULL, NULL), -1);
1575  PhInsertEMenuItem(Data->Menu, chooseColumnsMenuItem, -1);
1576 
1577  if (TreeNew_GetFixedColumn(Data->TreeNewHandle))
1578  minimumNumberOfColumns = 2; // don't allow user to remove all normal columns (the fixed column can never be removed)
1579  else
1580  minimumNumberOfColumns = 1;
1581 
1582  if (!Data->MouseEvent || !Data->MouseEvent->Column ||
1583  Data->MouseEvent->Column->Fixed || // don't allow the fixed column to be hidden
1584  TreeNew_GetVisibleColumnCount(Data->TreeNewHandle) < minimumNumberOfColumns + 1
1585  )
1586  {
1587  hideColumnMenuItem->Flags |= PH_EMENU_DISABLED;
1588  }
1589  }
1590  else
1591  {
1592  if (resetSortMenuItem)
1593  PhInsertEMenuItem(Data->Menu, resetSortMenuItem, -1);
1594  }
1595 
1596  if (!Data->MouseEvent || !Data->MouseEvent->Column)
1597  {
1598  sizeColumnToFitMenuItem->Flags |= PH_EMENU_DISABLED;
1599  }
1600 }
1601 
1603  _Inout_ HWND TreeNewHandle,
1604  _In_ ULONG DefaultSortColumn,
1605  _In_ PH_SORT_ORDER DefaultSortOrder
1606  )
1607 {
1608  ULONG sortColumn;
1609  PH_SORT_ORDER sortOrder;
1610 
1611  // Make sure the column we're sorting by is actually visible, and if not, don't sort anymore.
1612 
1613  TreeNew_GetSort(TreeNewHandle, &sortColumn, &sortOrder);
1614 
1615  if (sortOrder != NoSortOrder)
1616  {
1617  PH_TREENEW_COLUMN column;
1618 
1619  TreeNew_GetColumn(TreeNewHandle, sortColumn, &column);
1620 
1621  if (!column.Visible)
1622  {
1623  if (DefaultSortOrder != NoSortOrder)
1624  {
1625  // Make sure the default sort column is visible.
1626  TreeNew_GetColumn(TreeNewHandle, DefaultSortColumn, &column);
1627 
1628  if (!column.Visible)
1629  {
1630  ULONG maxId;
1631  ULONG id;
1632  BOOLEAN found;
1633 
1634  // Use the first visible column.
1635  maxId = TreeNew_GetMaxId(TreeNewHandle);
1636  id = 0;
1637  found = FALSE;
1638 
1639  while (id <= maxId)
1640  {
1641  if (TreeNew_GetColumn(TreeNewHandle, id, &column))
1642  {
1643  if (column.Visible)
1644  {
1645  DefaultSortColumn = id;
1646  found = TRUE;
1647  break;
1648  }
1649  }
1650 
1651  id++;
1652  }
1653 
1654  if (!found)
1655  {
1656  DefaultSortColumn = 0;
1657  DefaultSortOrder = NoSortOrder;
1658  }
1659  }
1660  }
1661 
1662  TreeNew_SetSort(TreeNewHandle, DefaultSortColumn, DefaultSortOrder);
1663  }
1664  }
1665 }
1666 
1668  _Inout_ PPH_TN_COLUMN_MENU_DATA Data
1669  )
1670 {
1671  if (!Data->Selection)
1672  return FALSE;
1673 
1674  switch (Data->Selection->Id)
1675  {
1677  {
1678  TreeNew_SetSort(Data->TreeNewHandle, Data->DefaultSortColumn, Data->DefaultSortOrder);
1679  }
1680  break;
1682  {
1683  if (Data->MouseEvent && Data->MouseEvent->Column)
1684  {
1685  TreeNew_AutoSizeColumn(Data->TreeNewHandle, Data->MouseEvent->Column->Id, 0);
1686  }
1687  }
1688  break;
1690  {
1691  ULONG maxId;
1692  ULONG id;
1693 
1694  maxId = TreeNew_GetMaxId(Data->TreeNewHandle);
1695  id = 0;
1696 
1697  while (id <= maxId)
1698  {
1699  TreeNew_AutoSizeColumn(Data->TreeNewHandle, id, 0);
1700  id++;
1701  }
1702  }
1703  break;
1705  {
1706  PH_TREENEW_COLUMN column;
1707 
1708  if (Data->MouseEvent && Data->MouseEvent->Column && !Data->MouseEvent->Column->Fixed)
1709  {
1710  column.Id = Data->MouseEvent->Column->Id;
1711  column.Visible = FALSE;
1712  TreeNew_SetColumn(Data->TreeNewHandle, TN_COLUMN_FLAG_VISIBLE, &column);
1713  PhpEnsureValidSortColumnTreeNew(Data->TreeNewHandle, Data->DefaultSortColumn, Data->DefaultSortOrder);
1714  InvalidateRect(Data->TreeNewHandle, NULL, FALSE);
1715  }
1716  }
1717  break;
1719  {
1720  PhShowChooseColumnsDialog(Data->TreeNewHandle, Data->TreeNewHandle, PH_CONTROL_TYPE_TREE_NEW);
1721  PhpEnsureValidSortColumnTreeNew(Data->TreeNewHandle, Data->DefaultSortColumn, Data->DefaultSortOrder);
1722  }
1723  break;
1724  default:
1725  return FALSE;
1726  }
1727 
1728  Data->ProcessedId = Data->Selection->Id;
1729 
1730  return TRUE;
1731 }
1732 
1734  _In_ PPH_TN_COLUMN_MENU_DATA Data
1735  )
1736 {
1737  if (Data->Menu)
1738  {
1739  PhDestroyEMenu(Data->Menu);
1740  Data->Menu = NULL;
1741  }
1742 }
1743 
1745  _Out_ PPH_TN_FILTER_SUPPORT Support,
1746  _In_ HWND TreeNewHandle,
1747  _In_ PPH_LIST NodeList
1748  )
1749 {
1750  Support->FilterList = NULL;
1751  Support->TreeNewHandle = TreeNewHandle;
1752  Support->NodeList = NodeList;
1753 }
1754 
1756  _In_ PPH_TN_FILTER_SUPPORT Support
1757  )
1758 {
1759  PhDereferenceObject(Support->FilterList);
1760 }
1761 
1763  _In_ PPH_TN_FILTER_SUPPORT Support,
1764  _In_ PPH_TN_FILTER_FUNCTION Filter,
1765  _In_opt_ PVOID Context
1766  )
1767 {
1768  PPH_TN_FILTER_ENTRY entry;
1769 
1770  entry = PhAllocate(sizeof(PH_TN_FILTER_ENTRY));
1771  entry->Filter = Filter;
1772  entry->Context = Context;
1773 
1774  if (!Support->FilterList)
1775  Support->FilterList = PhCreateList(2);
1776 
1777  PhAddItemList(Support->FilterList, entry);
1778 
1779  return entry;
1780 }
1781 
1783  _In_ PPH_TN_FILTER_SUPPORT Support,
1784  _In_ PPH_TN_FILTER_ENTRY Entry
1785  )
1786 {
1787  ULONG index;
1788 
1789  if (!Support->FilterList)
1790  return;
1791 
1792  index = PhFindItemList(Support->FilterList, Entry);
1793 
1794  if (index != -1)
1795  {
1796  PhRemoveItemList(Support->FilterList, index);
1797  PhFree(Entry);
1798  }
1799 }
1800 
1802  _In_ PPH_TN_FILTER_SUPPORT Support,
1803  _In_ PPH_TREENEW_NODE Node
1804  )
1805 {
1806  BOOLEAN show;
1807  ULONG i;
1808 
1809  show = TRUE;
1810 
1811  if (Support->FilterList)
1812  {
1813  for (i = 0; i < Support->FilterList->Count; i++)
1814  {
1815  PPH_TN_FILTER_ENTRY entry;
1816 
1817  entry = Support->FilterList->Items[i];
1818 
1819  if (!entry->Filter(Node, entry->Context))
1820  {
1821  show = FALSE;
1822  break;
1823  }
1824  }
1825  }
1826 
1827  return show;
1828 }
1829 
1831  _In_ PPH_TN_FILTER_SUPPORT Support
1832  )
1833 {
1834  ULONG i;
1835 
1836  for (i = 0; i < Support->NodeList->Count; i++)
1837  {
1838  PPH_TREENEW_NODE node;
1839 
1840  node = Support->NodeList->Items[i];
1841  node->Visible = PhApplyTreeNewFiltersToNode(Support, node);
1842 
1843  if (!node->Visible && node->Selected)
1844  {
1845  node->Selected = FALSE;
1846  }
1847  }
1848 
1849  TreeNew_NodesStructured(Support->TreeNewHandle);
1850 }
1851 
1853  _In_ struct _PH_EMENU_ITEM *Item
1854  )
1855 {
1856  PPH_COPY_CELL_CONTEXT context;
1857 
1858  context = Item->Context;
1860  PhFree(context);
1861 }
1862 
1864  _In_ struct _PH_EMENU_ITEM *Menu,
1865  _In_ ULONG InsertAfterId,
1866  _In_ HWND TreeNewHandle,
1867  _In_ PPH_TREENEW_COLUMN Column
1868  )
1869 {
1870  PPH_EMENU_ITEM parentItem;
1871  ULONG indexInParent;
1872  PPH_COPY_CELL_CONTEXT context;
1873  PH_STRINGREF columnText;
1874  PPH_STRING escapedText;
1875  PPH_STRING menuItemText;
1876  PPH_EMENU_ITEM copyCellItem;
1877 
1878  if (!Column)
1879  return FALSE;
1880 
1881  if (!PhFindEMenuItemEx(Menu, 0, NULL, InsertAfterId, &parentItem, &indexInParent))
1882  return FALSE;
1883 
1884  indexInParent++;
1885 
1886  context = PhAllocate(sizeof(PH_COPY_CELL_CONTEXT));
1887  context->TreeNewHandle = TreeNewHandle;
1888  context->Id = Column->Id;
1889 
1890  PhInitializeStringRef(&columnText, Column->Text);
1891  escapedText = PhEscapeStringForMenuPrefix(&columnText);
1892  menuItemText = PhFormatString(L"Copy \"%s\"", escapedText->Buffer);
1893  PhDereferenceObject(escapedText);
1894  copyCellItem = PhCreateEMenuItem(0, ID_COPY_CELL, menuItemText->Buffer, NULL, context);
1896  context->MenuItemText = menuItemText;
1897 
1898  if (Column->CustomDraw)
1899  copyCellItem->Flags |= PH_EMENU_DISABLED;
1900 
1901  PhInsertEMenuItem(parentItem, copyCellItem, indexInParent);
1902 
1903  return TRUE;
1904 }
1905 
1907  _In_ struct _PH_EMENU_ITEM *SelectedItem
1908  )
1909 {
1910  PPH_COPY_CELL_CONTEXT context;
1911  PH_STRING_BUILDER stringBuilder;
1912  ULONG count;
1913  ULONG selectedCount;
1914  ULONG i;
1915  PPH_TREENEW_NODE node;
1916  PH_TREENEW_GET_CELL_TEXT getCellText;
1917 
1918  if (!SelectedItem)
1919  return FALSE;
1920  if (SelectedItem->Id != ID_COPY_CELL)
1921  return FALSE;
1922 
1923  context = SelectedItem->Context;
1924 
1925  PhInitializeStringBuilder(&stringBuilder, 0x100);
1926  count = TreeNew_GetFlatNodeCount(context->TreeNewHandle);
1927  selectedCount = 0;
1928 
1929  for (i = 0; i < count; i++)
1930  {
1931  node = TreeNew_GetFlatNode(context->TreeNewHandle, i);
1932 
1933  if (node && node->Selected)
1934  {
1935  selectedCount++;
1936 
1937  getCellText.Flags = 0;
1938  getCellText.Node = node;
1939  getCellText.Id = context->Id;
1940  PhInitializeEmptyStringRef(&getCellText.Text);
1941  TreeNew_GetCellText(context->TreeNewHandle, &getCellText);
1942 
1943  PhAppendStringBuilder(&stringBuilder, &getCellText.Text);
1944  PhAppendStringBuilder2(&stringBuilder, L"\r\n");
1945  }
1946  }
1947 
1948  if (stringBuilder.String->Length != 0 && selectedCount == 1)
1949  PhRemoveEndStringBuilder(&stringBuilder, 2);
1950 
1951  PhSetClipboardString(context->TreeNewHandle, &stringBuilder.String->sr);
1952  PhDeleteStringBuilder(&stringBuilder);
1953 
1954  return TRUE;
1955 }
1956 
1958  _In_ HWND RegeditWindow,
1959  _In_ PPH_STRINGREF FavoriteName,
1960  _In_ BOOLEAN UsePhSvc
1961  )
1962 {
1963  HMENU menu;
1964  HMENU favoritesMenu;
1965  ULONG count;
1966  ULONG i;
1967  ULONG id = -1;
1968 
1969  if (!(menu = GetMenu(RegeditWindow)))
1970  return FALSE;
1971 
1972  // Cause the Registry Editor to refresh the Favorites menu.
1973  if (UsePhSvc)
1974  PhSvcCallSendMessage(RegeditWindow, WM_MENUSELECT, MAKEWPARAM(3, MF_POPUP), (LPARAM)menu);
1975  else
1976  SendMessage(RegeditWindow, WM_MENUSELECT, MAKEWPARAM(3, MF_POPUP), (LPARAM)menu);
1977 
1978  if (!(favoritesMenu = GetSubMenu(menu, 3)))
1979  return FALSE;
1980 
1981  // Find our entry.
1982 
1983  count = GetMenuItemCount(favoritesMenu);
1984 
1985  if (count == -1)
1986  return FALSE;
1987  if (count > 1000)
1988  count = 1000;
1989 
1990  for (i = 3; i < count; i++)
1991  {
1992  MENUITEMINFO info = { sizeof(MENUITEMINFO) };
1993  WCHAR buffer[32];
1994 
1995  info.fMask = MIIM_ID | MIIM_STRING;
1996  info.dwTypeData = buffer;
1997  info.cch = sizeof(buffer) / sizeof(WCHAR);
1998  GetMenuItemInfo(favoritesMenu, i, TRUE, &info);
1999 
2000  if (info.cch == FavoriteName->Length / 2)
2001  {
2002  PH_STRINGREF text;
2003 
2004  text.Buffer = buffer;
2005  text.Length = info.cch * 2;
2006 
2007  if (PhEqualStringRef(&text, FavoriteName, TRUE))
2008  {
2009  id = info.wID;
2010  break;
2011  }
2012  }
2013  }
2014 
2015  if (id == -1)
2016  return FALSE;
2017 
2018  // Activate our entry.
2019  if (UsePhSvc)
2020  PhSvcCallSendMessage(RegeditWindow, WM_COMMAND, MAKEWPARAM(id, 0), 0);
2021  else
2022  SendMessage(RegeditWindow, WM_COMMAND, MAKEWPARAM(id, 0), 0);
2023 
2024  // "Close" the Favorites menu and restore normal status bar text.
2025  if (UsePhSvc)
2026  PhSvcCallPostMessage(RegeditWindow, WM_MENUSELECT, MAKEWPARAM(0, 0xffff), 0);
2027  else
2028  PostMessage(RegeditWindow, WM_MENUSELECT, MAKEWPARAM(0, 0xffff), 0);
2029 
2030  // Bring regedit to the top.
2031  if (IsIconic(RegeditWindow))
2032  {
2033  ShowWindow(RegeditWindow, SW_RESTORE);
2034  SetForegroundWindow(RegeditWindow);
2035  }
2036  else
2037  {
2038  SetForegroundWindow(RegeditWindow);
2039  }
2040 
2041  return TRUE;
2042 }
2043 
2052  _In_ HWND hWnd,
2053  _In_ PPH_STRING KeyName
2054  )
2055 {
2056  static PH_STRINGREF favoritesKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows\\CurrentVersion\\Applets\\Regedit\\Favorites");
2057 
2058  BOOLEAN result = FALSE;
2059  HWND regeditWindow;
2060  HANDLE favoritesKeyHandle;
2061  WCHAR favoriteName[32];
2062  UNICODE_STRING valueName;
2063  PH_STRINGREF valueNameSr;
2064  PPH_STRING expandedKeyName;
2065 
2066  regeditWindow = FindWindow(L"RegEdit_RegEdit", NULL);
2067 
2068  if (!regeditWindow)
2069  {
2070  PhShellOpenKey(hWnd, KeyName);
2071  return TRUE;
2072  }
2073 
2074  if (!PhElevated)
2075  {
2076  if (!PhUiConnectToPhSvc(hWnd, FALSE))
2077  return FALSE;
2078  }
2079 
2080  // Create our entry in Favorites.
2081 
2082  if (!NT_SUCCESS(PhCreateKey(
2083  &favoritesKeyHandle,
2084  KEY_WRITE,
2086  &favoritesKeyName,
2087  0,
2088  0,
2089  NULL
2090  )))
2091  goto CleanupExit;
2092 
2093  memcpy(favoriteName, L"A_ProcessHacker", 15 * sizeof(WCHAR));
2094  PhGenerateRandomAlphaString(&favoriteName[15], 16);
2095  RtlInitUnicodeString(&valueName, favoriteName);
2096  PhUnicodeStringToStringRef(&valueName, &valueNameSr);
2097 
2098  expandedKeyName = PhExpandKeyName(KeyName, TRUE);
2099  NtSetValueKey(favoritesKeyHandle, &valueName, 0, REG_SZ, expandedKeyName->Buffer, (ULONG)expandedKeyName->Length + 2);
2100  PhDereferenceObject(expandedKeyName);
2101 
2102  // Select our entry in regedit.
2103  result = PhpSelectFavoriteInRegedit(regeditWindow, &valueNameSr, !PhElevated);
2104 
2105  NtDeleteValueKey(favoritesKeyHandle, &valueName);
2106  NtClose(favoritesKeyHandle);
2107 
2108 CleanupExit:
2109  if (!PhElevated)
2111 
2112  return result;
2113 }