Process Hacker
logwnd.c
Go to the documentation of this file.
1 /*
2  * Process Hacker -
3  * log window
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 <windowsx.h>
25 
26 #define WM_PH_LOG_UPDATED (WM_APP + 300)
27 
28 INT_PTR CALLBACK PhpLogDlgProc(
29  _In_ HWND hwndDlg,
30  _In_ UINT uMsg,
31  _In_ WPARAM wParam,
32  _In_ LPARAM lParam
33  );
34 
35 HWND PhLogWindowHandle = NULL;
36 static PH_LAYOUT_MANAGER WindowLayoutManager;
37 static RECT MinimumSize;
38 static HWND ListViewHandle;
39 static ULONG ListViewCount;
40 static PH_CALLBACK_REGISTRATION LoggedRegistration;
41 
43  VOID
44  )
45 {
46  if (!PhLogWindowHandle)
47  {
48  PhLogWindowHandle = CreateDialog(
50  MAKEINTRESOURCE(IDD_LOG),
53  );
55  ShowWindow(PhLogWindowHandle, SW_SHOW);
56  }
57 
58  if (IsIconic(PhLogWindowHandle))
59  ShowWindow(PhLogWindowHandle, SW_RESTORE);
60  else
61  SetForegroundWindow(PhLogWindowHandle);
62 }
63 
64 static VOID NTAPI LoggedCallback(
65  _In_opt_ PVOID Parameter,
66  _In_opt_ PVOID Context
67  )
68 {
69  PostMessage(PhLogWindowHandle, WM_PH_LOG_UPDATED, 0, 0);
70 }
71 
72 static VOID PhpUpdateLogList(
73  VOID
74  )
75 {
76  ListViewCount = PhLogBuffer.Count;
77  ListView_SetItemCountEx(ListViewHandle, ListViewCount, LVSICF_NOSCROLL);
78 
79  if (ListViewCount >= 2 && Button_GetCheck(GetDlgItem(PhLogWindowHandle, IDC_AUTOSCROLL)) == BST_CHECKED)
80  {
81  if (ListView_IsItemVisible(ListViewHandle, ListViewCount - 2))
82  {
83  ListView_EnsureVisible(ListViewHandle, ListViewCount - 1, FALSE);
84  }
85  }
86 }
87 
88 static PPH_STRING PhpGetStringForSelectedLogEntries(
89  _In_ BOOLEAN All
90  )
91 {
92  PH_STRING_BUILDER stringBuilder;
93  ULONG i;
94 
95  if (ListViewCount == 0)
96  return PhReferenceEmptyString();
97 
98  PhInitializeStringBuilder(&stringBuilder, 0x100);
99 
100  i = ListViewCount - 1;
101 
102  while (TRUE)
103  {
104  PPH_LOG_ENTRY entry;
105  SYSTEMTIME systemTime;
106  PPH_STRING temp;
107 
108  if (!All)
109  {
110  // The list view displays the items in reverse order...
111  if (!(ListView_GetItemState(ListViewHandle, ListViewCount - i - 1, LVIS_SELECTED) & LVIS_SELECTED))
112  {
113  goto ContinueLoop;
114  }
115  }
116 
117  entry = PhGetItemCircularBuffer_PVOID(&PhLogBuffer, i);
118 
119  if (!entry)
120  goto ContinueLoop;
121 
122  PhLargeIntegerToLocalSystemTime(&systemTime, &entry->Time);
123  temp = PhFormatDateTime(&systemTime);
124  PhAppendStringBuilder(&stringBuilder, &temp->sr);
125  PhDereferenceObject(temp);
126  PhAppendStringBuilder2(&stringBuilder, L": ");
127 
128  temp = PhFormatLogEntry(entry);
129  PhAppendStringBuilder(&stringBuilder, &temp->sr);
130  PhDereferenceObject(temp);
131  PhAppendStringBuilder2(&stringBuilder, L"\r\n");
132 
133 ContinueLoop:
134 
135  if (i == 0)
136  break;
137 
138  i--;
139  }
140 
141  return PhFinalStringBuilderString(&stringBuilder);
142 }
143 
144 INT_PTR CALLBACK PhpLogDlgProc(
145  _In_ HWND hwndDlg,
146  _In_ UINT uMsg,
147  _In_ WPARAM wParam,
148  _In_ LPARAM lParam
149  )
150 {
151  switch (uMsg)
152  {
153  case WM_INITDIALOG:
154  {
155  ListViewHandle = GetDlgItem(hwndDlg, IDC_LIST);
156  PhSetListViewStyle(ListViewHandle, FALSE, TRUE);
157  PhSetControlTheme(ListViewHandle, L"explorer");
158  PhAddListViewColumn(ListViewHandle, 0, 0, 0, LVCFMT_LEFT, 140, L"Time");
159  PhAddListViewColumn(ListViewHandle, 1, 1, 1, LVCFMT_LEFT, 260, L"Message");
160  PhLoadListViewColumnsFromSetting(L"LogListViewColumns", ListViewHandle);
161 
162  PhInitializeLayoutManager(&WindowLayoutManager, hwndDlg);
163  PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_LIST), NULL,
164  PH_ANCHOR_ALL);
165  PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDOK), NULL,
167  PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_COPY), NULL,
169  PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_SAVE), NULL,
171  PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_AUTOSCROLL), NULL,
173  PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_CLEAR), NULL,
175 
176  MinimumSize.left = 0;
177  MinimumSize.top = 0;
178  MinimumSize.right = 290;
179  MinimumSize.bottom = 150;
180  MapDialogRect(hwndDlg, &MinimumSize);
181 
182  PhLoadWindowPlacementFromSetting(L"LogWindowPosition", L"LogWindowSize", hwndDlg);
183 
184  Button_SetCheck(GetDlgItem(hwndDlg, IDC_AUTOSCROLL), BST_CHECKED);
185 
186  PhRegisterCallback(&PhLoggedCallback, LoggedCallback, NULL, &LoggedRegistration);
187  PhpUpdateLogList();
188  ListView_EnsureVisible(ListViewHandle, ListViewCount - 1, FALSE);
189  }
190  break;
191  case WM_DESTROY:
192  {
193  PhSaveListViewColumnsToSetting(L"LogListViewColumns", ListViewHandle);
194  PhSaveWindowPlacementToSetting(L"LogWindowPosition", L"LogWindowSize", hwndDlg);
195 
196  PhDeleteLayoutManager(&WindowLayoutManager);
197 
198  PhUnregisterCallback(&PhLoggedCallback, &LoggedRegistration);
200  PhLogWindowHandle = NULL;
201  }
202  break;
203  case WM_COMMAND:
204  {
205  switch (LOWORD(wParam))
206  {
207  case IDCANCEL:
208  case IDOK:
209  DestroyWindow(hwndDlg);
210  break;
211  case IDC_CLEAR:
212  {
214  PhpUpdateLogList();
215  }
216  break;
217  case IDC_COPY:
218  {
219  PPH_STRING string;
220  ULONG selectedCount;
221 
222  selectedCount = ListView_GetSelectedCount(ListViewHandle);
223 
224  if (selectedCount == 0)
225  {
226  // User didn't select anything, so copy all items.
227  string = PhpGetStringForSelectedLogEntries(TRUE);
228  PhSetStateAllListViewItems(ListViewHandle, LVIS_SELECTED, LVIS_SELECTED);
229  }
230  else
231  {
232  string = PhpGetStringForSelectedLogEntries(FALSE);
233  }
234 
235  PhSetClipboardString(hwndDlg, &string->sr);
236  PhDereferenceObject(string);
237 
238  SetFocus(ListViewHandle);
239  }
240  break;
241  case IDC_SAVE:
242  {
243  static PH_FILETYPE_FILTER filters[] =
244  {
245  { L"Text files (*.txt)", L"*.txt" },
246  { L"All files (*.*)", L"*.*" }
247  };
248  PVOID fileDialog;
249 
250  fileDialog = PhCreateSaveFileDialog();
251 
252  PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER));
253  PhSetFileDialogFileName(fileDialog, L"Process Hacker Log.txt");
254 
255  if (PhShowFileDialog(hwndDlg, fileDialog))
256  {
257  NTSTATUS status;
258  PPH_STRING fileName;
259  PPH_FILE_STREAM fileStream;
260  PPH_STRING string;
261 
262  fileName = PhGetFileDialogFileName(fileDialog);
263  PhAutoDereferenceObject(fileName);
264 
265  if (NT_SUCCESS(status = PhCreateFileStream(
266  &fileStream,
267  fileName->Buffer,
268  FILE_GENERIC_WRITE,
269  FILE_SHARE_READ,
271  0
272  )))
273  {
275  PhWritePhTextHeader(fileStream);
276 
277  string = PhpGetStringForSelectedLogEntries(TRUE);
278  PhWriteStringAsUtf8FileStreamEx(fileStream, string->Buffer, string->Length);
279  PhDereferenceObject(string);
280 
281  PhDereferenceObject(fileStream);
282  }
283 
284  if (!NT_SUCCESS(status))
285  PhShowStatus(hwndDlg, L"Unable to create the file", status, 0);
286  }
287 
288  PhFreeFileDialog(fileDialog);
289  }
290  break;
291  }
292  }
293  break;
294  case WM_NOTIFY:
295  {
296  LPNMHDR header = (LPNMHDR)lParam;
297 
298  switch (header->code)
299  {
300  case LVN_GETDISPINFO:
301  {
302  NMLVDISPINFO *dispInfo = (NMLVDISPINFO *)header;
303  PPH_LOG_ENTRY entry;
304 
305  entry = PhGetItemCircularBuffer_PVOID(&PhLogBuffer, ListViewCount - dispInfo->item.iItem - 1);
306 
307  if (dispInfo->item.iSubItem == 0)
308  {
309  if (dispInfo->item.mask & LVIF_TEXT)
310  {
311  SYSTEMTIME systemTime;
312  PPH_STRING dateTime;
313 
314  PhLargeIntegerToLocalSystemTime(&systemTime, &entry->Time);
315  dateTime = PhFormatDateTime(&systemTime);
316  wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, dateTime->Buffer, _TRUNCATE);
317  PhDereferenceObject(dateTime);
318  }
319  }
320  else if (dispInfo->item.iSubItem == 1)
321  {
322  if (dispInfo->item.mask & LVIF_TEXT)
323  {
324  PPH_STRING string;
325 
326  string = PhFormatLogEntry(entry);
327  wcsncpy_s(dispInfo->item.pszText, dispInfo->item.cchTextMax, string->Buffer, _TRUNCATE);
328  PhDereferenceObject(string);
329  }
330  }
331  }
332  break;
333  }
334  }
335  break;
336  case WM_SIZE:
337  {
338  PhLayoutManagerLayout(&WindowLayoutManager);
339  }
340  break;
341  case WM_SIZING:
342  {
343  PhResizingMinimumSize((PRECT)lParam, wParam, MinimumSize.right, MinimumSize.bottom);
344  }
345  break;
346  case WM_PH_LOG_UPDATED:
347  {
348  PhpUpdateLogList();
349  }
350  break;
351  }
352 
353  return FALSE;
354 }