Process Hacker
searchbox.c
Go to the documentation of this file.
1 /*
2  * Process Hacker -
3  * Subclassed Edit control
4  *
5  * Copyright (C) 2012-2015 dmex
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 
24 #include "toolstatus.h"
25 
26 static VOID NcAreaFreeTheme(
27  _Inout_ PEDIT_CONTEXT Context
28  )
29 {
30  if (Context->BrushNormal)
31  DeleteObject(Context->BrushNormal);
32 
33  if (Context->BrushHot)
34  DeleteObject(Context->BrushHot);
35 
36  if (Context->BrushFocused)
37  DeleteObject(Context->BrushFocused);
38 
39  if (Context->BrushFill)
40  DeleteObject(Context->BrushFill);
41 
42  if (Context->WindowFont)
43  DeleteObject(Context->WindowFont);
44 }
45 
46 static VOID NcAreaInitializeFont(
47  _Inout_ PEDIT_CONTEXT Context
48  )
49 {
50  NONCLIENTMETRICS metrics = { sizeof(NONCLIENTMETRICS) };
51 
52  // Cleanup existing Font handle.
53  if (Context->WindowFont)
54  DeleteObject(Context->WindowFont);
55 
56  if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &metrics, 0))
57  {
58  metrics.lfMessageFont.lfHeight = -11;
59 
60  Context->WindowFont = CreateFontIndirect(&metrics.lfMessageFont);
61  }
62  else
63  {
64  LOGFONT font;
65 
66  GetObject((HFONT)GetStockObject(DEFAULT_GUI_FONT), sizeof(LOGFONT), &font);
67 
68  font.lfHeight = -11;
69 
70  Context->WindowFont = CreateFontIndirect(&font);
71  }
72 
73  SendMessage(Context->WindowHandle, WM_SETFONT, (WPARAM)Context->WindowFont, TRUE);
74 }
75 
76 static VOID NcAreaInitializeTheme(
77  _Inout_ PEDIT_CONTEXT Context
78  )
79 {
80  Context->CXBorder = GetSystemMetrics(SM_CXBORDER) * 2;
81  //Context->CYBorder = GetSystemMetrics(SM_CYBORDER) * 2;
82 
83  Context->BackgroundColorRef = GetSysColor(COLOR_WINDOW);
84  Context->BrushFill = GetSysColorBrush(COLOR_WINDOW);
85  Context->BrushNormal = GetStockBrush(BLACK_BRUSH);
86  Context->BrushHot = WindowsVersion < WINDOWS_VISTA ? CreateSolidBrush(RGB(50, 150, 255)) : GetSysColorBrush(COLOR_HIGHLIGHT);
87  Context->BrushFocused = WindowsVersion < WINDOWS_VISTA ? CreateSolidBrush(RGB(50, 150, 255)) : GetSysColorBrush(COLOR_HIGHLIGHT);
88 }
89 
90 static VOID NcAreaInitializeImageList(
91  _Inout_ PEDIT_CONTEXT Context
92  )
93 {
94  HBITMAP bitmapActive = NULL;
95  HBITMAP bitmapInactive = NULL;
96 
97  Context->ImageWidth = 23;
98  Context->ImageHeight = 20;
99  Context->ImageList = ImageList_Create(32, 32, ILC_COLOR32 | ILC_MASK, 0, 0);
100 
101  ImageList_SetBkColor(Context->ImageList, Context->BackgroundColorRef);
102  ImageList_SetImageCount(Context->ImageList, 2);
103 
104  // Add the images to the imagelist
105  if (bitmapActive = LoadImageFromResources(Context->ImageWidth, Context->ImageHeight, MAKEINTRESOURCE(IDB_SEARCH_ACTIVE)))
106  {
107  ImageList_Replace(Context->ImageList, 0, bitmapActive, NULL);
108  DeleteObject(bitmapActive);
109  }
110  else
111  {
112  PhSetImageListBitmap(Context->ImageList, 0, PluginInstance->DllBase, MAKEINTRESOURCE(IDB_SEARCH_ACTIVE_BMP));
113  }
114 
115  if (bitmapInactive = LoadImageFromResources(Context->ImageWidth, Context->ImageHeight, MAKEINTRESOURCE(IDB_SEARCH_INACTIVE)))
116  {
117  ImageList_Replace(Context->ImageList, 1, bitmapInactive, NULL);
118  DeleteObject(bitmapInactive);
119  }
120  else
121  {
122  PhSetImageListBitmap(Context->ImageList, 1, PluginInstance->DllBase, MAKEINTRESOURCE(IDB_SEARCH_INACTIVE_BMP));
123  }
124 }
125 
126 static VOID NcAreaGetButtonRect(
127  _Inout_ PEDIT_CONTEXT Context,
128  _Inout_ PRECT ButtonRect
129  )
130 {
131  ButtonRect->left = (ButtonRect->right - Context->cxImgSize) - Context->CXBorder;
132  ButtonRect->bottom -= 2;
133  ButtonRect->right -= 2;
134  ButtonRect->top += 2;
135 }
136 
137 static VOID NcAreaDrawButton(
138  _Inout_ PEDIT_CONTEXT Context,
139  _In_ RECT ButtonRect
140  )
141 {
142  HDC hdc;
143 
144  if (!(hdc = GetWindowDC(Context->WindowHandle)))
145  return;
146 
147  SetBkMode(hdc, TRANSPARENT);
148 
149  FillRect(hdc, &ButtonRect, Context->BrushFill);
150 
151 //#ifdef _DEBUG
152 // BOOL isFocused = (GetFocus() == Context->WindowHandle);
153 // if (isFocused)
154 // {
155 // FrameRect(hdc, &ButtonRect, Context->BrushFocused);
156 // }
157 // else if (Context->MouseInClient)
158 // {
159 // FrameRect(hdc, &ButtonRect, Context->BrushHot);
160 // }
161 // else
162 // {
163 // FrameRect(hdc, &ButtonRect, Context->BrushNormal);
164 // }
165 //#endif
166 
167  // Draw the image centered within the rect.
168  if (SearchboxText->Length > 0)
169  {
170  ImageList_DrawEx(
171  Context->ImageList,
172  0,
173  hdc,
174  ButtonRect.left,
175  ButtonRect.top + ((ButtonRect.bottom - ButtonRect.top) - Context->ImageHeight) / 2,
176  0,
177  0,
178  Context->BackgroundColorRef,
179  Context->BackgroundColorRef,
180  ILD_NORMAL | ILD_TRANSPARENT
181  );
182  }
183  else
184  {
185  ImageList_DrawEx(
186  Context->ImageList,
187  1,
188  hdc,
189  ButtonRect.left,
190  ButtonRect.top + ((ButtonRect.bottom - ButtonRect.top) - (Context->ImageHeight - 1)) / 2, // Fix image offset by 1
191  0,
192  0,
193  Context->BackgroundColorRef,
194  Context->BackgroundColorRef,
195  ILD_NORMAL | ILD_TRANSPARENT
196  );
197  }
198 
199  ReleaseDC(Context->WindowHandle, hdc);
200 }
201 
202 static LRESULT CALLBACK NcAreaWndSubclassProc(
203  _In_ HWND hwndDlg,
204  _In_ UINT uMsg,
205  _In_ WPARAM wParam,
206  _In_ LPARAM lParam,
207  _In_ UINT_PTR uIdSubclass,
208  _In_ ULONG_PTR dwRefData
209  )
210 {
211  PEDIT_CONTEXT context;
212 
213  context = (PEDIT_CONTEXT)GetProp(hwndDlg, L"EditSubclassContext");
214 
215  switch (uMsg)
216  {
217  case WM_NCDESTROY:
218  {
219  NcAreaFreeTheme(context);
220 
221  if (context->ImageList)
222  ImageList_Destroy(context->ImageList);
223 
224  RemoveWindowSubclass(hwndDlg, NcAreaWndSubclassProc, uIdSubclass);
225  RemoveProp(hwndDlg, L"EditSubclassContext");
226  PhFree(context);
227  }
228  break;
229  case WM_ERASEBKGND:
230  return 1;
231  case WM_NCCALCSIZE:
232  {
233  LPNCCALCSIZE_PARAMS ncCalcSize = (NCCALCSIZE_PARAMS*)lParam;
234 
235  // Let Windows handle the non-client defaults.
236  DefSubclassProc(hwndDlg, uMsg, wParam, lParam);
237 
238  // Deflate the client area to accommodate the custom button.
239  ncCalcSize->rgrc[0].right -= context->cxImgSize;
240  }
241  return 0;
242  case WM_NCPAINT:
243  {
244  RECT windowRect;
245 
246  // Let Windows handle the non-client defaults.
247  DefSubclassProc(hwndDlg, uMsg, wParam, lParam);
248 
249  // Get the screen coordinates of the window.
250  GetWindowRect(hwndDlg, &windowRect);
251 
252  // Adjust the coordinates (start from 0,0).
253  OffsetRect(&windowRect, -windowRect.left, -windowRect.top);
254 
255  // Get the position of the inserted button.
256  NcAreaGetButtonRect(context, &windowRect);
257 
258  // Draw the button.
259  NcAreaDrawButton(context, windowRect);
260  }
261  return 0;
262  case WM_NCHITTEST:
263  {
264  POINT windowPoint;
265  RECT windowRect;
266 
267  // Get the screen coordinates of the mouse.
268  windowPoint.x = GET_X_LPARAM(lParam);
269  windowPoint.y = GET_Y_LPARAM(lParam);
270 
271  // Get the position of the inserted button.
272  GetWindowRect(hwndDlg, &windowRect);
273  NcAreaGetButtonRect(context, &windowRect);
274 
275  // Check that the mouse is within the inserted button.
276  if (PtInRect(&windowRect, windowPoint))
277  {
278  return HTBORDER;
279  }
280  }
281  break;
282  case WM_NCLBUTTONDOWN:
283  {
284  POINT windowPoint;
285  RECT windowRect;
286 
287  // Get the screen coordinates of the mouse.
288  windowPoint.x = GET_X_LPARAM(lParam);
289  windowPoint.y = GET_Y_LPARAM(lParam);
290 
291  // Get the position of the inserted button.
292  GetWindowRect(hwndDlg, &windowRect);
293  NcAreaGetButtonRect(context, &windowRect);
294 
295  // Check that the mouse is within the inserted button.
296  if (PtInRect(&windowRect, windowPoint))
297  {
298  SetCapture(hwndDlg);
299 
300  RedrawWindow(hwndDlg, NULL, NULL, RDW_FRAME | RDW_INVALIDATE);
301  }
302  }
303  break;
304  case WM_LBUTTONUP:
305  {
306  POINT windowPoint;
307  RECT windowRect;
308 
309  // Get the screen coordinates of the mouse.
310  windowPoint.x = GET_X_LPARAM(lParam);
311  windowPoint.y = GET_Y_LPARAM(lParam);
312 
313  // Get the screen coordinates of the window.
314  GetWindowRect(hwndDlg, &windowRect);
315 
316  // Adjust the coordinates (start from 0,0).
317  OffsetRect(&windowRect, -windowRect.left, -windowRect.top);
318 
319  // Get the position of the inserted button.
320  NcAreaGetButtonRect(context, &windowRect);
321 
322  // Check that the mouse is within the inserted button.
323  if (PtInRect(&windowRect, windowPoint))
324  {
325  // Forward click notification.
326  SendMessage(PhMainWndHandle, WM_COMMAND, MAKEWPARAM(context->CommandID, BN_CLICKED), 0);
327  }
328 
329  if (GetCapture() == hwndDlg)
330  {
331  ReleaseCapture();
332  }
333 
334  RedrawWindow(hwndDlg, NULL, NULL, RDW_FRAME | RDW_INVALIDATE);
335  }
336  break;
337  case WM_KEYDOWN:
338  {
339  if (wParam == '\t' || wParam == '\r')
340  {
341  HWND tnHandle;
342 
343  tnHandle = GetCurrentTreeNewHandle();
344 
345  if (tnHandle)
346  {
347  SetFocus(tnHandle);
348 
349  if (wParam == '\r')
350  {
351  if (TreeNew_GetFlatNodeCount(tnHandle) > 0)
352  {
353  TreeNew_DeselectRange(tnHandle, 0, -1);
354  TreeNew_SelectRange(tnHandle, 0, 0);
355  TreeNew_SetFocusNode(tnHandle, TreeNew_GetFlatNode(tnHandle, 0));
356  TreeNew_SetMarkNode(tnHandle, TreeNew_GetFlatNode(tnHandle, 0));
357  }
358  }
359  }
360  else
361  {
362  PTOOLSTATUS_TAB_INFO tabInfo;
363 
364  if ((tabInfo = FindTabInfo(SelectedTabIndex)) && tabInfo->ActivateContent)
365  tabInfo->ActivateContent(wParam == '\r');
366  }
367 
368  return FALSE;
369  }
370 
371  // Handle CTRL+A below Vista.
372  if (WindowsVersion < WINDOWS_VISTA && (GetKeyState(VK_CONTROL) & VK_LCONTROL) && wParam == 'A')
373  {
374  Edit_SetSel(hwndDlg, 0, -1);
375  return FALSE;
376  }
377  }
378  break;
379  case WM_CHAR:
380  if (wParam == '\t' || wParam == '\r')
381  return FALSE;
382  break;
383  case WM_CUT:
384  case WM_CLEAR:
385  case WM_PASTE:
386  case WM_UNDO:
387  case WM_KEYUP:
388  case WM_SETTEXT:
389  //case WM_SETFOCUS:
390  case WM_KILLFOCUS:
391  RedrawWindow(hwndDlg, NULL, NULL, RDW_FRAME | RDW_INVALIDATE);
392  break;
393  case WM_SETTINGCHANGE:
394  case WM_SYSCOLORCHANGE:
395  case WM_THEMECHANGED:
396  {
397  NcAreaFreeTheme(context);
398  NcAreaInitializeTheme(context);
399  NcAreaInitializeFont(context);
400 
401  // Reset the client area margins.
402  SendMessage(hwndDlg, EM_SETMARGINS, EC_LEFTMARGIN, MAKELPARAM(0, 0));
403 
404  // Force the edit control to update its non-client area.
405  RedrawWindow(hwndDlg, NULL, NULL, RDW_FRAME | RDW_INVALIDATE);
406  //SetWindowPos(hwndDlg, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
407  }
408  break;
409  case WM_SETFOCUS:
410  {
412  {
414  }
415  }
416  break;
417  }
418 
419  return DefSubclassProc(hwndDlg, uMsg, wParam, lParam);
420 }
421 
423  _In_ UINT Width,
424  _In_ UINT Height,
425  _In_ PCWSTR Name
426  )
427 {
428  UINT width = 0;
429  UINT height = 0;
430  UINT frameCount = 0;
431  BOOLEAN isSuccess = FALSE;
432  ULONG resourceLength = 0;
433  HGLOBAL resourceHandle = NULL;
434  HRSRC resourceHandleSource = NULL;
435  WICInProcPointer resourceBuffer = NULL;
436 
437  BITMAPINFO bitmapInfo = { 0 };
438  HBITMAP bitmapHandle = NULL;
439  PBYTE bitmapBuffer = NULL;
440 
441  IWICStream* wicStream = NULL;
442  IWICBitmapSource* wicBitmapSource = NULL;
443  IWICBitmapDecoder* wicDecoder = NULL;
444  IWICBitmapFrameDecode* wicFrame = NULL;
445  IWICImagingFactory* wicFactory = NULL;
446  IWICBitmapScaler* wicScaler = NULL;
447  WICPixelFormatGUID pixelFormat;
448 
449  WICRect rect = { 0, 0, Width, Height };
450 
451  __try
452  {
453  // Create the ImagingFactory
454  if (FAILED(CoCreateInstance(&CLSID_WICImagingFactory1, NULL, CLSCTX_INPROC_SERVER, &IID_IWICImagingFactory, &wicFactory)))
455  __leave;
456 
457  // Find the resource
458  if ((resourceHandleSource = FindResource(PluginInstance->DllBase, Name, L"PNG")) == NULL)
459  __leave;
460 
461  // Get the resource length
462  resourceLength = SizeofResource(PluginInstance->DllBase, resourceHandleSource);
463 
464  // Load the resource
465  if ((resourceHandle = LoadResource(PluginInstance->DllBase, resourceHandleSource)) == NULL)
466  __leave;
467 
468  if ((resourceBuffer = (WICInProcPointer)LockResource(resourceHandle)) == NULL)
469  __leave;
470 
471  // Create the Stream
472  if (FAILED(IWICImagingFactory_CreateStream(wicFactory, &wicStream)))
473  __leave;
474 
475  // Initialize the Stream from Memory
476  if (FAILED(IWICStream_InitializeFromMemory(wicStream, resourceBuffer, resourceLength)))
477  __leave;
478 
479  if (FAILED(IWICImagingFactory_CreateDecoder(wicFactory, &GUID_ContainerFormatPng, NULL, &wicDecoder)))
480  __leave;
481 
482  if (FAILED(IWICBitmapDecoder_Initialize(wicDecoder, (IStream*)wicStream, WICDecodeMetadataCacheOnLoad)))
483  __leave;
484 
485  // Get the Frame count
486  if (FAILED(IWICBitmapDecoder_GetFrameCount(wicDecoder, &frameCount)) || frameCount < 1)
487  __leave;
488 
489  // Get the Frame
490  if (FAILED(IWICBitmapDecoder_GetFrame(wicDecoder, 0, &wicFrame)))
491  __leave;
492 
493  // Get the WicFrame image format
494  if (FAILED(IWICBitmapFrameDecode_GetPixelFormat(wicFrame, &pixelFormat)))
495  __leave;
496 
497  // Check if the image format is supported:
498  if (IsEqualGUID(&pixelFormat, &GUID_WICPixelFormat32bppPBGRA)) // GUID_WICPixelFormat32bppRGB
499  {
500  wicBitmapSource = (IWICBitmapSource*)wicFrame;
501  }
502  else
503  {
504  // Convert the image to the correct format:
505  if (FAILED(WICConvertBitmapSource(&GUID_WICPixelFormat32bppPBGRA, (IWICBitmapSource*)wicFrame, &wicBitmapSource)))
506  __leave;
507 
508  IWICBitmapFrameDecode_Release(wicFrame);
509  }
510 
511  bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
512  bitmapInfo.bmiHeader.biWidth = rect.Width;
513  bitmapInfo.bmiHeader.biHeight = -((LONG)rect.Height);
514  bitmapInfo.bmiHeader.biPlanes = 1;
515  bitmapInfo.bmiHeader.biBitCount = 32;
516  bitmapInfo.bmiHeader.biCompression = BI_RGB;
517 
518  HDC hdc = CreateCompatibleDC(NULL);
519  bitmapHandle = CreateDIBSection(hdc, &bitmapInfo, DIB_RGB_COLORS, (PVOID*)&bitmapBuffer, NULL, 0);
520  ReleaseDC(NULL, hdc);
521 
522  // Check if it's the same rect as the requested size.
523  //if (width != rect.Width || height != rect.Height)
524  if (FAILED(IWICImagingFactory_CreateBitmapScaler(wicFactory, &wicScaler)))
525  __leave;
526  if (FAILED(IWICBitmapScaler_Initialize(wicScaler, wicBitmapSource, rect.Width, rect.Height, WICBitmapInterpolationModeFant)))
527  __leave;
528  if (FAILED(IWICBitmapScaler_CopyPixels(wicScaler, &rect, rect.Width * 4, rect.Width * rect.Height * 4, bitmapBuffer)))
529  __leave;
530 
531  isSuccess = TRUE;
532  }
533  __finally
534  {
535  if (wicScaler)
536  {
537  IWICBitmapScaler_Release(wicScaler);
538  }
539 
540  if (wicBitmapSource)
541  {
542  IWICBitmapSource_Release(wicBitmapSource);
543  }
544 
545  if (wicStream)
546  {
547  IWICStream_Release(wicStream);
548  }
549 
550  if (wicDecoder)
551  {
552  IWICBitmapDecoder_Release(wicDecoder);
553  }
554 
555  if (wicFactory)
556  {
557  IWICImagingFactory_Release(wicFactory);
558  }
559 
560  if (resourceHandle)
561  {
562  FreeResource(resourceHandle);
563  }
564  }
565 
566  return bitmapHandle;
567 }
568 
570  _In_ UINT CommandID
571  )
572 {
573  PEDIT_CONTEXT context;
574 
575  context = (PEDIT_CONTEXT)PhAllocate(sizeof(EDIT_CONTEXT));
576  memset(context, 0, sizeof(EDIT_CONTEXT));
577 
578  context->cxImgSize = 22; // GetSystemMetrics(SM_CXVSCROLL);
579  context->CommandID = CommandID;
580 
581  // Create the SearchBox window.
582  context->WindowHandle = CreateWindowEx(
583  WS_EX_CLIENTEDGE,
584  WC_EDIT,
585  NULL,
586  WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | ES_LEFT | ES_AUTOHSCROLL | (SearchBoxDisplayMode != SearchBoxDisplayHideInactive ? WS_VISIBLE : 0),
587  CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
588  RebarHandle,
589  NULL,
590  NULL,
591  NULL
592  );
593 
594  NcAreaInitializeTheme(context);
595  NcAreaInitializeImageList(context);
596 
597  // Set initial text
598  Edit_SetCueBannerText(context->WindowHandle, L"Search Processes (Ctrl+K)");
599 
600  // Set our window context data.
601  SetProp(context->WindowHandle, L"EditSubclassContext", (HANDLE)context);
602 
603  // Subclass the Edit control window procedure.
604  SetWindowSubclass(context->WindowHandle, NcAreaWndSubclassProc, 0, (ULONG_PTR)context);
605 
606  // Initialize the theme parameters.
607  SendMessage(context->WindowHandle, WM_THEMECHANGED, 0, 0);
608 
609  return context->WindowHandle;
610 }