Process Hacker
plugman.c
Go to the documentation of this file.
1 /*
2  * Process Hacker -
3  * plugins
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 <phapp.h>
24 #include <settings.h>
25 #include <phplug.h>
26 
27 #define IS_PLUGIN_LOADED(Plugin) (!!(Plugin)->AppContext.AppName.Buffer)
28 #define STR_OR_DEFAULT(String, Default) ((String) ? (String) : (Default))
29 
30 static HWND PluginsLv;
31 static PPH_PLUGIN SelectedPlugin;
32 static PPH_LIST DisabledPluginInstances; // fake PH_PLUGIN structures for disabled plugins
33 static PPH_HASHTABLE DisabledPluginLookup; // list of all disabled plugins (including fake structures) by PH_PLUGIN address
34 
35 INT_PTR CALLBACK PhpPluginsDlgProc(
36  _In_ HWND hwndDlg,
37  _In_ UINT uMsg,
38  _In_ WPARAM wParam,
39  _In_ LPARAM lParam
40  );
41 
43  _In_ HWND ParentWindowHandle
44  )
45 {
46  if (PhPluginsEnabled)
47  {
48  DialogBox(
50  MAKEINTRESOURCE(IDD_PLUGINS),
51  ParentWindowHandle,
53  );
54  }
55  else
56  {
57  PhShowInformation(ParentWindowHandle,
58  L"Plugins are not enabled. To use plugins enable them in Options and restart Process Hacker.");
59  }
60 }
61 
63  _In_ PPH_PLUGIN Plugin
64  )
65 {
66  if (Plugin->FileName)
67  {
68  PH_STRINGREF pathNamePart;
69  PH_STRINGREF baseNamePart;
70 
71  if (PhSplitStringRefAtLastChar(&Plugin->FileName->sr, '\\', &pathNamePart, &baseNamePart))
72  return baseNamePart.Buffer;
73  else
74  return Plugin->FileName->Buffer;
75  }
76  else
77  {
78  // Fake disabled plugin.
79  return Plugin->Name.Buffer;
80  }
81 }
82 
84  _In_ PWSTR BaseName
85  )
86 {
87  PH_STRINGREF baseName;
88 
89  PhInitializeStringRefLongHint(&baseName, BaseName);
90 
91  if (PhIsPluginDisabled(&baseName))
92  return L"Enable";
93  else
94  return L"Disable";
95 }
96 
98  _In_ HWND hwndDlg
99  )
100 {
101  PPH_STRING fileName;
102  PH_IMAGE_VERSION_INFO versionInfo;
103 
104  if (SelectedPlugin && SelectedPlugin->FileName) // if there's no FileName, then it's a fake disabled plugin instance
105  {
106  fileName = SelectedPlugin->FileName;
107 
108  SetDlgItemText(hwndDlg, IDC_NAME, SelectedPlugin->Information.DisplayName ? SelectedPlugin->Information.DisplayName : L"(unnamed)");
109  SetDlgItemText(hwndDlg, IDC_INTERNALNAME, SelectedPlugin->Name.Buffer);
110  SetDlgItemText(hwndDlg, IDC_AUTHOR, SelectedPlugin->Information.Author);
111  SetDlgItemText(hwndDlg, IDC_FILENAME, fileName->Buffer);
112  SetDlgItemText(hwndDlg, IDC_DESCRIPTION, SelectedPlugin->Information.Description);
113  SetDlgItemText(hwndDlg, IDC_URL, SelectedPlugin->Information.Url);
114 
115  if (PhInitializeImageVersionInfo(&versionInfo, fileName->Buffer))
116  {
117  SetDlgItemText(hwndDlg, IDC_VERSION, PhGetStringOrDefault(versionInfo.FileVersion, L"Unknown"));
118  PhDeleteImageVersionInfo(&versionInfo);
119  }
120  else
121  {
122  SetDlgItemText(hwndDlg, IDC_VERSION, L"Unknown");
123  }
124 
125  ShowWindow(GetDlgItem(hwndDlg, IDC_OPENURL), SelectedPlugin->Information.Url ? SW_SHOW : SW_HIDE);
126  EnableWindow(GetDlgItem(hwndDlg, IDC_DISABLE), TRUE);
127  SetDlgItemText(hwndDlg, IDC_DISABLE, PhpGetPluginDisableButtonText(PhpGetPluginBaseName(SelectedPlugin)));
128  EnableWindow(GetDlgItem(hwndDlg, IDC_OPTIONS), SelectedPlugin->Information.HasOptions);
129  }
130  else
131  {
132  SetDlgItemText(hwndDlg, IDC_NAME, L"N/A");
133  SetDlgItemText(hwndDlg, IDC_VERSION, L"N/A");
134  SetDlgItemText(hwndDlg, IDC_INTERNALNAME, L"N/A");
135  SetDlgItemText(hwndDlg, IDC_AUTHOR, L"N/A");
136  SetDlgItemText(hwndDlg, IDC_URL, L"N/A");
137  SetDlgItemText(hwndDlg, IDC_FILENAME, L"N/A");
138  SetDlgItemText(hwndDlg, IDC_DESCRIPTION, L"N/A");
139 
140  ShowWindow(GetDlgItem(hwndDlg, IDC_OPENURL), SW_HIDE);
141 
142  if (SelectedPlugin)
143  {
144  // This is a disabled plugin.
145  EnableWindow(GetDlgItem(hwndDlg, IDC_DISABLE), TRUE);
146  SetDlgItemText(hwndDlg, IDC_DISABLE, PhpGetPluginDisableButtonText(SelectedPlugin->Name.Buffer));
147  }
148  else
149  {
150  EnableWindow(GetDlgItem(hwndDlg, IDC_DISABLE), FALSE);
151  SetDlgItemText(hwndDlg, IDC_DISABLE, L"Disable");
152  }
153 
154  EnableWindow(GetDlgItem(hwndDlg, IDC_OPTIONS), FALSE);
155  }
156 }
157 
159  _In_ PPH_STRINGREF BaseName
160  )
161 {
162  PPH_AVL_LINKS links;
163 
164  // Extremely inefficient code follows.
165  // TODO: Make this better.
166 
168 
169  while (links)
170  {
171  PPH_PLUGIN plugin = CONTAINING_RECORD(links, PH_PLUGIN, Links);
172  PH_STRINGREF pluginBaseName;
173 
174  PhInitializeStringRefLongHint(&pluginBaseName, PhpGetPluginBaseName(plugin));
175 
176  if (PhEqualStringRef(&pluginBaseName, BaseName, TRUE))
177  return TRUE;
178 
179  links = PhSuccessorElementAvlTree(links);
180  }
181 
182  return FALSE;
183 }
184 
186  _In_ PPH_STRINGREF BaseName
187  )
188 {
189  PPH_PLUGIN plugin;
190 
191  plugin = PhAllocate(sizeof(PH_PLUGIN));
192  memset(plugin, 0, sizeof(PH_PLUGIN));
193 
194  plugin->Name.Length = BaseName->Length;
195  plugin->Name.Buffer = PhAllocate(BaseName->Length + sizeof(WCHAR));
196  memcpy(plugin->Name.Buffer, BaseName->Buffer, BaseName->Length);
197  plugin->Name.Buffer[BaseName->Length / 2] = 0;
198 
199  return plugin;
200 }
201 
203  _In_ PPH_PLUGIN Plugin
204  )
205 {
206  PhFree(Plugin->Name.Buffer);
207  PhFree(Plugin);
208 }
209 
211  VOID
212  )
213 {
214  PPH_STRING disabled;
215  PH_STRINGREF remainingPart;
216  PH_STRINGREF part;
217  PPH_PLUGIN disabledPlugin;
218  PPH_STRING displayText;
219  INT lvItemIndex;
220 
221  disabled = PhGetStringSetting(L"DisabledPlugins");
222  remainingPart = disabled->sr;
223 
224  while (remainingPart.Length != 0)
225  {
226  PhSplitStringRefAtChar(&remainingPart, '|', &part, &remainingPart);
227 
228  if (part.Length != 0)
229  {
230  if (!PhpIsPluginLoadedByBaseName(&part))
231  {
232  disabledPlugin = PhpCreateDisabledPlugin(&part);
233  PhAddItemList(DisabledPluginInstances, disabledPlugin);
234  PhAddItemSimpleHashtable(DisabledPluginLookup, disabledPlugin, NULL);
235 
236  displayText = PhCreateString2(&part);
237  lvItemIndex = PhAddListViewItem(PluginsLv, MAXINT, displayText->Buffer, disabledPlugin);
238  PhDereferenceObject(displayText);
239  }
240  }
241  }
242 
243  PhDereferenceObject(disabled);
244 }
245 
247  _In_ HWND hwndDlg,
248  _In_ INT ItemIndex,
249  _In_ PPH_PLUGIN Plugin,
250  _In_ BOOLEAN NewDisabledState
251  )
252 {
253  if (NewDisabledState)
254  {
255  PhAddItemSimpleHashtable(DisabledPluginLookup, Plugin, NULL);
256  }
257  else
258  {
259  PhRemoveItemSimpleHashtable(DisabledPluginLookup, Plugin);
260  }
261 
262  if (!IS_PLUGIN_LOADED(Plugin))
263  {
264  assert(!NewDisabledState);
265  ListView_DeleteItem(PluginsLv, ItemIndex);
266  }
267 
268  InvalidateRect(PluginsLv, NULL, TRUE);
269 
270  ShowWindow(GetDlgItem(hwndDlg, IDC_INSTRUCTION), SW_SHOW);
271 }
272 
273 static COLORREF PhpPluginColorFunction(
274  _In_ INT Index,
275  _In_ PVOID Param,
276  _In_opt_ PVOID Context
277  )
278 {
279  PPH_PLUGIN plugin = Param;
280 
281  if (PhFindItemSimpleHashtable(DisabledPluginLookup, plugin))
282  return RGB(0x77, 0x77, 0x77); // fake disabled plugin
283 
284  return GetSysColor(COLOR_WINDOW);
285 }
286 
287 INT_PTR CALLBACK PhpPluginsDlgProc(
288  _In_ HWND hwndDlg,
289  _In_ UINT uMsg,
290  _In_ WPARAM wParam,
291  _In_ LPARAM lParam
292  )
293 {
294  switch (uMsg)
295  {
296  case WM_INITDIALOG:
297  {
298  PPH_AVL_LINKS links;
299 
300  PluginsLv = GetDlgItem(hwndDlg, IDC_LIST);
301  PhSetListViewStyle(PluginsLv, FALSE, TRUE);
302  PhSetControlTheme(PluginsLv, L"explorer");
303  PhAddListViewColumn(PluginsLv, 0, 0, 0, LVCFMT_LEFT, 100, L"File");
304  PhAddListViewColumn(PluginsLv, 1, 1, 1, LVCFMT_LEFT, 150, L"Name");
305  PhAddListViewColumn(PluginsLv, 2, 2, 2, LVCFMT_LEFT, 100, L"Author");
306  PhSetExtendedListView(PluginsLv);
307  ExtendedListView_SetItemColorFunction(PluginsLv, PhpPluginColorFunction);
308 
309  DisabledPluginLookup = PhCreateSimpleHashtable(10);
310 
312 
313  while (links)
314  {
315  PPH_PLUGIN plugin = CONTAINING_RECORD(links, PH_PLUGIN, Links);
316  INT lvItemIndex;
317  PH_STRINGREF baseNameSr;
318 
319  lvItemIndex = PhAddListViewItem(PluginsLv, MAXINT, PhpGetPluginBaseName(plugin), plugin);
320 
321  PhSetListViewSubItem(PluginsLv, lvItemIndex, 1, plugin->Information.DisplayName ? plugin->Information.DisplayName : plugin->Name.Buffer);
322 
323  if (plugin->Information.Author)
324  PhSetListViewSubItem(PluginsLv, lvItemIndex, 2, plugin->Information.Author);
325 
327 
328  if (PhIsPluginDisabled(&baseNameSr))
329  PhAddItemSimpleHashtable(DisabledPluginLookup, plugin, NULL);
330 
331  links = PhSuccessorElementAvlTree(links);
332  }
333 
334  DisabledPluginInstances = PhCreateList(10);
336 
337  ExtendedListView_SortItems(PluginsLv);
338 
339  SelectedPlugin = NULL;
340  PhpRefreshPluginDetails(hwndDlg);
341  }
342  break;
343  case WM_DESTROY:
344  {
345  ULONG i;
346 
347  for (i = 0; i < DisabledPluginInstances->Count; i++)
348  PhpFreeDisabledPlugin(DisabledPluginInstances->Items[i]);
349 
350  PhDereferenceObject(DisabledPluginInstances);
351  PhDereferenceObject(DisabledPluginLookup);
352  }
353  break;
354  case WM_COMMAND:
355  {
356  switch (LOWORD(wParam))
357  {
358  case IDCANCEL:
359  case IDOK:
360  EndDialog(hwndDlg, IDOK);
361  break;
362  case IDC_DISABLE:
363  {
364  if (SelectedPlugin)
365  {
366  PWSTR baseName;
367  PH_STRINGREF baseNameRef;
368  BOOLEAN newDisabledState;
369 
370  baseName = PhpGetPluginBaseName(SelectedPlugin);
371  PhInitializeStringRef(&baseNameRef, baseName);
372  newDisabledState = !PhIsPluginDisabled(&baseNameRef);
373  PhSetPluginDisabled(&baseNameRef, newDisabledState);
374  PhpUpdateDisabledPlugin(hwndDlg, PhFindListViewItemByFlags(PluginsLv, -1, LVNI_SELECTED), SelectedPlugin, newDisabledState);
375 
376  SetDlgItemText(hwndDlg, IDC_DISABLE, PhpGetPluginDisableButtonText(baseName));
377  }
378  }
379  break;
380  case IDC_OPTIONS:
381  {
382  if (SelectedPlugin && IS_PLUGIN_LOADED(SelectedPlugin))
383  {
385  }
386  }
387  break;
388  case IDC_CLEANUP:
389  {
390  if (PhShowMessage(hwndDlg, MB_ICONQUESTION | MB_YESNO,
391  L"Do you want to clean up unused plugin settings?") == IDYES)
392  {
394  }
395  }
396  break;
397  case IDC_OPENURL:
398  {
399  NOTHING;
400  }
401  break;
402  }
403  }
404  break;
405  case WM_NOTIFY:
406  {
407  LPNMHDR header = (LPNMHDR)lParam;
408 
409  switch (header->code)
410  {
411  case LVN_ITEMCHANGED:
412  {
413  if (header->hwndFrom == PluginsLv)
414  {
415  if (ListView_GetSelectedCount(PluginsLv) == 1)
416  SelectedPlugin = PhGetSelectedListViewItemParam(PluginsLv);
417  else
418  SelectedPlugin = NULL;
419 
420  PhpRefreshPluginDetails(hwndDlg);
421  }
422  }
423  break;
424  case NM_CLICK:
425  {
426  if (header->hwndFrom == GetDlgItem(hwndDlg, IDC_OPENURL))
427  {
428  if (SelectedPlugin && IS_PLUGIN_LOADED(SelectedPlugin))
429  PhShellExecute(hwndDlg, SelectedPlugin->Information.Url, NULL);
430  }
431  }
432  break;
433  case NM_DBLCLK:
434  {
435  if (header->hwndFrom == PluginsLv)
436  {
437  if (SelectedPlugin && IS_PLUGIN_LOADED(SelectedPlugin))
438  {
440  }
441  }
442  }
443  break;
444  }
445  }
446  break;
447  }
448 
449  REFLECT_MESSAGE_DLG(hwndDlg, PluginsLv, uMsg, wParam, lParam);
450 
451  return FALSE;
452 }