Process Hacker
emenu.c
Go to the documentation of this file.
1 /*
2  * Process Hacker -
3  * extended menus
4  *
5  * Copyright (C) 2010-2011 wj32
6  *
7  * This file is part of Process Hacker.
8  *
9  * Process Hacker is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * Process Hacker is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include <phgui.h>
24 #include <emenu.h>
25 
26 static const PH_FLAG_MAPPING EMenuTypeMappings[] =
27 {
28  { PH_EMENU_MENUBARBREAK, MFT_MENUBARBREAK },
29  { PH_EMENU_MENUBREAK, MFT_MENUBREAK },
30  { PH_EMENU_RADIOCHECK, MFT_RADIOCHECK }
31 };
32 
33 static const PH_FLAG_MAPPING EMenuStateMappings[] =
34 {
35  { PH_EMENU_CHECKED, MFS_CHECKED },
36  { PH_EMENU_DEFAULT, MFS_DEFAULT },
37  { PH_EMENU_DISABLED, MFS_DISABLED },
38  { PH_EMENU_HIGHLIGHT, MFS_HILITE }
39 };
40 
42  VOID
43  )
44 {
45  PPH_EMENU_ITEM item;
46 
47  item = PhAllocate(sizeof(PH_EMENU_ITEM));
48  memset(item, 0, sizeof(PH_EMENU_ITEM));
49 
50  return item;
51 }
52 
73  _In_ ULONG Flags,
74  _In_ ULONG Id,
75  _In_ PWSTR Text,
76  _In_opt_ HBITMAP Bitmap,
77  _In_opt_ PVOID Context
78  )
79 {
80  PPH_EMENU_ITEM item;
81 
82  item = PhAllocateEMenuItem();
83 
84  item->Flags = Flags;
85  item->Id = Id;
86  item->Text = Text;
87  item->Bitmap = Bitmap;
88 
89  item->Context = Context;
90 
91  return item;
92 }
93 
103  _In_ PPH_EMENU_ITEM Item
104  )
105 {
106  if (Item->DeleteFunction)
107  Item->DeleteFunction(Item);
108 
109  if ((Item->Flags & PH_EMENU_TEXT_OWNED) && Item->Text)
110  PhFree(Item->Text);
111  if ((Item->Flags & PH_EMENU_BITMAP_OWNED) && Item->Bitmap)
112  DeleteObject(Item->Bitmap);
113 
114  if (Item->Items)
115  {
116  ULONG i;
117 
118  for (i = 0; i < Item->Items->Count; i++)
119  {
120  PhpDestroyEMenuItem(Item->Items->Items[i]);
121  }
122 
123  PhDereferenceObject(Item->Items);
124  }
125 
126  PhFree(Item);
127 }
128 
137  _In_ PPH_EMENU_ITEM Item
138  )
139 {
140  // Remove the item from its parent, if it has one.
141  if (Item->Parent)
142  PhRemoveEMenuItem(NULL, Item, -1);
143 
144  PhpDestroyEMenuItem(Item);
145 }
146 
167  _In_ PPH_EMENU_ITEM Item,
168  _In_ ULONG Flags,
169  _In_opt_ PWSTR Text,
170  _In_opt_ ULONG Id
171  )
172 {
173  return PhFindEMenuItemEx(Item, Flags, Text, Id, NULL, NULL);
174 }
175 
200  _In_ PPH_EMENU_ITEM Item,
201  _In_ ULONG Flags,
202  _In_opt_ PWSTR Text,
203  _In_opt_ ULONG Id,
204  _Out_opt_ PPH_EMENU_ITEM *FoundParent,
205  _Out_opt_ PULONG FoundIndex
206  )
207 {
208  PH_STRINGREF searchText;
209  ULONG i;
210  PPH_EMENU_ITEM item;
211 
212  if (!Item->Items)
213  return NULL;
214 
215  if (Text && (Flags & PH_EMENU_FIND_LITERAL))
216  PhInitializeStringRef(&searchText, Text);
217 
218  for (i = 0; i < Item->Items->Count; i++)
219  {
220  item = Item->Items->Items[i];
221 
222  if (Text)
223  {
224  if (Flags & PH_EMENU_FIND_LITERAL)
225  {
226  PH_STRINGREF text;
227 
228  PhInitializeStringRef(&text, item->Text);
229 
230  if (Flags & PH_EMENU_FIND_STARTSWITH)
231  {
232  if (PhStartsWithStringRef(&text, &searchText, TRUE))
233  goto FoundItemHere;
234  }
235  else
236  {
237  if (PhEqualStringRef(&text, &searchText, TRUE))
238  goto FoundItemHere;
239  }
240  }
241  else
242  {
244  TRUE, !!(Flags & PH_EMENU_FIND_STARTSWITH)) == 0)
245  goto FoundItemHere;
246  }
247  }
248 
249  if (Id && item->Id == Id)
250  goto FoundItemHere;
251 
252  if (Flags & PH_EMENU_FIND_DESCEND)
253  {
254  PPH_EMENU_ITEM foundItem;
255  PPH_EMENU_ITEM foundParent;
256  ULONG foundIndex;
257 
258  foundItem = PhFindEMenuItemEx(item, Flags, Text, Id, &foundParent, &foundIndex);
259 
260  if (foundItem)
261  {
262  if (FoundParent)
263  *FoundParent = foundParent;
264  if (FoundIndex)
265  *FoundIndex = foundIndex;
266 
267  return foundItem;
268  }
269  }
270  }
271 
272  return NULL;
273 
274 FoundItemHere:
275  if (FoundParent)
276  *FoundParent = Item;
277  if (FoundIndex)
278  *FoundIndex = i;
279 
280  return item;
281 }
282 
293  _In_ PPH_EMENU_ITEM Parent,
294  _In_ PPH_EMENU_ITEM Item
295  )
296 {
297  if (!Parent->Items)
298  return -1;
299 
300  return PhFindItemList(Parent->Items, Item);
301 }
302 
313  _Inout_ PPH_EMENU_ITEM Parent,
314  _Inout_ PPH_EMENU_ITEM Item,
315  _In_ ULONG Index
316  )
317 {
318  // Remove the item from its old parent if it has one.
319  if (Item->Parent)
320  PhRemoveEMenuItem(Item->Parent, Item, 0);
321 
322  if (!Parent->Items)
323  Parent->Items = PhCreateList(16);
324 
325  if (Index > Parent->Items->Count)
326  Index = Parent->Items->Count;
327 
328  if (Index == -1)
329  PhAddItemList(Parent->Items, Item);
330  else
331  PhInsertItemList(Parent->Items, Index, Item);
332 
333  Item->Parent = Parent;
334 }
335 
347  _Inout_opt_ PPH_EMENU_ITEM Parent,
348  _In_opt_ PPH_EMENU_ITEM Item,
349  _In_opt_ ULONG Index
350  )
351 {
352  if (Item)
353  {
354  if (!Parent)
355  Parent = Item->Parent;
356  if (!Parent->Items)
357  return FALSE;
358 
359  Index = PhFindItemList(Parent->Items, Item);
360 
361  if (Index == -1)
362  return FALSE;
363  }
364  else
365  {
366  if (!Parent)
367  return FALSE;
368  if (!Parent->Items)
369  return FALSE;
370  }
371 
372  Item = Parent->Items->Items[Index];
373  PhRemoveItemList(Parent->Items, Index);
374  Item->Parent = NULL;
375 
376  return TRUE;
377 }
378 
385  _Inout_ PPH_EMENU_ITEM Parent
386  )
387 {
388  ULONG i;
389 
390  if (!Parent->Items)
391  return;
392 
393  for (i = 0; i < Parent->Items->Count; i++)
394  {
395  PhpDestroyEMenuItem(Parent->Items->Items[i]);
396  }
397 
398  PhClearList(Parent->Items);
399 }
400 
405  VOID
406  )
407 {
408  PPH_EMENU menu;
409 
410  menu = PhAllocate(sizeof(PH_EMENU));
411  memset(menu, 0, sizeof(PH_EMENU));
412  menu->Items = PhCreateList(16);
413 
414  return menu;
415 }
416 
423  _In_ PPH_EMENU Menu
424  )
425 {
426  ULONG i;
427 
428  for (i = 0; i < Menu->Items->Count; i++)
429  {
430  PhpDestroyEMenuItem(Menu->Items->Items[i]);
431  }
432 
433  PhDereferenceObject(Menu->Items);
434  PhFree(Menu);
435 }
436 
442  _Out_ PPH_EMENU_DATA Data
443  )
444 {
445  Data->IdToItem = PhCreateList(16);
446 }
447 
453  _Inout_ PPH_EMENU_DATA Data
454  )
455 {
456  PhDereferenceObject(Data->IdToItem);
457 }
458 
475  _In_ PPH_EMENU_ITEM Menu,
476  _In_ ULONG Flags,
477  _Inout_opt_ PPH_EMENU_DATA Data
478  )
479 {
480  HMENU menuHandle;
481 
482  menuHandle = CreatePopupMenu();
483 
484  if (!menuHandle)
485  return NULL;
486 
487  PhEMenuToHMenu2(menuHandle, Menu, Flags, Data);
488 
489  if (!(Menu->Flags & PH_EMENU_SEPARATECHECKSPACE))
490  {
491  MENUINFO menuInfo;
492 
493  memset(&menuInfo, 0, sizeof(MENUINFO));
494  menuInfo.cbSize = sizeof(MENUINFO);
495  menuInfo.fMask = MIM_STYLE;
496  menuInfo.dwStyle = MNS_CHECKORBMP;
497  SetMenuInfo(menuHandle, &menuInfo);
498  }
499 
500  return menuHandle;
501 }
502 
518  _In_ HMENU MenuHandle,
519  _In_ PPH_EMENU_ITEM Menu,
520  _In_ ULONG Flags,
521  _Inout_opt_ PPH_EMENU_DATA Data
522  )
523 {
524  ULONG i;
525  PPH_EMENU_ITEM item;
526  MENUITEMINFO menuItemInfo;
527 
528  for (i = 0; i < Menu->Items->Count; i++)
529  {
530  item = Menu->Items->Items[i];
531 
532  memset(&menuItemInfo, 0, sizeof(MENUITEMINFO));
533  menuItemInfo.cbSize = sizeof(MENUITEMINFO);
534 
535  // Type
536 
537  menuItemInfo.fMask = MIIM_FTYPE | MIIM_STATE;
538 
539  if (item->Flags & PH_EMENU_SEPARATOR)
540  {
541  menuItemInfo.fType = MFT_SEPARATOR;
542  }
543  else
544  {
545  menuItemInfo.fType = MFT_STRING;
546  menuItemInfo.fMask |= MIIM_STRING;
547  menuItemInfo.dwTypeData = item->Text;
548  }
549 
550  PhMapFlags1(
551  &menuItemInfo.fType,
552  item->Flags,
553  EMenuTypeMappings,
554  sizeof(EMenuTypeMappings) / sizeof(PH_FLAG_MAPPING)
555  );
556 
557  // Bitmap
558 
559  if (item->Bitmap)
560  {
561  menuItemInfo.fMask |= MIIM_BITMAP;
562  menuItemInfo.hbmpItem = item->Bitmap;
563  }
564 
565  // Id
566 
567  if (Flags & PH_EMENU_CONVERT_ID)
568  {
569  ULONG id;
570 
571  if (Data)
572  {
573  PhAddItemList(Data->IdToItem, item);
574  id = Data->IdToItem->Count;
575 
576  menuItemInfo.fMask |= MIIM_ID;
577  menuItemInfo.wID = id;
578  }
579  }
580  else
581  {
582  if (item->Id)
583  {
584  menuItemInfo.fMask |= MIIM_ID;
585  menuItemInfo.wID = item->Id;
586  }
587  }
588 
589  // State
590 
591  PhMapFlags1(
592  &menuItemInfo.fState,
593  item->Flags,
594  EMenuStateMappings,
595  sizeof(EMenuStateMappings) / sizeof(PH_FLAG_MAPPING)
596  );
597 
598  // Context
599 
600  menuItemInfo.fMask |= MIIM_DATA;
601  menuItemInfo.dwItemData = (ULONG_PTR)item;
602 
603  // Submenu
604 
605  if (item->Items && item->Items->Count != 0)
606  {
607  menuItemInfo.fMask |= MIIM_SUBMENU;
608  menuItemInfo.hSubMenu = PhEMenuToHMenu(item, Flags, Data);
609  }
610 
611  InsertMenuItem(MenuHandle, MAXINT, TRUE, &menuItemInfo);
612  }
613 }
614 
623  _Inout_ PPH_EMENU_ITEM MenuItem,
624  _In_ HMENU MenuHandle
625  )
626 {
627  ULONG i;
628  ULONG count;
629 
630  count = GetMenuItemCount(MenuHandle);
631 
632  if (count != -1)
633  {
634  for (i = 0; i < count; i++)
635  {
636  MENUITEMINFO menuItemInfo;
637  WCHAR buffer[256];
638  PPH_EMENU_ITEM menuItem;
639 
640  menuItemInfo.cbSize = sizeof(menuItemInfo);
641  menuItemInfo.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STATE | MIIM_STRING | MIIM_SUBMENU;
642  menuItemInfo.cch = sizeof(buffer) / sizeof(WCHAR);
643  menuItemInfo.dwTypeData = buffer;
644 
645  if (!GetMenuItemInfo(MenuHandle, i, TRUE, &menuItemInfo))
646  continue;
647 
648  menuItem = PhCreateEMenuItem(
650  menuItemInfo.wID,
651  PhDuplicateStringZ(buffer),
652  NULL,
653  NULL
654  );
655 
656  if (menuItemInfo.fType & MFT_SEPARATOR)
657  menuItem->Flags |= PH_EMENU_SEPARATOR;
658 
659  PhMapFlags2(
660  &menuItem->Flags,
661  menuItemInfo.fType,
662  EMenuTypeMappings,
663  sizeof(EMenuTypeMappings) / sizeof(PH_FLAG_MAPPING)
664  );
665  PhMapFlags2(
666  &menuItem->Flags,
667  menuItemInfo.fState,
668  EMenuStateMappings,
669  sizeof(EMenuStateMappings) / sizeof(PH_FLAG_MAPPING)
670  );
671 
672  if (menuItemInfo.hSubMenu)
673  PhHMenuToEMenuItem(menuItem, menuItemInfo.hSubMenu);
674 
675  PhInsertEMenuItem(MenuItem, menuItem, -1);
676  }
677  }
678 }
679 
691  _Inout_ PPH_EMENU_ITEM MenuItem,
692  _In_ HINSTANCE InstanceHandle,
693  _In_ PWSTR Resource,
694  _In_ ULONG SubMenuIndex
695  )
696 {
697  HMENU menu;
698  HMENU realMenu;
699 
700  menu = LoadMenu(InstanceHandle, Resource);
701 
702  if (SubMenuIndex != -1)
703  realMenu = GetSubMenu(menu, SubMenuIndex);
704  else
705  realMenu = menu;
706 
707  PhHMenuToEMenuItem(MenuItem, realMenu);
708 
709  DestroyMenu(menu);
710 }
711 
729  _In_ PPH_EMENU Menu,
730  _In_ HWND WindowHandle,
731  _In_ ULONG Flags,
732  _In_ ULONG Align,
733  _In_ ULONG X,
734  _In_ ULONG Y
735  )
736 {
737  PPH_EMENU_ITEM selectedItem;
738  ULONG result;
739  ULONG flags;
740  PH_EMENU_DATA data;
741  HMENU popupMenu;
742 
743  selectedItem = NULL;
744  flags = TPM_RETURNCMD | TPM_NONOTIFY;
745 
746  // Flags
747 
748  if (Flags & PH_EMENU_SHOW_LEFTRIGHT)
749  flags |= TPM_RIGHTBUTTON;
750  else
751  flags |= TPM_LEFTBUTTON;
752 
753  // Align
754 
755  if (Align & PH_ALIGN_LEFT)
756  flags |= TPM_LEFTALIGN;
757  else if (Align & PH_ALIGN_RIGHT)
758  flags |= TPM_RIGHTALIGN;
759  else
760  flags |= TPM_CENTERALIGN;
761 
762  if (Align & PH_ALIGN_TOP)
763  flags |= TPM_TOPALIGN;
764  else if (Align & PH_ALIGN_BOTTOM)
765  flags |= TPM_BOTTOMALIGN;
766  else
767  flags |= TPM_VCENTERALIGN;
768 
769  PhInitializeEMenuData(&data);
770 
771  if (popupMenu = PhEMenuToHMenu(Menu, PH_EMENU_CONVERT_ID, &data))
772  {
773  result = TrackPopupMenu(
774  popupMenu,
775  flags,
776  X,
777  Y,
778  0,
779  WindowHandle,
780  NULL
781  );
782 
783  if (result != 0)
784  {
785  selectedItem = data.IdToItem->Items[result - 1];
786  }
787 
788  DestroyMenu(popupMenu);
789  }
790 
791  PhDeleteEMenuData(&data);
792 
793  if ((Flags & PH_EMENU_SHOW_SEND_COMMAND) && selectedItem && selectedItem->Id != 0)
794  SendMessage(WindowHandle, WM_COMMAND, MAKEWPARAM(selectedItem->Id, 0), 0);
795 
796  return selectedItem;
797 }
798 
808  _Inout_ PPH_EMENU_ITEM Item,
809  _In_ ULONG Id,
810  _In_ ULONG Mask,
811  _In_ ULONG Value
812  )
813 {
814  PPH_EMENU_ITEM item;
815 
816  item = PhFindEMenuItem(Item, PH_EMENU_FIND_DESCEND, NULL, Id);
817 
818  if (item)
819  {
820  item->Flags &= ~Mask;
821  item->Flags |= Value;
822 
823  return TRUE;
824  }
825  else
826  {
827  return FALSE;
828  }
829 }
830 
839  _In_ PPH_EMENU_ITEM Item,
840  _In_ ULONG Mask,
841  _In_ ULONG Value
842  )
843 {
844  ULONG i;
845 
846  for (i = 0; i < Item->Items->Count; i++)
847  {
848  PPH_EMENU_ITEM item = Item->Items->Items[i];
849 
850  item->Flags &= ~Mask;
851  item->Flags |= Value;
852  }
853 }
854 
856  _Inout_ PPH_EMENU_ITEM Item,
857  _In_ ULONG ModifyFlags,
858  _In_ ULONG OwnedFlags,
859  _In_opt_ PWSTR Text,
860  _In_opt_ HBITMAP Bitmap
861  )
862 {
863  if (ModifyFlags & PH_EMENU_MODIFY_TEXT)
864  {
865  if ((Item->Flags & PH_EMENU_TEXT_OWNED) && Item->Text)
866  PhFree(Item->Text);
867 
868  Item->Text = Text;
869  Item->Flags &= ~PH_EMENU_TEXT_OWNED;
870  Item->Flags |= OwnedFlags & PH_EMENU_TEXT_OWNED;
871  }
872 
873  if (ModifyFlags & PH_EMENU_MODIFY_BITMAP)
874  {
875  if ((Item->Flags & PH_EMENU_BITMAP_OWNED) && Item->Bitmap)
876  DeleteObject(Item->Bitmap);
877 
878  Item->Bitmap = Bitmap;
879  Item->Flags &= ~PH_EMENU_BITMAP_OWNED;
880  Item->Flags |= OwnedFlags & PH_EMENU_BITMAP_OWNED;
881  }
882 }