Process Hacker
guisup.c
Go to the documentation of this file.
1 /*
2  * Process Hacker -
3  * GUI support functions
4  *
5  * Copyright (C) 2009-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 <phgui.h>
24 #include <guisupp.h>
25 #include <windowsx.h>
26 
43 
45  VOID
46  )
47 {
48  HMODULE shell32Handle;
49  HMODULE shlwapiHandle;
50  HMODULE uxthemeHandle;
51 
52  shell32Handle = LoadLibrary(L"shell32.dll");
53  shlwapiHandle = LoadLibrary(L"shlwapi.dll");
54  uxthemeHandle = LoadLibrary(L"uxtheme.dll");
55 
56  if (WINDOWS_HAS_UAC)
57  ChangeWindowMessageFilter_I = PhGetModuleProcAddress(L"user32.dll", "ChangeWindowMessageFilter");
59  IsImmersiveProcess_I = PhGetModuleProcAddress(L"user32.dll", "IsImmersiveProcess");
60  RunFileDlg = (PVOID)GetProcAddress(shell32Handle, (PSTR)61);
61  SetWindowTheme_I = (PVOID)GetProcAddress(uxthemeHandle, "SetWindowTheme");
62  IsThemeActive_I = (PVOID)GetProcAddress(uxthemeHandle, "IsThemeActive");
63  OpenThemeData_I = (PVOID)GetProcAddress(uxthemeHandle, "OpenThemeData");
64  CloseThemeData_I = (PVOID)GetProcAddress(uxthemeHandle, "CloseThemeData");
65  IsThemePartDefined_I = (PVOID)GetProcAddress(uxthemeHandle, "IsThemePartDefined");
66  DrawThemeBackground_I = (PVOID)GetProcAddress(uxthemeHandle, "DrawThemeBackground");
67  DrawThemeText_I = (PVOID)GetProcAddress(uxthemeHandle, "DrawThemeText");
68  GetThemeInt_I = (PVOID)GetProcAddress(uxthemeHandle, "GetThemeInt");
69  SHAutoComplete_I = (PVOID)GetProcAddress(shlwapiHandle, "SHAutoComplete");
70  SHCreateShellItem_I = (PVOID)GetProcAddress(shell32Handle, "SHCreateShellItem");
71  SHOpenFolderAndSelectItems_I = (PVOID)GetProcAddress(shell32Handle, "SHOpenFolderAndSelectItems");
72  SHParseDisplayName_I = (PVOID)GetProcAddress(shell32Handle, "SHParseDisplayName");
73  TaskDialogIndirect_I = PhGetModuleProcAddress(L"comctl32.dll", "TaskDialogIndirect");
74 }
75 
77  _In_ HWND Handle,
78  _In_ PWSTR Theme
79  )
80 {
82  {
83  if (SetWindowTheme_I)
84  SetWindowTheme_I(Handle, Theme, NULL);
85  }
86 }
87 
89  _In_ HWND ListViewHandle,
90  _In_ INT Index,
91  _In_ INT DisplayIndex,
92  _In_ INT SubItemIndex,
93  _In_ INT Format,
94  _In_ INT Width,
95  _In_ PWSTR Text
96  )
97 {
98  LVCOLUMN column;
99 
100  column.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM | LVCF_ORDER;
101  column.fmt = Format;
102  column.cx = Width;
103  column.pszText = Text;
104  column.iSubItem = SubItemIndex;
105  column.iOrder = DisplayIndex;
106 
107  return ListView_InsertColumn(ListViewHandle, Index, &column);
108 }
109 
111  _In_ HWND ListViewHandle,
112  _In_ INT Index,
113  _In_ PWSTR Text,
114  _In_opt_ PVOID Param
115  )
116 {
117  LVITEM item;
118 
119  item.mask = LVIF_TEXT | LVIF_PARAM;
120  item.iItem = Index;
121  item.iSubItem = 0;
122  item.pszText = Text;
123  item.lParam = (LPARAM)Param;
124 
125  return ListView_InsertItem(ListViewHandle, &item);
126 }
127 
129  _In_ HWND ListViewHandle,
130  _In_ INT StartIndex,
131  _In_ ULONG Flags
132  )
133 {
134  return ListView_GetNextItem(ListViewHandle, StartIndex, Flags);
135 }
136 
138  _In_ HWND ListViewHandle,
139  _In_ INT StartIndex,
140  _In_opt_ PVOID Param
141  )
142 {
143  LVFINDINFO findInfo;
144 
145  findInfo.flags = LVFI_PARAM;
146  findInfo.lParam = (LPARAM)Param;
147 
148  return ListView_FindItem(ListViewHandle, StartIndex, &findInfo);
149 }
150 
152  _In_ HWND ListViewHandle,
153  _In_ INT Index,
154  _Out_ PINT ImageIndex
155  )
156 {
157  LOGICAL result;
158  LVITEM item;
159 
160  item.mask = LVIF_IMAGE;
161  item.iItem = Index;
162  item.iSubItem = 0;
163 
164  result = ListView_GetItem(ListViewHandle, &item);
165 
166  if (!result)
167  return result;
168 
169  *ImageIndex = item.iImage;
170 
171  return result;
172 }
173 
175  _In_ HWND ListViewHandle,
176  _In_ INT Index,
177  _Out_ PVOID *Param
178  )
179 {
180  LOGICAL result;
181  LVITEM item;
182 
183  item.mask = LVIF_PARAM;
184  item.iItem = Index;
185  item.iSubItem = 0;
186 
187  result = ListView_GetItem(ListViewHandle, &item);
188 
189  if (!result)
190  return result;
191 
192  *Param = (PVOID)item.lParam;
193 
194  return result;
195 }
196 
198  _In_ HWND ListViewHandle,
199  _In_ INT Index
200  )
201 {
202  ListView_DeleteItem(ListViewHandle, Index);
203 }
204 
206  _In_ HWND ListViewHandle,
207  _In_ INT Index,
208  _In_ INT ImageIndex
209  )
210 {
211  LVITEM item;
212 
213  item.mask = LVIF_IMAGE;
214  item.iItem = Index;
215  item.iSubItem = 0;
216  item.iImage = ImageIndex;
217 
218  ListView_SetItem(ListViewHandle, &item);
219 }
220 
222  _In_ HWND ListViewHandle,
223  _In_ INT Index,
224  _In_ INT SubItemIndex,
225  _In_ PWSTR Text
226  )
227 {
228  LVITEM item;
229 
230  item.mask = LVIF_TEXT;
231  item.iItem = Index;
232  item.iSubItem = SubItemIndex;
233  item.pszText = Text;
234 
235  ListView_SetItem(ListViewHandle, &item);
236 }
237 
239  _In_ HWND ListViewHandle,
240  _In_ PPH_STRING Settings
241  )
242 {
243 #define ORDER_LIMIT 50
244  PH_STRINGREF remainingPart;
245  ULONG columnIndex;
246  ULONG orderArray[ORDER_LIMIT]; // HACK, but reasonable limit
247  ULONG maxOrder;
248 
249  if (Settings->Length == 0)
250  return FALSE;
251 
252  remainingPart = Settings->sr;
253  columnIndex = 0;
254  memset(orderArray, 0, sizeof(orderArray));
255  maxOrder = 0;
256 
257  while (remainingPart.Length != 0)
258  {
259  PH_STRINGREF columnPart;
260  PH_STRINGREF orderPart;
261  PH_STRINGREF widthPart;
262  ULONG64 integer;
263  ULONG order;
264  LVCOLUMN lvColumn;
265 
266  PhSplitStringRefAtChar(&remainingPart, '|', &columnPart, &remainingPart);
267 
268  if (columnPart.Length == 0)
269  return FALSE;
270 
271  PhSplitStringRefAtChar(&columnPart, ',', &orderPart, &widthPart);
272 
273  if (orderPart.Length == 0 || widthPart.Length == 0)
274  return FALSE;
275 
276  // Order
277 
278  if (!PhStringToInteger64(&orderPart, 10, &integer))
279  return FALSE;
280 
281  order = (ULONG)integer;
282 
283  if (order < ORDER_LIMIT)
284  {
285  orderArray[order] = columnIndex;
286 
287  if (maxOrder < order + 1)
288  maxOrder = order + 1;
289  }
290 
291  // Width
292 
293  if (!PhStringToInteger64(&widthPart, 10, &integer))
294  return FALSE;
295 
296  lvColumn.mask = LVCF_WIDTH;
297  lvColumn.cx = (ULONG)integer;
298  ListView_SetColumn(ListViewHandle, columnIndex, &lvColumn);
299 
300  columnIndex++;
301  }
302 
303  ListView_SetColumnOrderArray(ListViewHandle, maxOrder, orderArray);
304 
305  return TRUE;
306 }
307 
309  _In_ HWND ListViewHandle
310  )
311 {
312  PH_STRING_BUILDER stringBuilder;
313  ULONG i = 0;
314  LVCOLUMN lvColumn;
315 
316  PhInitializeStringBuilder(&stringBuilder, 20);
317 
318  lvColumn.mask = LVCF_WIDTH | LVCF_ORDER;
319 
320  while (ListView_GetColumn(ListViewHandle, i, &lvColumn))
321  {
323  &stringBuilder,
324  L"%u,%u|",
325  lvColumn.iOrder,
326  lvColumn.cx
327  );
328  i++;
329  }
330 
331  if (stringBuilder.String->Length != 0)
332  PhRemoveEndStringBuilder(&stringBuilder, 1);
333 
334  return PhFinalStringBuilderString(&stringBuilder);
335 }
336 
338  _In_ HWND TabControlHandle,
339  _In_ INT Index,
340  _In_ PWSTR Text
341  )
342 {
343  TCITEM item;
344 
345  item.mask = TCIF_TEXT;
346  item.pszText = Text;
347 
348  return TabCtrl_InsertItem(TabControlHandle, Index, &item);
349 }
350 
352  _In_ HWND hwnd
353  )
354 {
355  PPH_STRING text;
356 
357  PhGetWindowTextEx(hwnd, 0, &text);
358  return text;
359 }
360 
362  _In_ HWND hwnd,
363  _In_ ULONG Flags,
364  _Out_opt_ PPH_STRING *Text
365  )
366 {
367  PPH_STRING string;
368  ULONG length;
369 
370  if (Flags & PH_GET_WINDOW_TEXT_INTERNAL)
371  {
372  if (Flags & PH_GET_WINDOW_TEXT_LENGTH_ONLY)
373  {
374  WCHAR buffer[32];
375  length = InternalGetWindowText(hwnd, buffer, sizeof(buffer) / sizeof(WCHAR));
376  }
377  else
378  {
379  // TODO: Resize the buffer until we get the entire thing.
380  string = PhCreateStringEx(NULL, 256 * sizeof(WCHAR));
381  length = InternalGetWindowText(hwnd, string->Buffer, (ULONG)string->Length / sizeof(WCHAR) + 1);
382  string->Length = length * sizeof(WCHAR);
383 
384  if (Text)
385  *Text = string;
386  else
387  PhDereferenceObject(string);
388  }
389 
390  return length;
391  }
392  else
393  {
394  length = GetWindowTextLength(hwnd);
395 
396  if (length == 0 || (Flags & PH_GET_WINDOW_TEXT_LENGTH_ONLY))
397  {
398  if (Text)
399  *Text = PhReferenceEmptyString();
400 
401  return length;
402  }
403 
404  string = PhCreateStringEx(NULL, length * sizeof(WCHAR));
405 
406  if (GetWindowText(hwnd, string->Buffer, (ULONG)string->Length / sizeof(WCHAR) + 1))
407  {
408  if (Text)
409  *Text = string;
410  else
411  PhDereferenceObject(string);
412 
413  return length;
414  }
415  else
416  {
417  if (Text)
418  *Text = PhReferenceEmptyString();
419 
420  PhDereferenceObject(string);
421 
422  return 0;
423  }
424  }
425 }
426 
428  _In_ HWND hWnd,
429  _In_ PWSTR *Strings,
430  _In_ ULONG NumberOfStrings
431  )
432 {
433  ULONG i;
434 
435  for (i = 0; i < NumberOfStrings; i++)
436  ComboBox_AddString(hWnd, Strings[i]);
437 }
438 
440  _In_ HWND hwnd,
441  _In_ INT Index
442  )
443 {
444  PPH_STRING string;
445  ULONG length;
446 
447  if (Index == -1)
448  {
449  Index = ComboBox_GetCurSel(hwnd);
450 
451  if (Index == -1)
452  return NULL;
453  }
454 
455  length = ComboBox_GetLBTextLen(hwnd, Index);
456 
457  if (length == CB_ERR)
458  return NULL;
459  if (length == 0)
460  return PhReferenceEmptyString();
461 
462  string = PhCreateStringEx(NULL, length * 2);
463 
464  if (ComboBox_GetLBText(hwnd, Index, string->Buffer) != CB_ERR)
465  {
466  return string;
467  }
468  else
469  {
470  PhDereferenceObject(string);
471  return NULL;
472  }
473 }
474 
476  _In_ HWND hwnd,
477  _In_ PWSTR String,
478  _In_ BOOLEAN Partial
479  )
480 {
481  if (Partial)
482  {
483  return ComboBox_SelectString(hwnd, -1, String);
484  }
485  else
486  {
487  INT index;
488 
489  index = ComboBox_FindStringExact(hwnd, -1, String);
490 
491  if (index == CB_ERR)
492  return CB_ERR;
493 
494  ComboBox_SetCurSel(hwnd, index);
495 
496  return index;
497  }
498 }
499 
501  _In_ HWND hwnd,
502  _In_ INT Index
503  )
504 {
505  PPH_STRING string;
506  ULONG length;
507 
508  if (Index == -1)
509  {
510  Index = ListBox_GetCurSel(hwnd);
511 
512  if (Index == -1)
513  return NULL;
514  }
515 
516  length = ListBox_GetTextLen(hwnd, Index);
517 
518  if (length == LB_ERR)
519  return NULL;
520  if (length == 0)
521  return PhReferenceEmptyString();
522 
523  string = PhCreateStringEx(NULL, length * 2);
524 
525  if (ListBox_GetText(hwnd, Index, string->Buffer) != LB_ERR)
526  {
527  return string;
528  }
529  else
530  {
531  PhDereferenceObject(string);
532  return NULL;
533  }
534 }
535 
537  _In_ HWND hWnd,
538  _In_ ULONG State,
539  _In_ ULONG Mask
540  )
541 {
542  ULONG i;
543  ULONG count;
544 
545  count = ListView_GetItemCount(hWnd);
546 
547  if (count == -1)
548  return;
549 
550  for (i = 0; i < count; i++)
551  {
552  ListView_SetItemState(hWnd, i, State, Mask);
553  }
554 }
555 
557  _In_ HWND hWnd
558  )
559 {
560  INT index;
561  PVOID param;
562 
564  hWnd,
565  -1,
566  LVNI_SELECTED
567  );
568 
569  if (index != -1)
570  {
572  hWnd,
573  index,
574  &param
575  ))
576  {
577  return param;
578  }
579  }
580 
581  return NULL;
582 }
583 
585  _In_ HWND hWnd,
586  _Out_ PVOID **Items,
587  _Out_ PULONG NumberOfItems
588  )
589 {
590  PPH_LIST list;
591  ULONG index;
592  PVOID param;
593 
594  list = PhCreateList(2);
595  index = -1;
596 
597  while ((index = PhFindListViewItemByFlags(
598  hWnd,
599  index,
600  LVNI_SELECTED
601  )) != -1)
602  {
604  hWnd,
605  index,
606  &param
607  ))
608  {
609  PhAddItemList(list, param);
610  }
611  }
612 
613  *Items = PhAllocateCopy(list->Items, sizeof(PVOID) * list->Count);
614  *NumberOfItems = list->Count;
615 
616  PhDereferenceObject(list);
617 }
618 
620  _In_ HIMAGELIST ImageList,
621  _In_ INT Index,
622  _In_ HINSTANCE InstanceHandle,
623  _In_ LPCWSTR BitmapName
624  )
625 {
626  HBITMAP bitmap;
627 
628  bitmap = LoadImage(InstanceHandle, BitmapName, IMAGE_BITMAP, 0, 0, 0);
629 
630  if (bitmap)
631  {
632  ImageList_Replace(ImageList, Index, bitmap, NULL);
633  DeleteObject(bitmap);
634  }
635 }
636 
646  _Out_opt_ HICON *SmallIcon,
647  _Out_opt_ HICON *LargeIcon
648  )
649 {
650  static PH_INITONCE initOnce = PH_INITONCE_INIT;
651  static HICON smallIcon = NULL;
652  static HICON largeIcon = NULL;
653 
654  // This no longer uses SHGetFileInfo because it is *very* slow and causes
655  // many other DLLs to be loaded, increasing memory usage. The worst thing
656  // about it, however, is that it is horribly incompatible with multi-threading.
657  // The first time it is called, it tries to perform some one-time initialization.
658  // It guards this with a lock, but when multiple threads try to call the function
659  // at the same time, instead of waiting for initialization to finish it simply
660  // fails the other threads.
661 
662  if (PhBeginInitOnce(&initOnce))
663  {
664  PPH_STRING systemDirectory;
665  PPH_STRING dllFileName;
666 
667  // imageres,11 (Windows 10 and above), user32,0 (Vista and above) or shell32,2 (XP) contains the default application icon.
668 
669  if (systemDirectory = PhGetSystemDirectory())
670  {
671  PH_STRINGREF dllBaseName;
672  ULONG index;
673 
674  // TODO: Find a better solution.
675  if (WindowsVersion >= WINDOWS_10)
676  {
677  PhInitializeStringRef(&dllBaseName, L"\\imageres.dll");
678  index = 11;
679  }
680  else if (WindowsVersion >= WINDOWS_VISTA)
681  {
682  PhInitializeStringRef(&dllBaseName, L"\\user32.dll");
683  index = 0;
684  }
685  else
686  {
687  PhInitializeStringRef(&dllBaseName, L"\\shell32.dll");
688  index = 2;
689  }
690 
691  dllFileName = PhConcatStringRef2(&systemDirectory->sr, &dllBaseName);
692  PhDereferenceObject(systemDirectory);
693 
694  ExtractIconEx(dllFileName->Buffer, index, &largeIcon, &smallIcon, 1);
695  PhDereferenceObject(dllFileName);
696  }
697 
698  // Fallback icons - this is bad, because the icon isn't scaled correctly.
699  if (!smallIcon)
700  smallIcon = LoadIcon(NULL, IDI_APPLICATION);
701  if (!largeIcon)
702  largeIcon = LoadIcon(NULL, IDI_APPLICATION);
703 
704  PhEndInitOnce(&initOnce);
705  }
706 
707  if (SmallIcon)
708  *SmallIcon = smallIcon;
709  if (LargeIcon)
710  *LargeIcon = largeIcon;
711 }
712 
714  _In_opt_ PWSTR FileName,
715  _In_opt_ PWSTR DefaultExtension,
716  _In_ BOOLEAN LargeIcon
717  )
718 {
719  SHFILEINFO fileInfo;
720  ULONG iconFlag;
721  HICON icon;
722 
723  if (DefaultExtension && PhEqualStringZ(DefaultExtension, L".exe", TRUE))
724  {
725  // Special case for executable files (see above for reasoning).
726 
727  icon = NULL;
728 
729  if (FileName)
730  {
731  ExtractIconEx(
732  FileName,
733  0,
734  LargeIcon ? &icon : NULL,
735  !LargeIcon ? &icon : NULL,
736  1
737  );
738  }
739 
740  if (!icon)
741  {
743  !LargeIcon ? &icon : NULL,
744  LargeIcon ? &icon : NULL
745  );
746 
747  if (icon)
748  icon = DuplicateIcon(NULL, icon);
749  }
750 
751  return icon;
752  }
753 
754  iconFlag = LargeIcon ? SHGFI_LARGEICON : SHGFI_SMALLICON;
755  icon = NULL;
756 
757  if (FileName && SHGetFileInfo(
758  FileName,
759  0,
760  &fileInfo,
761  sizeof(SHFILEINFO),
762  SHGFI_ICON | iconFlag
763  ))
764  {
765  icon = fileInfo.hIcon;
766  }
767 
768  if (!icon && DefaultExtension)
769  {
770  if (SHGetFileInfo(
771  DefaultExtension,
772  FILE_ATTRIBUTE_NORMAL,
773  &fileInfo,
774  sizeof(SHFILEINFO),
775  SHGFI_ICON | iconFlag | SHGFI_USEFILEATTRIBUTES
776  ))
777  icon = fileInfo.hIcon;
778  }
779 
780  return icon;
781 }
782 
784  _In_ HWND hWnd,
785  _In_ ULONG Format,
786  _In_ HANDLE Data
787  )
788 {
789  if (OpenClipboard(hWnd))
790  {
791  if (!EmptyClipboard())
792  goto Fail;
793 
794  if (!SetClipboardData(Format, Data))
795  goto Fail;
796 
797  CloseClipboard();
798 
799  return;
800  }
801 
802 Fail:
803  GlobalFree(Data);
804 }
805 
807  _In_ HWND hWnd,
808  _In_ PPH_STRINGREF String
809  )
810 {
811  HANDLE data;
812  PVOID memory;
813 
814  data = GlobalAlloc(GMEM_MOVEABLE, String->Length + sizeof(WCHAR));
815  memory = GlobalLock(data);
816 
817  memcpy(memory, String->Buffer, String->Length);
818  *(PWCHAR)((PCHAR)memory + String->Length) = 0;
819 
820  GlobalUnlock(memory);
821 
822  PhpSetClipboardData(hWnd, CF_UNICODETEXT, data);
823 }
824 
826  _In_ HWND Parent,
827  _In_ ULONG Style,
828  _In_ PVOID Instance,
829  _In_ PWSTR Template,
830  _In_ DLGPROC DialogProc,
831  _In_ PVOID Parameter
832  )
833 {
834  HRSRC resourceInfo;
835  ULONG resourceSize;
836  HGLOBAL resourceHandle;
837  PDLGTEMPLATEEX dialog;
838  PDLGTEMPLATEEX dialogCopy;
839  HWND dialogHandle;
840 
841  resourceInfo = FindResource(Instance, Template, MAKEINTRESOURCE(RT_DIALOG));
842 
843  if (!resourceInfo)
844  return NULL;
845 
846  resourceSize = SizeofResource(Instance, resourceInfo);
847 
848  if (resourceSize == 0)
849  return NULL;
850 
851  resourceHandle = LoadResource(Instance, resourceInfo);
852 
853  if (!resourceHandle)
854  return NULL;
855 
856  dialog = LockResource(resourceHandle);
857 
858  if (!dialog)
859  return NULL;
860 
861  dialogCopy = PhAllocateCopy(dialog, resourceSize);
862 
863  if (dialogCopy->signature == 0xffff)
864  {
865  dialogCopy->style = Style;
866  }
867  else
868  {
869  ((DLGTEMPLATE *)dialogCopy)->style = Style;
870  }
871 
872  dialogHandle = CreateDialogIndirectParam(Instance, (DLGTEMPLATE *)dialogCopy, Parent, DialogProc, (LPARAM)Parameter);
873 
874  PhFree(dialogCopy);
875 
876  return dialogHandle;
877 }
878 
880  _Out_ PPH_LAYOUT_MANAGER Manager,
881  _In_ HWND RootWindowHandle
882  )
883 {
884  Manager->List = PhCreateList(4);
885  Manager->LayoutNumber = 0;
886 
887  Manager->RootItem.Handle = RootWindowHandle;
888  GetClientRect(Manager->RootItem.Handle, &Manager->RootItem.Rect);
889  Manager->RootItem.OrigRect = Manager->RootItem.Rect;
890  Manager->RootItem.ParentItem = NULL;
891  Manager->RootItem.LayoutParentItem = NULL;
892  Manager->RootItem.LayoutNumber = 0;
893  Manager->RootItem.NumberOfChildren = 0;
894  Manager->RootItem.DeferHandle = NULL;
895 }
896 
898  _Inout_ PPH_LAYOUT_MANAGER Manager
899  )
900 {
901  ULONG i;
902 
903  for (i = 0; i < Manager->List->Count; i++)
904  PhFree(Manager->List->Items[i]);
905 
906  PhDereferenceObject(Manager->List);
907 }
908 
909 // HACK: The maths below is all horribly broken, especially the HACK for multiline tab
910 // controls.
911 
913  _Inout_ PPH_LAYOUT_MANAGER Manager,
914  _In_ HWND Handle,
915  _In_opt_ PPH_LAYOUT_ITEM ParentItem,
916  _In_ ULONG Anchor
917  )
918 {
919  PPH_LAYOUT_ITEM layoutItem;
920  RECT dummy = { 0 };
921 
922  layoutItem = PhAddLayoutItemEx(
923  Manager,
924  Handle,
925  ParentItem,
926  Anchor,
927  dummy
928  );
929 
930  layoutItem->Margin = layoutItem->Rect;
931  PhConvertRect(&layoutItem->Margin, &layoutItem->ParentItem->Rect);
932 
933  if (layoutItem->ParentItem != layoutItem->LayoutParentItem)
934  {
935  // Fix the margin because the item has a dummy parent. They share
936  // the same layout parent item.
937  layoutItem->Margin.top -= layoutItem->ParentItem->Rect.top;
938  layoutItem->Margin.left -= layoutItem->ParentItem->Rect.left;
939  layoutItem->Margin.right = layoutItem->ParentItem->Margin.right;
940  layoutItem->Margin.bottom = layoutItem->ParentItem->Margin.bottom;
941  }
942 
943  return layoutItem;
944 }
945 
947  _Inout_ PPH_LAYOUT_MANAGER Manager,
948  _In_ HWND Handle,
949  _In_opt_ PPH_LAYOUT_ITEM ParentItem,
950  _In_ ULONG Anchor,
951  _In_ RECT Margin
952  )
953 {
954  PPH_LAYOUT_ITEM item;
955 
956  if (!ParentItem)
957  ParentItem = &Manager->RootItem;
958 
959  item = PhAllocate(sizeof(PH_LAYOUT_ITEM));
960  item->Handle = Handle;
961  item->ParentItem = ParentItem;
962  item->LayoutNumber = Manager->LayoutNumber;
963  item->NumberOfChildren = 0;
964  item->DeferHandle = NULL;
965  item->Anchor = Anchor;
966 
967  item->LayoutParentItem = item->ParentItem;
968 
969  while ((item->LayoutParentItem->Anchor & PH_LAYOUT_DUMMY_MASK) &&
971  {
973  }
974 
976 
977  GetWindowRect(Handle, &item->Rect);
978  MapWindowPoints(NULL, item->LayoutParentItem->Handle, (POINT *)&item->Rect, 2);
979 
980  if (item->Anchor & PH_LAYOUT_TAB_CONTROL)
981  {
982  // We want to convert the tab control rectangle to the tab page display rectangle.
983  TabCtrl_AdjustRect(Handle, FALSE, &item->Rect);
984  }
985 
986  item->Margin = Margin;
987 
988  item->OrigRect = item->Rect;
989 
990  PhAddItemList(Manager->List, item);
991 
992  return item;
993 }
994 
996  _Inout_ PPH_LAYOUT_MANAGER Manager,
997  _Inout_ PPH_LAYOUT_ITEM Item
998  )
999 {
1000  RECT rect;
1001  BOOLEAN hasDummyParent;
1002 
1003  if (Item->NumberOfChildren > 0 && !Item->DeferHandle)
1004  Item->DeferHandle = BeginDeferWindowPos(Item->NumberOfChildren);
1005 
1006  if (Item->LayoutNumber == Manager->LayoutNumber)
1007  return;
1008 
1009  // If this is the root item we must stop here.
1010  if (!Item->ParentItem)
1011  return;
1012 
1013  PhpLayoutItemLayout(Manager, Item->ParentItem);
1014 
1015  if (Item->ParentItem != Item->LayoutParentItem)
1016  {
1017  PhpLayoutItemLayout(Manager, Item->LayoutParentItem);
1018  hasDummyParent = TRUE;
1019  }
1020  else
1021  {
1022  hasDummyParent = FALSE;
1023  }
1024 
1025  GetWindowRect(Item->Handle, &Item->Rect);
1026  MapWindowPoints(NULL, Item->LayoutParentItem->Handle, (POINT *)&Item->Rect, 2);
1027 
1028  if (Item->Anchor & PH_LAYOUT_TAB_CONTROL)
1029  {
1030  // We want to convert the tab control rectangle to the tab page display rectangle.
1031  TabCtrl_AdjustRect(Item->Handle, FALSE, &Item->Rect);
1032  }
1033 
1034  if (!(Item->Anchor & PH_LAYOUT_DUMMY_MASK))
1035  {
1036  // Convert right/bottom into margins to make the calculations
1037  // easier.
1038  rect = Item->Rect;
1039  PhConvertRect(&rect, &Item->LayoutParentItem->Rect);
1040 
1041  if (!(Item->Anchor & (PH_ANCHOR_LEFT | PH_ANCHOR_RIGHT)))
1042  {
1043  // TODO
1044  PhRaiseStatus(STATUS_NOT_IMPLEMENTED);
1045  }
1046  else if (Item->Anchor & PH_ANCHOR_RIGHT)
1047  {
1048  if (Item->Anchor & PH_ANCHOR_LEFT)
1049  {
1050  rect.left = (hasDummyParent ? Item->ParentItem->Rect.left : 0) + Item->Margin.left;
1051  rect.right = Item->Margin.right;
1052  }
1053  else
1054  {
1055  ULONG diff = Item->Margin.right - rect.right;
1056 
1057  rect.left -= diff;
1058  rect.right += diff;
1059  }
1060  }
1061 
1062  if (!(Item->Anchor & (PH_ANCHOR_TOP | PH_ANCHOR_BOTTOM)))
1063  {
1064  // TODO
1065  PhRaiseStatus(STATUS_NOT_IMPLEMENTED);
1066  }
1067  else if (Item->Anchor & PH_ANCHOR_BOTTOM)
1068  {
1069  if (Item->Anchor & PH_ANCHOR_TOP)
1070  {
1071  // tab control hack
1072  rect.top = (hasDummyParent ? Item->ParentItem->Rect.top : 0) + Item->Margin.top;
1073  rect.bottom = Item->Margin.bottom;
1074  }
1075  else
1076  {
1077  ULONG diff = Item->Margin.bottom - rect.bottom;
1078 
1079  rect.top -= diff;
1080  rect.bottom += diff;
1081  }
1082  }
1083 
1084  // Convert the right/bottom back into co-ordinates.
1085  PhConvertRect(&rect, &Item->LayoutParentItem->Rect);
1086  Item->Rect = rect;
1087 
1088  if (!(Item->Anchor & PH_LAYOUT_IMMEDIATE_RESIZE))
1089  {
1090  Item->LayoutParentItem->DeferHandle = DeferWindowPos(
1091  Item->LayoutParentItem->DeferHandle, Item->Handle,
1092  NULL, rect.left, rect.top,
1093  rect.right - rect.left, rect.bottom - rect.top,
1094  SWP_NOACTIVATE | SWP_NOZORDER
1095  );
1096  }
1097  else
1098  {
1099  // This is needed for tab controls, so that TabCtrl_AdjustRect will give us an up-to-date result.
1100  SetWindowPos(
1101  Item->Handle,
1102  NULL, rect.left, rect.top,
1103  rect.right - rect.left, rect.bottom - rect.top,
1104  SWP_NOACTIVATE | SWP_NOZORDER
1105  );
1106  }
1107  }
1108 
1109  Item->LayoutNumber = Manager->LayoutNumber;
1110 }
1111 
1113  _Inout_ PPH_LAYOUT_MANAGER Manager
1114  )
1115 {
1116  ULONG i;
1117 
1118  Manager->LayoutNumber++;
1119 
1120  GetClientRect(Manager->RootItem.Handle, &Manager->RootItem.Rect);
1121 
1122  for (i = 0; i < Manager->List->Count; i++)
1123  {
1124  PPH_LAYOUT_ITEM item = (PPH_LAYOUT_ITEM)Manager->List->Items[i];
1125 
1126  PhpLayoutItemLayout(Manager, item);
1127  }
1128 
1129  for (i = 0; i < Manager->List->Count; i++)
1130  {
1131  PPH_LAYOUT_ITEM item = (PPH_LAYOUT_ITEM)Manager->List->Items[i];
1132 
1133  if (item->DeferHandle)
1134  {
1135  EndDeferWindowPos(item->DeferHandle);
1136  item->DeferHandle = NULL;
1137  }
1138 
1139  if (item->Anchor & PH_LAYOUT_FORCE_INVALIDATE)
1140  {
1141  InvalidateRect(item->Handle, NULL, FALSE);
1142  }
1143  }
1144 
1145  if (Manager->RootItem.DeferHandle)
1146  {
1147  EndDeferWindowPos(Manager->RootItem.DeferHandle);
1148  Manager->RootItem.DeferHandle = NULL;
1149  }
1150 }