ReactOS  0.4.15-dev-5459-gb85f005
eventvwr.c
Go to the documentation of this file.
1 /*
2  * PROJECT: ReactOS Event Log Viewer
3  * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE: Event Log Viewer main file.
5  * COPYRIGHT: Copyright 2007 Marc Piulachs <marc.piulachs@codexchange.net>
6  * Copyright 2008-2016 Eric Kohl <eric.kohl@reactos.org>
7  * Copyright 2016-2022 Hermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
8  */
9 
10 #include "eventvwr.h"
11 #include "evtdetctl.h"
12 
13 #include <sddl.h> // For ConvertSidToStringSidW
14 #include <shellapi.h>
15 #include <shlwapi.h>
16 
17 #include <pseh/pseh2.h>
18 
19 // #include "resource.h"
20 
21 #define LVM_PROGRESS (WM_APP + 1) // Used by the subclassed ListView
22 
23 static const LPCWSTR EVENTVWR_WNDCLASS = L"EVENTVWR"; /* The main window class name */
24 static const LPCWSTR EVENTLOG_BASE_KEY = L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\";
25 static const LPCWSTR EVNTVWR_PARAM_KEY = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Event Viewer";
26 
27 /* The 3 system logs that should always exist in the user's system */
28 static const LPCWSTR SystemLogs[] =
29 {
30  L"Application",
31  L"Security",
32  L"System"
33 };
34 
35 /* MessageFile message buffer size */
36 #define EVENT_MESSAGE_EVENTTEXT_BUFFER (1024*10) // NOTE: Used by evtdetctl.c
37 #define EVENT_MESSAGE_FILE_BUFFER (1024*10)
38 #define EVENT_DLL_SEPARATOR L";"
39 #define EVENT_CATEGORY_MESSAGE_FILE L"CategoryMessageFile"
40 #define EVENT_MESSAGE_FILE L"EventMessageFile"
41 #define EVENT_PARAMETER_MESSAGE_FILE L"ParameterMessageFile"
42 
43 #define MAX_LOADSTRING 255
44 
45 #define SPLIT_WIDTH 4
46 
47 /* Globals */
48 HINSTANCE hInst; /* Current instance */
49 WCHAR szTitle[MAX_LOADSTRING]; /* The title bar text */
50 WCHAR szTitleTemplate[MAX_LOADSTRING]; /* The logged-on title bar text */
51 WCHAR szStatusBarTemplate[MAX_LOADSTRING]; /* The status bar text */
52 WCHAR szLoadingWait[MAX_LOADSTRING]; /* The "Loading, please wait..." text */
53 WCHAR szEmptyList[MAX_LOADSTRING]; /* The "There are no items to show in this view" text */
54 WCHAR szSaveFilter[MAX_LOADSTRING]; /* Filter Mask for the save Dialog */
55 
56 INT nVSplitPos; /* Vertical splitter (1) position */
57 INT nHSplitPos; /* Horizontal splitter (2) position */
58 BYTE bSplit = 0; /* Splitter state:
59  * 0: No splitting;
60  * 1: Vertical splitting;
61  * 2: Horizontal splitting.
62  */
63 
64 HWND hwndMainWindow = NULL; /* Main window */
65 HWND hwndTreeView; /* TreeView control */
66 HWND hwndListView; /* ListView control */ // NOTE: Used by evtdetctl.c
67 HWND hwndEventDetails; /* Event details pane */
68 HWND hwndStatus; /* Status bar */
69 HWND hwndStatusProgress; /* Progress bar in the status bar */
70 HMENU hMainMenu; /* The application's main menu */
71 
73 
74 LPWSTR lpComputerName = NULL; /* NULL: local user computer (default) */
75 LPWSTR lpszzUserLogsToLoad = NULL; /* The list of user logs to load at startup (multi-string) */
77 
78 HKEY hkMachine = NULL; // Registry handle to the HKEY_LOCAL_MACHINE key of the remote computer registry.
79 
80 /* Global event records cache for the current active event log filter */
83 
84 /* Lists of event logs and event log filters */
88 
91 
92 /*
93  * Setting EnumFilter to a valid pointer and raising the hStartEnumEvent event
94  * triggers the event-enumerator thread to perform a new enumeration.
95  */
97 HANDLE hStartStopEnumEvent = NULL; // End-of-application event
98 HANDLE hStartEnumEvent = NULL; // Command event
99 
100 /* Default Open/Save-As dialog box */
102 
103 
104 /* Event Viewer Application Settings */
105 typedef struct _SETTINGS
106 {
107  BOOL bShowDetailsPane; /* Show (TRUE) or Hide (FALSE) the events details pane */
108  BOOL bShowGrid; /* Show (TRUE) or Hide (FALSE) the events view grid */
109  BOOL bSaveSettings; /* Save (TRUE) or do not save (FALSE) current settings on exit */
110  BOOL bNewestEventsFirst; /* Sort the displayed events the newest ones first (TRUE) or last (FALSE) */
111  INT nVSplitPos; /* Vertical splitter position */
112  INT nHSplitPos; /* Horizontal splitter position */
114 } SETTINGS, *PSETTINGS;
115 
117 
118 
119 /* Forward declarations of functions included in this code module */
120 
121 static DWORD WINAPI
122 StartStopEnumEventsThread(IN LPVOID lpParameter);
123 
124 VOID OpenUserEventLogFile(IN LPCWSTR lpszFileName);
125 
129 
135 
136 
137 /* MAIN FUNCTIONS *************************************************************/
138 
139 VOID
141 {
142  LPWSTR lpMessageBuffer;
143 
144  if (dwError == ERROR_SUCCESS)
145  return;
146 
150  NULL,
151  dwError,
153  (LPWSTR)&lpMessageBuffer,
154  0, NULL))
155  {
156  return;
157  }
158 
159  MessageBoxW(hwndMainWindow, lpMessageBuffer, szTitle, MB_OK | MB_ICONERROR);
160  LocalFree(lpMessageBuffer);
161 }
162 
163 VOID
165 {
167  LPCWSTR lpUsage;
168  INT iUsageLen = LoadStringW(hInst, IDS_USAGE, (LPWSTR)&lpUsage, 0);
169 
170  if (iUsageLen == 0)
171  return;
172 
173  lpBuffer = HeapAlloc(GetProcessHeap(), 0, (iUsageLen + 1) * sizeof(WCHAR));
174  if (!lpBuffer)
175  return;
176 
177  StringCchCopyNW(lpBuffer, iUsageLen + 1, lpUsage, iUsageLen);
179 
181 }
182 
183 BOOL
185 {
186  BOOL Success = FALSE;
187  INT i, argc;
188  LPWSTR* argv;
189 
190  /* Skip any leading whitespace */
191  if (lpCmdLine)
192  {
193  while (iswspace(*lpCmdLine))
194  ++lpCmdLine;
195  }
196 
197  /* No command line means no processing needed */
198  if (!lpCmdLine || !*lpCmdLine)
199  return TRUE;
200 
201  /* Build the arguments vector */
202  argv = CommandLineToArgvW(lpCmdLine, &argc);
203  if (!argv)
204  return FALSE;
205 
206  /* Parse the command line for options (skip the program name) */
207  for (i = 1; i < argc; ++i)
208  {
209  /* Check for new options */
210  if (argv[i][0] == L'-' || argv[i][0] == L'/')
211  {
212  if (argv[i][1] == L'?' && argv[i][2] == 0)
213  {
214  /* Display help */
215  DisplayUsage();
216  goto Quit;
217  }
218  else
219  if (argv[i][2] == L':')
220  {
221  switch (towupper(argv[i][1]))
222  {
223  case L'L':
224  {
225  LPWSTR lpNewBuffer;
226  LPWSTR lpFileName = argv[i] + 3;
227  SIZE_T cbFileName;
228 
229  /* Check for a quoted file name */
230  if (*lpFileName == L'\"')
231  {
232  /* Skip this quote, and the last one too if any */
233  ++lpFileName;
234  cbFileName = wcslen(lpFileName);
235  if (cbFileName > 0 && lpFileName[cbFileName - 1] == L'\"')
236  lpFileName[cbFileName - 1] = UNICODE_NULL;
237  }
238 
239  /* Skip this one if we do not actually have a file name */
240  if (!*lpFileName)
241  continue;
242 
243  cbFileName = (wcslen(lpFileName) + 1) * sizeof(WCHAR);
244 
245  /* Reallocate the list of user logs to load */
247  {
248  lpNewBuffer = HeapReAlloc(GetProcessHeap(),
251  /* Count the multi-string NULL-terminator */
252  cbUserLogsSize + cbFileName + sizeof(WCHAR));
253  }
254  else
255  {
256  cbUserLogsSize = 0;
257  lpNewBuffer = HeapAlloc(GetProcessHeap(),
259  /* Count the multi-string NULL-terminator */
260  cbUserLogsSize + cbFileName + sizeof(WCHAR));
261  }
262 
263  if (!lpNewBuffer)
264  {
266  goto Quit;
267  }
268 
269  lpszzUserLogsToLoad = lpNewBuffer;
270  lpNewBuffer = (LPWSTR)((ULONG_PTR)lpNewBuffer + cbUserLogsSize);
271  cbUserLogsSize += cbFileName;
272 
273  /* Save the file name */
274  StringCbCopyW(lpNewBuffer, cbFileName, lpFileName);
275 
276  continue;
277  }
278 
279  default:
280  break;
281  }
282  }
283 
284  /* Unknown argument: display help and bail out */
285  DisplayUsage();
286  goto Quit;
287  }
288  else
289  {
290  /*
291  * An argument that does not start with the switch character.
292  * If this is the first argument then this corresponds to the
293  * optional computer name. Otherwise this is a wrong argument.
294  */
295  if (i == 1)
296  {
297  /* Store the computer name */
298  LPWSTR lpTemp = argv[i];
299  SIZE_T cbLength;
300 
301  /* Strip any leading backslashes */
302  while (*lpTemp == L'\\')
303  ++lpTemp;
304 
305  cbLength = (wcslen(lpTemp) + 1) * sizeof(WCHAR);
307  if (lpComputerName)
308  {
309  StringCbCopyW(lpComputerName, cbLength, lpTemp);
310  }
311  /* else, fall back to local computer */
312  }
313  else
314  {
315  /* Invalid syntax: display help and bail out */
316  DisplayUsage();
317  goto Quit;
318  }
319  }
320  }
321 
322  Success = TRUE;
323 
324 Quit:
325  /* In case of failure, free anything we have allocated */
326  if (!Success)
327  {
329  {
330  cbUserLogsSize = 0;
333  }
334  if (lpComputerName)
335  {
338  }
339  }
340 
341  /* Free the arguments vector and exit */
342  LocalFree(argv);
343  return Success;
344 }
345 
346 BOOL
347 LoadSettings(int nDefCmdShow)
348 {
349  LONG Result;
350  HKEY hKeyEventVwr;
351  DWORD dwType, cbData;
352  WCHAR buffer[100];
353 
354  /* Load the default values */
355  Settings.bSaveSettings = TRUE;
356  Settings.bShowDetailsPane = TRUE;
357  Settings.bShowGrid = FALSE;
358  Settings.bNewestEventsFirst = TRUE;
359  Settings.nVSplitPos = 250; /* Splitter default positions */
360  Settings.nHSplitPos = 250;
361  ZeroMemory(&Settings.wpPos, sizeof(Settings.wpPos));
362  Settings.wpPos.length = sizeof(Settings.wpPos);
363  SetRect(&Settings.wpPos.rcNormalPosition,
365  Settings.wpPos.showCmd = nDefCmdShow; // SW_SHOWNORMAL;
366 
367  /* Try to open the Event Viewer user key */
370  0,
372  &hKeyEventVwr) != ERROR_SUCCESS)
373  {
374  return FALSE;
375  }
376 
377  // Result = RegQueryValueExW(hKeyEventVwr, L"Filter", NULL, &dwType, (LPBYTE)&szFilter, &cbData); // REG_SZ
378  // Result = RegQueryValueExW(hKeyEventVwr, L"Find", NULL, &dwType, (LPBYTE)&szFind, &cbData); // REG_SZ
379  // Result = RegQueryValueExW(hKeyEventVwr, L"Module", NULL, &dwType, (LPBYTE)&szModule, &cbData); // REG_SZ
380 
381  cbData = sizeof(buffer);
382  Result = RegQueryValueExW(hKeyEventVwr, L"SaveSettings", NULL, &dwType, (LPBYTE)buffer, &cbData);
383  if (Result == ERROR_SUCCESS)
384  {
385  if (dwType == REG_SZ)
386  {
387  buffer[cbData / sizeof(WCHAR) - 1] = UNICODE_NULL;
388  Settings.bSaveSettings = !!(DWORD)_wtoi(buffer);
389  }
390  else if (dwType == REG_DWORD && cbData == sizeof(DWORD))
391  {
392  Settings.bSaveSettings = !!*(PDWORD)buffer;
393  }
394  }
395 
396  cbData = sizeof(buffer);
397  Result = RegQueryValueExW(hKeyEventVwr, L"DetailsPane", NULL, &dwType, (LPBYTE)buffer, &cbData);
398  if (Result == ERROR_SUCCESS)
399  {
400  if (dwType == REG_SZ)
401  {
402  buffer[cbData / sizeof(WCHAR) - 1] = UNICODE_NULL;
403  Settings.bShowDetailsPane = !!(DWORD)_wtoi(buffer);
404  }
405  else if (dwType == REG_DWORD && cbData == sizeof(DWORD))
406  {
407  Settings.bShowDetailsPane = !!*(PDWORD)buffer;
408  }
409  }
410 
411  cbData = sizeof(buffer);
412  Result = RegQueryValueExW(hKeyEventVwr, L"ShowGrid", NULL, &dwType, (LPBYTE)buffer, &cbData);
413  if (Result == ERROR_SUCCESS)
414  {
415  if (dwType == REG_SZ)
416  {
417  buffer[cbData / sizeof(WCHAR) - 1] = UNICODE_NULL;
418  Settings.bShowGrid = !!(DWORD)_wtoi(buffer);
419  }
420  else if (dwType == REG_DWORD && cbData == sizeof(DWORD))
421  {
422  Settings.bShowGrid = !!*(PDWORD)buffer;
423  }
424  }
425 
426  cbData = sizeof(buffer);
427  Result = RegQueryValueExW(hKeyEventVwr, L"SortOrder", NULL, &dwType, (LPBYTE)buffer, &cbData);
428  if (Result == ERROR_SUCCESS)
429  {
430  if (dwType == REG_SZ)
431  {
432  buffer[cbData / sizeof(WCHAR) - 1] = UNICODE_NULL;
433  Settings.bNewestEventsFirst = !!(DWORD)_wtoi(buffer);
434  }
435  else if (dwType == REG_DWORD && cbData == sizeof(DWORD))
436  {
437  Settings.bNewestEventsFirst = !!*(PDWORD)buffer;
438  }
439  }
440 
441  /* Retrieve the splitter positions */
442  cbData = sizeof(buffer);
443  Result = RegQueryValueExW(hKeyEventVwr, L"VSplitPos", NULL, &dwType, (LPBYTE)buffer, &cbData);
444  if (Result == ERROR_SUCCESS)
445  {
446  if (dwType == REG_SZ)
447  {
448  buffer[cbData / sizeof(WCHAR) - 1] = UNICODE_NULL;
449  Settings.nVSplitPos = (DWORD)_wtoi(buffer);
450  }
451  else if (dwType == REG_DWORD && cbData == sizeof(DWORD))
452  {
453  Settings.nVSplitPos = *(PDWORD)buffer;
454  }
455  }
456 
457  cbData = sizeof(buffer);
458  Result = RegQueryValueExW(hKeyEventVwr, L"HSplitPos", NULL, &dwType, (LPBYTE)buffer, &cbData);
459  if (Result == ERROR_SUCCESS)
460  {
461  if (dwType == REG_SZ)
462  {
463  buffer[cbData / sizeof(WCHAR) - 1] = UNICODE_NULL;
464  Settings.nHSplitPos = (DWORD)_wtoi(buffer);
465  }
466  else if (dwType == REG_DWORD && cbData == sizeof(DWORD))
467  {
468  Settings.nHSplitPos = *(PDWORD)buffer;
469  }
470  }
471 
472  /* Retrieve the geometry of the main window */
473  cbData = sizeof(buffer);
474  Result = RegQueryValueExW(hKeyEventVwr, L"Window", NULL, &dwType, (LPBYTE)buffer, &cbData);
475  if ((Result == ERROR_SUCCESS) && (dwType == REG_SZ))
476  buffer[cbData / sizeof(WCHAR) - 1] = UNICODE_NULL;
477  else
478  buffer[0] = UNICODE_NULL;
479 
480  if (swscanf(buffer, L"%d %d %d %d %d",
481  &Settings.wpPos.rcNormalPosition.left,
482  &Settings.wpPos.rcNormalPosition.top,
483  &Settings.wpPos.rcNormalPosition.right,
484  &Settings.wpPos.rcNormalPosition.bottom,
485  &Settings.wpPos.showCmd) != 5)
486  {
487  /* Parsing failed, use defaults */
488  SetRect(&Settings.wpPos.rcNormalPosition,
490  Settings.wpPos.showCmd = nDefCmdShow; // SW_SHOWNORMAL;
491  }
492 
493  RegCloseKey(hKeyEventVwr);
494  return TRUE;
495 }
496 
497 BOOL
499 {
500  HKEY hKeyEventVwr;
501  DWORD dwSize;
502  WCHAR buffer[100];
503 
504  /* Try to create/open the Event Viewer user key */
507  0,
508  NULL,
511  NULL,
512  &hKeyEventVwr,
513  NULL) != ERROR_SUCCESS)
514  {
515  return FALSE;
516  }
517 
518  dwSize = sizeof(Settings.bSaveSettings);
519  RegSetValueExW(hKeyEventVwr, L"SaveSettings", 0, REG_DWORD, (LPBYTE)&Settings.bSaveSettings, dwSize);
520 
521  /* Do not save more settings if we are not asked to do so */
522  if (!Settings.bSaveSettings)
523  goto Quit;
524 
525  dwSize = sizeof(Settings.bShowDetailsPane);
526  RegSetValueExW(hKeyEventVwr, L"DetailsPane", 0, REG_DWORD, (LPBYTE)&Settings.bShowDetailsPane, dwSize);
527 
528  dwSize = sizeof(Settings.bShowGrid);
529  RegSetValueExW(hKeyEventVwr, L"ShowGrid", 0, REG_DWORD, (LPBYTE)&Settings.bShowGrid, dwSize);
530 
531  dwSize = sizeof(Settings.bNewestEventsFirst);
532  RegSetValueExW(hKeyEventVwr, L"SortOrder", 0, REG_DWORD, (LPBYTE)&Settings.bNewestEventsFirst, dwSize);
533 
534  Settings.nVSplitPos = nVSplitPos;
535  dwSize = sizeof(Settings.nVSplitPos);
536  RegSetValueExW(hKeyEventVwr, L"VSplitPos", 0, REG_DWORD, (LPBYTE)&Settings.nVSplitPos, dwSize);
537 
538  Settings.nHSplitPos = nHSplitPos;
539  dwSize = sizeof(Settings.nHSplitPos);
540  RegSetValueExW(hKeyEventVwr, L"HSplitPos", 0, REG_DWORD, (LPBYTE)&Settings.nHSplitPos, dwSize);
541 
542  StringCbPrintfW(buffer, sizeof(buffer),
543  L"%d %d %d %d %d",
544  Settings.wpPos.rcNormalPosition.left,
545  Settings.wpPos.rcNormalPosition.top,
546  Settings.wpPos.rcNormalPosition.right,
547  Settings.wpPos.rcNormalPosition.bottom,
548  Settings.wpPos.showCmd);
549 
550  dwSize = (DWORD)((wcslen(buffer) + 1) * sizeof(WCHAR));
551  RegSetValueExW(hKeyEventVwr, L"Window", 0, REG_SZ, (LPBYTE)buffer, dwSize);
552 
553 Quit:
554  RegCloseKey(hKeyEventVwr);
555  return TRUE;
556 }
557 
558 int APIENTRY
560  HINSTANCE hPrevInstance,
561  LPWSTR lpCmdLine,
562  int nCmdShow)
563 {
564  HANDLE hThread;
566  HMODULE hRichEdit;
567  HACCEL hAccelTable;
568  MSG msg;
569 
570  UNREFERENCED_PARAMETER(hPrevInstance);
571  UNREFERENCED_PARAMETER(lpCmdLine);
572 
573  /* Whenever any of the common controls are used in your app,
574  * you must call InitCommonControlsEx() to register the classes
575  * for those controls. */
576  iccx.dwSize = sizeof(iccx);
578  InitCommonControlsEx(&iccx);
579 
580  /* Load the RichEdit DLL to add support for RichEdit controls */
581  hRichEdit = LoadLibraryW(L"riched20.dll");
582  if (!hRichEdit)
583  return -1;
584 
585  msg.wParam = (WPARAM)-1;
586 
587  /* Store the instance handle in the global variable */
588  hInst = hInstance;
589 
590  /* Initialize global strings */
596 
597  /*
598  * Process the command-line arguments. Note that we need the full
599  * command-line, with the program file included, and not just what
600  * WinMain() provides in its lpCmdLine parameter.
601  */
603  goto Quit;
604 
606  goto Quit;
607 
608  /* Load the settings */
609  LoadSettings(nCmdShow);
610 
611  /* Perform application initialization */
612  if (!InitInstance(hInstance))
613  goto Quit;
614 
616 
617  /* Create the Start/Stop enumerator thread */
618  // Manual-reset event
620  if (!hStartStopEnumEvent)
621  goto Cleanup;
622 
623  // Auto-reset event
625  if (!hStartEnumEvent)
626  goto Cleanup;
627 
630  NULL, 0, NULL);
631  if (!hThread)
632  goto Cleanup;
633 
634  /* Retrieve the available event logs on this computer and create filters for them */
638 
639  /* Open the user-specified logs if any are present on the command-line */
641  {
642  LPWSTR lpUserLog;
643  for (lpUserLog = lpszzUserLogsToLoad; *lpUserLog; lpUserLog += wcslen(lpUserLog) + 1)
644  {
645  OpenUserEventLogFile(lpUserLog);
646  }
647 
648  /* Now cleanup the list of user logs */
649  cbUserLogsSize = 0;
652  }
653 
654  /* Main message loop */
655  while (GetMessageW(&msg, NULL, 0, 0))
656  {
657  if (!TranslateAcceleratorW(hwndMainWindow, hAccelTable, &msg))
658  {
661  }
662  }
663 
664  /* Save the settings */
665  SaveSettings();
666 
667  /* Disconnect from computer */
669  {
670  /* We are connected to some other computer, close the old connection */
672  hkMachine = NULL;
673  }
674 
675  /* Stop the enumerator thread */
679 
680  /* Free the filters list and the event logs list */
682  FreeLogList();
683 
684 Cleanup:
685  /* Handle cleanup */
686  if (hStartEnumEvent)
690 
691 Quit:
692  /* Final cleanup */
694  {
695  cbUserLogsSize = 0;
698  }
699  if (lpComputerName)
700  {
703  }
704  FreeLibrary(hRichEdit);
705 
706  return (int)msg.wParam;
707 }
708 
709 
710 /* GENERIC HELPER FUNCTIONS ***************************************************/
711 
712 VOID
714  OUT PSYSTEMTIME pSystemTime)
715 {
716  SYSTEMTIME st1970 = { 1970, 1, 0, 1, 0, 0, 0, 0 };
717  FILETIME ftLocal;
718  union
719  {
720  FILETIME ft;
721  ULONGLONG ll;
722  } u1970, uUCT;
723 
724  uUCT.ft.dwHighDateTime = 0;
725  uUCT.ft.dwLowDateTime = EventTime;
726  SystemTimeToFileTime(&st1970, &u1970.ft);
727  uUCT.ll = uUCT.ll * 10000000 + u1970.ll;
728  FileTimeToLocalFileTime(&uUCT.ft, &ftLocal);
729  FileTimeToSystemTime(&ftLocal, pSystemTime);
730 }
731 
732 /*
733  * This function takes in entry a path to a single DLL, in which
734  * the message string of ID dwMessageId has to be searched.
735  * The other parameters are similar to those of the FormatMessageW API.
736  */
737 LPWSTR
739  IN LPCWSTR lpMessageDll,
740  IN DWORD dwFlags, // If we always use the same flags, just remove this param...
741  IN DWORD dwMessageId,
742  IN DWORD nSize,
743  IN va_list* Arguments OPTIONAL)
744 {
746  DWORD dwLength;
747  LPWSTR lpMsgBuf = NULL;
748 
749  hLibrary = LoadLibraryExW(lpMessageDll, NULL,
750  /* LOAD_LIBRARY_AS_IMAGE_RESOURCE | */ LOAD_LIBRARY_AS_DATAFILE);
751  if (hLibrary == NULL)
752  return NULL;
753 
754  /* Sanitize dwFlags */
757 
758  _SEH2_TRY
759  {
760  /*
761  * Retrieve the message string without appending extra newlines.
762  * Wrap in SEH to protect from invalid string parameters.
763  */
764  _SEH2_TRY
765  {
767  /* FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE |
768  FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, */
769  hLibrary,
770  dwMessageId,
772  (LPWSTR)&lpMsgBuf,
773  nSize,
774  Arguments);
775  }
777  {
778  dwLength = 0;
779 
780  /*
781  * An exception occurred while calling FormatMessage, this is usually
782  * the sign that a parameter was invalid, either 'lpMsgBuf' was NULL
783  * but we did not pass the flag FORMAT_MESSAGE_ALLOCATE_BUFFER, or the
784  * array pointer 'Arguments' was NULL or did not contain enough elements,
785  * and we did not pass the flag FORMAT_MESSAGE_IGNORE_INSERTS, and the
786  * message string expected too many inserts.
787  * In this last case only, we can call again FormatMessage but ignore
788  * explicitly the inserts. The string that we will return to the user
789  * will not be pre-formatted.
790  */
791  if (((dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) || lpMsgBuf) &&
793  {
794  /* Remove any possible harmful flags and always ignore inserts */
797 
798  /* If this call also throws an exception, we are really dead */
800  hLibrary,
801  dwMessageId,
803  (LPWSTR)&lpMsgBuf,
804  nSize,
805  NULL /* Arguments */);
806  }
807  }
808  _SEH2_END;
809  }
811  {
813  }
814  _SEH2_END;
815 
816  if (dwLength == 0)
817  {
818  ASSERT(lpMsgBuf == NULL);
819  lpMsgBuf = NULL;
820  }
821  else
822  {
823  LPWSTR ptr;
824 
825  ASSERT(lpMsgBuf);
826 
827  /* Trim any trailing whitespace */
828  ptr = lpMsgBuf + dwLength - 1;
829  while (iswspace(*ptr))
830  *ptr-- = UNICODE_NULL;
831  }
832 
833  return lpMsgBuf;
834 }
835 
836 /*
837  * This function takes in entry a comma-separated list of DLLs, in which
838  * the message string of ID dwMessageId has to be searched.
839  * The other parameters are similar to those of the FormatMessageW API.
840  */
841 LPWSTR
843  IN LPCWSTR lpMessageDllList,
844  IN DWORD dwFlags, // If we always use the same flags, just remove this param...
845  IN DWORD dwMessageId,
846  IN DWORD nSize,
847  IN va_list* Arguments OPTIONAL)
848 {
849  BOOL Success = FALSE;
850  SIZE_T cbLength;
851  LPWSTR szMessageDllList;
852  LPWSTR szDll;
853  LPWSTR lpMsgBuf = NULL;
854 
855  /* Allocate a local buffer for the DLL list that can be tokenized */
856  // TODO: Optimize that!! Maybe we can cleverly use lpMessageDllList in read/write mode
857  // and cleverly temporarily replace the ';' by UNICODE_NULL, do our job, then reverse the change.
858  cbLength = (wcslen(lpMessageDllList) + 1) * sizeof(WCHAR);
859  szMessageDllList = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbLength);
860  if (!szMessageDllList)
861  return NULL;
862  CopyMemory(szMessageDllList, lpMessageDllList, cbLength);
863 
864  /* Loop through the list of message DLLs */
865  szDll = wcstok(szMessageDllList, EVENT_DLL_SEPARATOR);
866  while ((szDll != NULL) && !Success)
867  {
868  // Uses LANG_USER_DEFAULT
869  lpMsgBuf = GetMessageStringFromDll(szDll,
870  dwFlags,
871  dwMessageId,
872  nSize,
873  Arguments);
874  if (lpMsgBuf)
875  {
876  /* The ID was found and the message was formatted */
877  Success = TRUE;
878  break;
879  }
880 
881  /*
882  * The DLL could not be loaded, or the message could not be found,
883  * try the next DLL, if any.
884  */
885  szDll = wcstok(NULL, EVENT_DLL_SEPARATOR);
886  }
887 
888  HeapFree(GetProcessHeap(), 0, szMessageDllList);
889 
890  return lpMsgBuf;
891 }
892 
893 
894 typedef struct
895 {
896  LPWSTR pStartingAddress; // Pointer to the beginning of a parameter string in pMessage
897  LPWSTR pEndingAddress; // Pointer to the end of a parameter string in pMessage
898  DWORD pParameterID; // Parameter identifier found in pMessage
899  LPWSTR pParameter; // Actual parameter string
901 
902 DWORD
904  IN LPCWSTR lpMessageDllList,
905  IN BOOL bMessagePreFormatted,
906  IN CONST LPCWSTR pMessage,
907  OUT LPWSTR* pFinalMessage)
908 {
909  /*
910  * This code is heavily adapted from the MSDN example:
911  * https://msdn.microsoft.com/en-us/library/windows/desktop/bb427356.aspx
912  * with bugs removed.
913  */
914 
916  DWORD dwParamCount = 0; // Number of insertion strings found in pMessage
917  size_t cchBuffer = 0; // Size of the buffer in characters
918  size_t cchParams = 0; // Number of characters in all the parameter strings
919  size_t cch = 0;
920  DWORD i = 0;
921  param_strings_format_data* pParamData = NULL; // Array of pointers holding information about each parameter string in pMessage
922  LPWSTR pTempMessage = (LPWSTR)pMessage;
923  LPWSTR pTempFinalMessage = NULL;
924 
925  *pFinalMessage = NULL;
926 
927  /* Determine the number of parameter insertion strings in pMessage */
928  if (bMessagePreFormatted)
929  {
930  while ((pTempMessage = wcschr(pTempMessage, L'%')))
931  {
932  pTempMessage++;
933  if (iswdigit(*pTempMessage))
934  {
935  dwParamCount++;
936  while (iswdigit(*++pTempMessage)) ;
937  }
938  }
939  }
940  else
941  {
942  while ((pTempMessage = wcsstr(pTempMessage, L"%%")))
943  {
944  pTempMessage += 2;
945  if (iswdigit(*pTempMessage))
946  {
947  dwParamCount++;
948  while (iswdigit(*++pTempMessage)) ;
949  }
950  }
951  }
952 
953  /* If there are no parameter insertion strings in pMessage, just return */
954  if (dwParamCount == 0)
955  {
956  // *pFinalMessage = NULL;
957  goto Cleanup;
958  }
959 
960  /* Allocate the array of parameter string format data */
961  pParamData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwParamCount * sizeof(param_strings_format_data));
962  if (!pParamData)
963  {
965  goto Cleanup;
966  }
967 
968  /*
969  * Retrieve each parameter in pMessage and the beginning and end of the
970  * insertion string, as well as the message identifier of the parameter.
971  */
972  pTempMessage = (LPWSTR)pMessage;
973  if (bMessagePreFormatted)
974  {
975  while ((pTempMessage = wcschr(pTempMessage, L'%')) && (i < dwParamCount))
976  {
977  pTempMessage++;
978  if (iswdigit(*pTempMessage))
979  {
980  pParamData[i].pStartingAddress = pTempMessage-1;
981  pParamData[i].pParameterID = (DWORD)_wtol(pTempMessage);
982 
983  while (iswdigit(*++pTempMessage)) ;
984 
985  pParamData[i].pEndingAddress = pTempMessage;
986  i++;
987  }
988  }
989  }
990  else
991  {
992  while ((pTempMessage = wcsstr(pTempMessage, L"%%")) && (i < dwParamCount))
993  {
994  pTempMessage += 2;
995  if (iswdigit(*pTempMessage))
996  {
997  pParamData[i].pStartingAddress = pTempMessage-2;
998  pParamData[i].pParameterID = (DWORD)_wtol(pTempMessage);
999 
1000  while (iswdigit(*++pTempMessage)) ;
1001 
1002  pParamData[i].pEndingAddress = pTempMessage;
1003  i++;
1004  }
1005  }
1006  }
1007 
1008  /* Retrieve each parameter string */
1009  for (i = 0; i < dwParamCount; i++)
1010  {
1011  // pParamData[i].pParameter = GetMessageString(pParamData[i].pParameterID, 0, NULL);
1012  pParamData[i].pParameter =
1013  GetMessageStringFromDllList(lpMessageDllList,
1016  pParamData[i].pParameterID,
1017  0, NULL);
1018  if (!pParamData[i].pParameter)
1019  {
1020  /* Skip the insertion string */
1021  continue;
1022  }
1023 
1024  cchParams += wcslen(pParamData[i].pParameter);
1025  }
1026 
1027  /*
1028  * Allocate the final message buffer, the size of which is based on the
1029  * length of the original message and the length of each parameter string.
1030  */
1031  cchBuffer = wcslen(pMessage) + cchParams + 1;
1032  *pFinalMessage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cchBuffer * sizeof(WCHAR));
1033  if (!*pFinalMessage)
1034  {
1036  goto Cleanup;
1037  }
1038 
1039  pTempFinalMessage = *pFinalMessage;
1040 
1041  /* Build the final message string */
1042  pTempMessage = (LPWSTR)pMessage;
1043  for (i = 0; i < dwParamCount; i++)
1044  {
1045  /* Append the segment from pMessage */
1046  cch = pParamData[i].pStartingAddress - pTempMessage;
1047  StringCchCopyNW(pTempFinalMessage, cchBuffer, pTempMessage, cch);
1048  pTempMessage = pParamData[i].pEndingAddress;
1049  cchBuffer -= cch;
1050  pTempFinalMessage += cch;
1051 
1052  /* Append the parameter string */
1053  if (pParamData[i].pParameter)
1054  {
1055  StringCchCopyW(pTempFinalMessage, cchBuffer, pParamData[i].pParameter);
1056  cch = wcslen(pParamData[i].pParameter); // pTempFinalMessage
1057  }
1058  else
1059  {
1060  /*
1061  * We failed to retrieve the parameter string before, so just
1062  * place back the original string placeholder.
1063  */
1064  cch = pParamData[i].pEndingAddress /* == pTempMessage */ - pParamData[i].pStartingAddress;
1065  StringCchCopyNW(pTempFinalMessage, cchBuffer, pParamData[i].pStartingAddress, cch);
1066  // cch = wcslen(pTempFinalMessage);
1067  }
1068  cchBuffer -= cch;
1069  pTempFinalMessage += cch;
1070  }
1071 
1072  /* Append the last segment from pMessage */
1073  StringCchCopyW(pTempFinalMessage, cchBuffer, pTempMessage);
1074 
1075 Cleanup:
1076 
1077  // if (Status != ERROR_SUCCESS)
1078  // *pFinalMessage = NULL;
1079 
1080  if (pParamData)
1081  {
1082  for (i = 0; i < dwParamCount; i++)
1083  {
1084  if (pParamData[i].pParameter)
1085  LocalFree(pParamData[i].pParameter);
1086  }
1087 
1088  HeapFree(GetProcessHeap(), 0, pParamData);
1089  }
1090 
1091  return Status;
1092 }
1093 
1094 
1095 /*
1096  * The following functions were adapted from
1097  * shell32!dialogs/filedefext.cpp:``SH_...'' functions.
1098  */
1099 
1100 UINT
1101 FormatInteger(LONGLONG Num, LPWSTR pwszResult, UINT cchResultMax)
1102 {
1103  WCHAR wszNumber[24];
1104  WCHAR wszDecimalSep[8], wszThousandSep[8];
1105  NUMBERFMTW nf;
1106  WCHAR wszGrouping[12];
1107  INT cchGrouping;
1108  INT cchResult;
1109  INT i;
1110 
1111  // Print the number in uniform mode
1112  swprintf(wszNumber, L"%I64u", Num);
1113 
1114  // Get system strings for decimal and thousand separators.
1115  GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, wszDecimalSep, _countof(wszDecimalSep));
1116  GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, wszThousandSep, _countof(wszThousandSep));
1117 
1118  // Initialize format for printing the number in bytes
1119  ZeroMemory(&nf, sizeof(nf));
1120  nf.lpDecimalSep = wszDecimalSep;
1121  nf.lpThousandSep = wszThousandSep;
1122 
1123  // Get system string for groups separator
1124  cchGrouping = GetLocaleInfoW(LOCALE_USER_DEFAULT,
1126  wszGrouping,
1127  _countof(wszGrouping));
1128 
1129  // Convert grouping specs from string to integer
1130  for (i = 0; i < cchGrouping; i++)
1131  {
1132  WCHAR wch = wszGrouping[i];
1133 
1134  if (wch >= L'0' && wch <= L'9')
1135  nf.Grouping = nf.Grouping * 10 + (wch - L'0');
1136  else if (wch != L';')
1137  break;
1138  }
1139 
1140  if ((nf.Grouping % 10) == 0)
1141  nf.Grouping /= 10;
1142  else
1143  nf.Grouping *= 10;
1144 
1145  // Format the number
1147  0,
1148  wszNumber,
1149  &nf,
1150  pwszResult,
1151  cchResultMax);
1152 
1153  if (!cchResult)
1154  return 0;
1155 
1156  // GetNumberFormatW returns number of characters including UNICODE_NULL
1157  return cchResult - 1;
1158 }
1159 
1160 UINT
1161 FormatByteSize(LONGLONG cbSize, LPWSTR pwszResult, UINT cchResultMax)
1162 {
1163  UINT cchWritten, cchRemaining;
1164  LPWSTR pwszEnd;
1165  size_t cchStringRemaining;
1166 
1167  /* Write formated bytes count */
1168  cchWritten = FormatInteger(cbSize, pwszResult, cchResultMax);
1169  if (!cchWritten)
1170  return 0;
1171 
1172  /* Copy " bytes" to buffer */
1173  pwszEnd = pwszResult + cchWritten;
1174  cchRemaining = cchResultMax - cchWritten;
1175  StringCchCopyExW(pwszEnd, cchRemaining, L" ", &pwszEnd, &cchStringRemaining, 0);
1176  cchRemaining = (UINT)cchStringRemaining;
1177  cchWritten = LoadStringW(hInst, IDS_BYTES_FORMAT, pwszEnd, cchRemaining);
1178  cchRemaining -= cchWritten;
1179 
1180  return cchResultMax - cchRemaining;
1181 }
1182 
1183 LPWSTR
1184 FormatFileSizeWithBytes(const PULARGE_INTEGER lpQwSize, LPWSTR pwszResult, UINT cchResultMax)
1185 {
1186  UINT cchWritten, cchRemaining;
1187  LPWSTR pwszEnd;
1188  size_t cchCopyRemaining;
1189 
1190  /* Format bytes in KBs, MBs etc */
1191  if (StrFormatByteSizeW(lpQwSize->QuadPart, pwszResult, cchResultMax) == NULL)
1192  return NULL;
1193 
1194  /* If there is less bytes than 1KB, we have nothing to do */
1195  if (lpQwSize->QuadPart < 1024)
1196  return pwszResult;
1197 
1198  /* Concatenate " (" */
1199  cchWritten = (UINT)wcslen(pwszResult);
1200  pwszEnd = pwszResult + cchWritten;
1201  cchRemaining = cchResultMax - cchWritten;
1202  StringCchCopyExW(pwszEnd, cchRemaining, L" (", &pwszEnd, &cchCopyRemaining, 0);
1203  cchRemaining = (UINT)cchCopyRemaining;
1204 
1205  /* Write formated bytes count */
1206  cchWritten = FormatByteSize(lpQwSize->QuadPart, pwszEnd, cchRemaining);
1207  pwszEnd += cchWritten;
1208  cchRemaining -= cchWritten;
1209 
1210  /* Copy ")" to the buffer */
1211  StringCchCopyW(pwszEnd, cchRemaining, L")");
1212 
1213  return pwszResult;
1214 }
1215 
1216 /* Adapted from shell32!dialogs/filedefext.cpp:``CFileDefExt::GetFileTimeString'' */
1217 BOOL
1218 GetFileTimeString(LPFILETIME lpFileTime, LPWSTR pwszResult, UINT cchResult)
1219 {
1220  FILETIME ft;
1221  SYSTEMTIME st;
1222  int cchWritten;
1223  UINT cchRemaining = cchResult;
1224  size_t cchCopyRemaining;
1225  LPWSTR pwszEnd = pwszResult;
1226 
1227  if (!FileTimeToLocalFileTime(lpFileTime, &ft) || !FileTimeToSystemTime(&ft, &st))
1228  return FALSE;
1229 
1230  cchWritten = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, pwszEnd, cchRemaining);
1231  if (cchWritten)
1232  --cchWritten; // GetDateFormatW returns count with terminating zero
1233  // else
1234  // ERR("GetDateFormatW failed\n");
1235 
1236  cchRemaining -= cchWritten;
1237  pwszEnd += cchWritten;
1238 
1239  StringCchCopyExW(pwszEnd, cchRemaining, L", ", &pwszEnd, &cchCopyRemaining, 0);
1240  cchRemaining = (UINT)cchCopyRemaining;
1241 
1242  cchWritten = GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, pwszEnd, cchRemaining);
1243  if (cchWritten)
1244  --cchWritten; // GetTimeFormatW returns count with terminating zero
1245  // else
1246  // ERR("GetTimeFormatW failed\n");
1247 
1248  return TRUE;
1249 }
1250 
1251 
1252 HTREEITEM
1254  IN HTREEITEM hParent,
1255  IN LPWSTR lpText,
1256  IN INT Image,
1257  IN INT SelectedImage,
1258  IN LPARAM lParam)
1259 {
1260  TV_INSERTSTRUCTW Insert;
1261 
1262  ZeroMemory(&Insert, sizeof(Insert));
1263 
1264  Insert.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
1265  Insert.hInsertAfter = TVI_LAST;
1266  Insert.hParent = hParent;
1267  Insert.item.pszText = lpText;
1268  Insert.item.iImage = Image;
1269  Insert.item.iSelectedImage = SelectedImage;
1270  Insert.item.lParam = lParam;
1271 
1272  Insert.item.mask |= TVIF_STATE;
1273  Insert.item.stateMask = TVIS_OVERLAYMASK;
1274  Insert.item.state = INDEXTOOVERLAYMASK(1);
1275 
1276  return TreeView_InsertItem(hTreeView, &Insert);
1277 }
1278 
1279 
1280 /* LOG HELPER FUNCTIONS *******************************************************/
1281 
1282 PEVENTLOG
1284  IN PCWSTR LogName,
1285  IN BOOL Permanent)
1286 {
1287  PEVENTLOG EventLog;
1288  SIZE_T cchName;
1289 
1290  /* Allocate a new event log entry */
1291  EventLog = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*EventLog));
1292  if (!EventLog)
1293  return NULL;
1294 
1295  /* Allocate the computer name string (optional) and copy it */
1296  if (ComputerName)
1297  {
1298  cchName = wcslen(ComputerName) + 1;
1299  EventLog->ComputerName = HeapAlloc(GetProcessHeap(), 0, cchName * sizeof(WCHAR));
1300  if (EventLog->ComputerName)
1301  StringCchCopyW(EventLog->ComputerName, cchName, ComputerName);
1302  }
1303 
1304  /* Allocate the event log name string and copy it */
1305  cchName = wcslen(LogName) + 1;
1306  EventLog->LogName = HeapAlloc(GetProcessHeap(), 0, cchName * sizeof(WCHAR));
1307  if (!EventLog->LogName)
1308  {
1309  if (EventLog->ComputerName)
1310  HeapFree(GetProcessHeap(), 0, EventLog->ComputerName);
1311  HeapFree(GetProcessHeap(), 0, EventLog);
1312  return NULL;
1313  }
1314  StringCchCopyW(EventLog->LogName, cchName, LogName);
1315 
1316  EventLog->Permanent = Permanent;
1317 
1318  return EventLog;
1319 }
1320 
1321 VOID
1323 {
1324  if (EventLog->LogName)
1325  HeapFree(GetProcessHeap(), 0, EventLog->LogName);
1326 
1327  if (EventLog->ComputerName)
1328  HeapFree(GetProcessHeap(), 0, EventLog->ComputerName);
1329 
1330  if (EventLog->FileName)
1331  HeapFree(GetProcessHeap(), 0, EventLog->FileName);
1332 
1333  HeapFree(GetProcessHeap(), 0, EventLog);
1334 }
1335 
1336 
1337 PWSTR
1339 {
1340  PWSTR pStr;
1341  ULONG Length;
1342 
1343  if (!MultiStr)
1344  return NULL;
1345 
1346  pStr = (PWSTR)MultiStr;
1347  while (*pStr) pStr += (wcslen(pStr) + 1);
1348  Length = MultiStr - pStr + 2;
1349 
1350  pStr = HeapAlloc(GetProcessHeap(), 0, Length * sizeof(WCHAR));
1351  // NOTE: If we failed allocating the string, then fall back into no filter!
1352  if (pStr)
1353  CopyMemory(pStr, MultiStr, Length * sizeof(WCHAR));
1354 
1355  return pStr;
1356 }
1357 
1359 AllocEventLogFilter(// IN PCWSTR FilterName,
1361  IN BOOL Warning,
1362  IN BOOL Error,
1363  IN BOOL AuditSuccess,
1364  IN BOOL AuditFailure,
1365  IN PCWSTR Sources OPTIONAL,
1366  IN PCWSTR Users OPTIONAL,
1367  IN PCWSTR ComputerNames OPTIONAL,
1368  IN ULONG NumOfEventLogs,
1369  IN PEVENTLOG* EventLogs)
1370 {
1371  PEVENTLOGFILTER EventLogFilter;
1372 
1373  /* Allocate a new event log filter entry, big enough to accommodate the list of logs */
1374  EventLogFilter = HeapAlloc(GetProcessHeap(),
1376  FIELD_OFFSET(EVENTLOGFILTER, EventLogs[NumOfEventLogs]));
1377  if (!EventLogFilter)
1378  return NULL;
1379 
1380  EventLogFilter->Information = Information;
1381  EventLogFilter->Warning = Warning;
1382  EventLogFilter->Error = Error;
1383  EventLogFilter->AuditSuccess = AuditSuccess;
1384  EventLogFilter->AuditFailure = AuditFailure;
1385 
1386  /* Allocate and copy the sources, users, and computers multi-strings */
1387  EventLogFilter->Sources = AllocAndCopyMultiStr(Sources);
1388  EventLogFilter->Users = AllocAndCopyMultiStr(Users);
1389  EventLogFilter->ComputerNames = AllocAndCopyMultiStr(ComputerNames);
1390 
1391  /* Copy the list of event logs */
1392  EventLogFilter->NumOfEventLogs = NumOfEventLogs;
1393  CopyMemory(EventLogFilter->EventLogs, EventLogs, NumOfEventLogs * sizeof(PEVENTLOG));
1394 
1395  /* Initialize the filter reference count */
1396  EventLogFilter->ReferenceCount = 1;
1397 
1398  return EventLogFilter;
1399 }
1400 
1401 VOID
1403 {
1404  if (EventLogFilter->Sources)
1405  HeapFree(GetProcessHeap(), 0, EventLogFilter->Sources);
1406 
1407  if (EventLogFilter->Users)
1408  HeapFree(GetProcessHeap(), 0, EventLogFilter->Users);
1409 
1410  if (EventLogFilter->ComputerNames)
1411  HeapFree(GetProcessHeap(), 0, EventLogFilter->ComputerNames);
1412 
1413  HeapFree(GetProcessHeap(), 0, EventLogFilter);
1414 }
1415 
1417 {
1418  ASSERT(EventLogFilter);
1419  return InterlockedIncrement(&EventLogFilter->ReferenceCount);
1420 }
1421 
1423 {
1424  LONG RefCount;
1425 
1426  ASSERT(EventLogFilter);
1427 
1428  /* When the reference count reaches zero, delete the filter */
1429  RefCount = InterlockedDecrement(&EventLogFilter->ReferenceCount);
1430  if (RefCount <= 0)
1431  {
1432  /* Remove the filter from the list */
1434  EventLogFilter_Free(EventLogFilter);
1435  }
1436 
1437  return RefCount;
1438 }
1439 
1440 void
1442 {
1443  WCHAR *c;
1444 
1445  if (s != NULL)
1446  {
1447  c = s + wcslen(s) - 1;
1448  while (c >= s && iswspace(*c))
1449  --c;
1450  *++c = L'\0';
1451  }
1452 }
1453 
1454 DWORD
1456  IN LPCWSTR ComputerName OPTIONAL,
1458  OUT LPWSTR lpFullFileName OPTIONAL,
1459  IN DWORD nSize)
1460 {
1461  SIZE_T dwLength;
1462 
1463  /* Determine the needed size after expansion of any environment strings */
1465  if (dwLength == 0)
1466  {
1467  /* We failed, bail out */
1468  return 0;
1469  }
1470 
1471  /* If the file path is on a remote computer, estimate its length */
1472  // FIXME: Use WNetGetUniversalName instead?
1473  if (ComputerName && *ComputerName)
1474  {
1475  /* Skip any leading backslashes */
1476  while (*ComputerName == L'\\')
1477  ++ComputerName;
1478 
1479  if (*ComputerName)
1480  {
1481  /* Count 2 backslashes plus the computer name and one backslash separator */
1482  dwLength += 2 + wcslen(ComputerName) + 1;
1483  }
1484  }
1485 
1486  /* Check whether we have enough space */
1487  if (dwLength > nSize)
1488  {
1489  /* No, return the needed size in characters (includes NULL-terminator) */
1490  return dwLength;
1491  }
1492 
1493 
1494  /* Now expand the file path */
1495  ASSERT(dwLength <= nSize);
1496 
1497  /* Expand any existing environment strings */
1498  if (ExpandEnvironmentStringsW(lpFileName, lpFullFileName, dwLength) == 0)
1499  {
1500  /* We failed, bail out */
1501  return 0;
1502  }
1503 
1504  /* If the file path is on a remote computer, retrieve the network share form of the file name */
1505  // FIXME: Use WNetGetUniversalName instead?
1506  if (ComputerName && *ComputerName)
1507  {
1508  /* Note that we previously skipped any potential leading backslashes */
1509 
1510  /* Replace ':' by '$' in the drive letter */
1511  if (*lpFullFileName && lpFullFileName[1] == L':')
1512  lpFullFileName[1] = L'$';
1513 
1514  /* Prepend the computer name */
1515  MoveMemory(lpFullFileName + 2 + wcslen(ComputerName) + 1,
1516  lpFullFileName, dwLength * sizeof(WCHAR) - (2 + wcslen(ComputerName) + 1) * sizeof(WCHAR));
1517  lpFullFileName[0] = L'\\';
1518  lpFullFileName[1] = L'\\';
1519  wcsncpy(lpFullFileName + 2, ComputerName, wcslen(ComputerName));
1520  lpFullFileName[2 + wcslen(ComputerName)] = L'\\';
1521  }
1522 
1523  /* Return the number of stored characters (includes NULL-terminator) */
1524  return dwLength;
1525 }
1526 
1527 BOOL
1530  IN LPCWSTR EntryName,
1531  OUT PWCHAR lpModuleName) // TODO: Add IN DWORD BufLen
1532 {
1533  BOOL Success = FALSE;
1534  LONG Result;
1535  DWORD dwType, dwSize;
1537  WCHAR szKeyName[MAX_PATH];
1538  HKEY hLogKey = NULL;
1539  HKEY hSourceKey = NULL;
1540 
1541  StringCbCopyW(szKeyName, sizeof(szKeyName), EVENTLOG_BASE_KEY);
1542  StringCbCatW(szKeyName, sizeof(szKeyName), lpLogName);
1543 
1545  szKeyName,
1546  0,
1547  KEY_READ,
1548  &hLogKey);
1549  if (Result != ERROR_SUCCESS)
1550  return FALSE;
1551 
1552  Result = RegOpenKeyExW(hLogKey,
1553  SourceName,
1554  0,
1556  &hSourceKey);
1557  if (Result != ERROR_SUCCESS)
1558  {
1559  RegCloseKey(hLogKey);
1560  return FALSE;
1561  }
1562 
1563  dwSize = sizeof(szModuleName);
1564  Result = RegQueryValueExW(hSourceKey,
1565  EntryName,
1566  NULL,
1567  &dwType,
1569  &dwSize);
1570  if ((Result != ERROR_SUCCESS) || (dwType != REG_EXPAND_SZ && dwType != REG_SZ))
1571  {
1573  }
1574  else
1575  {
1576  /* NULL-terminate the string and expand it */
1577  szModuleName[dwSize / sizeof(WCHAR) - 1] = UNICODE_NULL;
1579  Success = TRUE;
1580  }
1581 
1582  RegCloseKey(hSourceKey);
1583  RegCloseKey(hLogKey);
1584 
1585  return Success;
1586 }
1587 
1588 BOOL
1591  IN PEVENTLOGRECORD pevlr,
1592  OUT PWCHAR CategoryName) // TODO: Add IN DWORD BufLen
1593 {
1594  BOOL Success = FALSE;
1595  WCHAR szMessageDLL[MAX_PATH];
1596  LPWSTR lpMsgBuf = NULL;
1597 
1599  goto Quit;
1600 
1601  /* Retrieve the message string without appending extra newlines */
1602  lpMsgBuf =
1603  GetMessageStringFromDllList(szMessageDLL,
1606  pevlr->EventCategory,
1608  NULL);
1609  if (lpMsgBuf)
1610  {
1611  /* Trim the string */
1612  TrimNulls(lpMsgBuf);
1613 
1614  /* Copy the category name */
1615  StringCchCopyW(CategoryName, MAX_PATH, lpMsgBuf);
1616 
1617  /* Free the buffer allocated by FormatMessage */
1618  LocalFree(lpMsgBuf);
1619 
1620  /* The ID was found and the message was formatted */
1621  Success = TRUE;
1622  }
1623 
1624 Quit:
1625  if (!Success)
1626  {
1627  if (pevlr->EventCategory != 0)
1628  {
1629  StringCchPrintfW(CategoryName, MAX_PATH, L"(%lu)", pevlr->EventCategory);
1630  Success = TRUE;
1631  }
1632  }
1633 
1634  return Success;
1635 }
1636 
1637 
1638 BOOL // NOTE: Used by evtdetctl.c
1641  IN PEVENTLOGRECORD pevlr,
1642  OUT PWCHAR EventText) // TODO: Add IN DWORD BufLen
1643 {
1644  BOOL Success = FALSE;
1645  DWORD i;
1646  size_t cch;
1647  WCHAR SourceModuleName[1024];
1648  WCHAR ParameterModuleName[1024];
1649  BOOL IsParamModNameCached = FALSE;
1650  LPWSTR lpMsgBuf = NULL;
1651  LPWSTR szStringArray, szMessage;
1652  LPWSTR *szArguments;
1653 
1654  /* Get the event string array */
1655  szStringArray = (LPWSTR)((LPBYTE)pevlr + pevlr->StringOffset);
1656 
1657  /* NOTE: GetEventMessageFileDLL can return a comma-separated list of DLLs */
1658  if (!GetEventMessageFileDLL(KeyName, SourceName, EVENT_MESSAGE_FILE, SourceModuleName))
1659  goto Quit;
1660 
1661  /* Allocate space for insertion strings */
1662  szArguments = HeapAlloc(GetProcessHeap(), 0, pevlr->NumStrings * sizeof(LPVOID));
1663  if (!szArguments)
1664  goto Quit;
1665 
1666  if (!IsParamModNameCached)
1667  {
1668  /* Now that the parameter file list is loaded, no need to reload it at the next run! */
1669  IsParamModNameCached = GetEventMessageFileDLL(KeyName, SourceName, EVENT_PARAMETER_MESSAGE_FILE, ParameterModuleName);
1670  // FIXME: If the string loading failed the first time, no need to retry it just after???
1671  }
1672 
1673  if (IsParamModNameCached)
1674  {
1675  /* Not yet support for reading messages from parameter message DLL */
1676  }
1677 
1678  szMessage = szStringArray;
1679  /*
1680  * HACK:
1681  * We do some hackish preformatting of the cached event strings...
1682  * That's because after we pass the string to FormatMessage
1683  * (via GetMessageStringFromDllList) with the FORMAT_MESSAGE_ARGUMENT_ARRAY
1684  * flag, instead of ignoring the insertion parameters and do the formatting
1685  * by ourselves. Therefore, the resulting string should have the parameter
1686  * string placeholders starting with a single '%' instead of a mix of one
1687  * and two '%'.
1688  */
1689  /* HACK part 1: Compute the full length of the string array */
1690  cch = 0;
1691  for (i = 0; i < pevlr->NumStrings; i++)
1692  {
1693  szMessage += wcslen(szMessage) + 1;
1694  }
1695  cch = szMessage - szStringArray;
1696 
1697  /* HACK part 2: Now do the HACK proper! */
1698  szMessage = szStringArray;
1699  for (i = 0; i < pevlr->NumStrings; i++)
1700  {
1701  lpMsgBuf = szMessage;
1702  while ((lpMsgBuf = wcsstr(lpMsgBuf, L"%%")))
1703  {
1704  if (iswdigit(lpMsgBuf[2]))
1705  {
1706  MoveMemory(lpMsgBuf, lpMsgBuf+1, ((szStringArray + cch) - lpMsgBuf - 1) * sizeof(WCHAR));
1707  }
1708  }
1709 
1710  szArguments[i] = szMessage;
1711  szMessage += wcslen(szMessage) + 1;
1712  }
1713 
1714  /* Retrieve the message string without appending extra newlines */
1715  lpMsgBuf =
1716  GetMessageStringFromDllList(SourceModuleName,
1719  pevlr->EventID,
1720  0,
1721  (va_list*)szArguments);
1722  if (lpMsgBuf)
1723  {
1724  /* Trim the string */
1725  TrimNulls(lpMsgBuf);
1726 
1727  szMessage = NULL;
1728  Success = (ApplyParameterStringsToMessage(ParameterModuleName,
1729  TRUE,
1730  lpMsgBuf,
1731  &szMessage) == ERROR_SUCCESS);
1732  if (Success && szMessage)
1733  {
1734  /* Free the buffer allocated by FormatMessage */
1735  LocalFree(lpMsgBuf);
1736  lpMsgBuf = szMessage;
1737  }
1738 
1739  /* Copy the event text */
1740  StringCchCopyW(EventText, EVENT_MESSAGE_EVENTTEXT_BUFFER, lpMsgBuf);
1741 
1742  /* Free the buffer allocated by FormatMessage */
1743  LocalFree(lpMsgBuf);
1744  }
1745 
1746  HeapFree(GetProcessHeap(), 0, szArguments);
1747 
1748 Quit:
1749  if (!Success)
1750  {
1751  /* Get a read-only pointer to the "event-not-found" string */
1752  lpMsgBuf = HeapAlloc(GetProcessHeap(), 0, EVENT_MESSAGE_EVENTTEXT_BUFFER * sizeof(WCHAR));
1754  StringCchPrintfW(EventText, EVENT_MESSAGE_EVENTTEXT_BUFFER, lpMsgBuf, (pevlr->EventID & 0xFFFF), SourceName);
1755 
1756  /* Append the strings */
1757  szMessage = szStringArray;
1758  for (i = 0; i < pevlr->NumStrings; i++)
1759  {
1760  StringCchCatW(EventText, EVENT_MESSAGE_EVENTTEXT_BUFFER, szMessage);
1762  szMessage += wcslen(szMessage) + 1;
1763  }
1764  }
1765 
1766  return Success;
1767 }
1768 
1769 VOID
1770 GetEventType(IN WORD dwEventType,
1771  OUT PWCHAR eventTypeText) // TODO: Add IN DWORD BufLen
1772 {
1773  switch (dwEventType)
1774  {
1775  case EVENTLOG_ERROR_TYPE:
1777  break;
1778  case EVENTLOG_WARNING_TYPE:
1780  break;
1783  break;
1784  case EVENTLOG_SUCCESS:
1786  break;
1789  break;
1792  break;
1793  default:
1795  break;
1796  }
1797 }
1798 
1799 BOOL
1801  IN OUT PSID *pLastSid,
1802  OUT PWCHAR pszUser) // TODO: Add IN DWORD BufLen
1803 {
1804  PSID pCurrentSid;
1805  PWSTR StringSid;
1806  WCHAR szName[1024];
1807  WCHAR szDomain[1024];
1810  DWORD cchDomain = ARRAYSIZE(szDomain);
1811  BOOL Success = FALSE;
1812 
1813  /* Point to the SID */
1814  pCurrentSid = (PSID)((LPBYTE)pelr + pelr->UserSidOffset);
1815 
1816  if (!IsValidSid(pCurrentSid))
1817  {
1818  *pLastSid = NULL;
1819  return FALSE;
1820  }
1821  else if (*pLastSid && EqualSid(*pLastSid, pCurrentSid))
1822  {
1823  return TRUE;
1824  }
1825 
1826  /* User SID */
1827  if (pelr->UserSidLength > 0)
1828  {
1829  /*
1830  * Try to retrieve the user account name and domain name corresponding
1831  * to the SID. If it cannot be retrieved, try to convert the SID to a
1832  * string-form. It should not be bigger than the user-provided buffer
1833  * 'pszUser', otherwise we return an error.
1834  */
1836  pCurrentSid,
1837  szName,
1838  &cchName,
1839  szDomain,
1840  &cchDomain,
1841  &peUse))
1842  {
1843  StringCchCopyW(pszUser, MAX_PATH, szName);
1844  Success = TRUE;
1845  }
1846  else if (ConvertSidToStringSidW(pCurrentSid, &StringSid))
1847  {
1848  /* Copy the string only if the user-provided buffer is big enough */
1849  if (wcslen(StringSid) + 1 <= MAX_PATH) // + 1 for NULL-terminator
1850  {
1851  StringCchCopyW(pszUser, MAX_PATH, StringSid);
1852  Success = TRUE;
1853  }
1854  else
1855  {
1857  Success = FALSE;
1858  }
1859 
1860  /* Free the allocated buffer */
1861  LocalFree(StringSid);
1862  }
1863  }
1864 
1865  *pLastSid = Success ? pCurrentSid : NULL;
1866 
1867  return Success;
1868 }
1869 
1870 
1872 {
1873  DWORD iIndex;
1874 
1875  if (!g_RecordPtrs)
1876  return;
1877 
1878  for (iIndex = 0; iIndex < g_TotalRecords; iIndex++)
1879  {
1880  if (g_RecordPtrs[iIndex])
1881  HeapFree(GetProcessHeap(), 0, g_RecordPtrs[iIndex]);
1882  }
1884  g_RecordPtrs = NULL;
1885  g_TotalRecords = 0;
1886 }
1887 
1888 BOOL
1890  IN PEVENTLOGRECORD pevlr)
1891 {
1892  if ((pevlr->EventType == EVENTLOG_SUCCESS && !EventLogFilter->Information ) ||
1893  (pevlr->EventType == EVENTLOG_INFORMATION_TYPE && !EventLogFilter->Information ) ||
1894  (pevlr->EventType == EVENTLOG_WARNING_TYPE && !EventLogFilter->Warning ) ||
1895  (pevlr->EventType == EVENTLOG_ERROR_TYPE && !EventLogFilter->Error ) ||
1896  (pevlr->EventType == EVENTLOG_AUDIT_SUCCESS && !EventLogFilter->AuditSuccess) ||
1897  (pevlr->EventType == EVENTLOG_AUDIT_FAILURE && !EventLogFilter->AuditFailure))
1898  {
1899  return FALSE;
1900  }
1901  return TRUE;
1902 }
1903 
1904 BOOL
1905 FilterByString(IN PCWSTR FilterString, // This is a multi-string
1906  IN PWSTR String)
1907 {
1908  PCWSTR pStr;
1909 
1910  /* The filter string is NULL so it does not filter anything */
1911  if (!FilterString)
1912  return TRUE;
1913 
1914  /*
1915  * If the filter string filters for an empty string AND the source string
1916  * is an empty string, we have a match (particular case of the last one).
1917  */
1918  if (!*FilterString && !*String)
1919  return TRUE;
1920 
1921  // if (*FilterString || *String)
1922 
1923  /*
1924  * If the filter string is empty BUT the source string is not empty,
1925  * OR vice-versa, we cannot have a match.
1926  */
1927  if ( (!*FilterString && *String) || (*FilterString && !*String) )
1928  return FALSE;
1929 
1930  /*
1931  * If the filter string filters for at least a non-empty string,
1932  * browse it and search for a string that matches the source string.
1933  */
1934  // else if (*FilterString && *String)
1935  {
1936  pStr = FilterString;
1937  while (*pStr)
1938  {
1939  if (wcsicmp(pStr, String) == 0)
1940  {
1941  /* We have a match, break the loop */
1942  break;
1943  }
1944 
1945  pStr += (wcslen(pStr) + 1);
1946  }
1947  if (!*pStr) // && *String
1948  {
1949  /* We do not have a match */
1950  return FALSE;
1951  }
1952  }
1953 
1954  /* We have a match */
1955  return TRUE;
1956 }
1957 
1958 /*
1959  * The events enumerator thread.
1960  */
1961 static DWORD WINAPI
1963 {
1964  PEVENTLOGFILTER EventLogFilter = (PEVENTLOGFILTER)lpParameter;
1965  PEVENTLOG EventLog;
1966 
1967  ULONG LogIndex;
1968  HANDLE hEventLog;
1969  PEVENTLOGRECORD pEvlr = NULL;
1970  PBYTE pEvlrEnd;
1971  PBYTE pEvlrBuffer;
1972  DWORD dwWanted, dwRead, dwNeeded, dwStatus = ERROR_SUCCESS;
1973  DWORD dwTotalRecords = 0, dwCurrentRecord = 0;
1974  DWORD dwFlags, dwMaxLength;
1975  size_t cchRemaining;
1976  LPWSTR lpszSourceName;
1977  LPWSTR lpszComputerName;
1978  BOOL bResult = TRUE; /* Read succeeded */
1980  PSID pLastSid = NULL;
1981 
1982  UINT uStep = 0, uStepAt = 0, uPos = 0;
1983 
1984  WCHAR szWindowTitle[MAX_PATH];
1985  WCHAR szStatusText[MAX_PATH];
1986  WCHAR szLocalDate[MAX_PATH];
1987  WCHAR szLocalTime[MAX_PATH];
1988  WCHAR szEventID[MAX_PATH];
1989  WCHAR szEventTypeText[MAX_LOADSTRING];
1990  WCHAR szCategoryID[MAX_PATH];
1991  WCHAR szUsername[MAX_PATH];
1992  WCHAR szNoUsername[MAX_PATH];
1993  WCHAR szCategory[MAX_PATH];
1994  WCHAR szNoCategory[MAX_PATH];
1995  PWCHAR lpTitleTemplateEnd;
1996 
1997  SYSTEMTIME time;
1998  LVITEMW lviEventItem;
1999 
2000  /* Save the current event log filter globally */
2001  EventLogFilter_AddRef(EventLogFilter);
2002  ActiveFilter = EventLogFilter;
2003 
2004 
2006  EventLog = EventLogFilter->EventLogs[0];
2007 
2008  // FIXME: Use something else instead of EventLog->LogName !!
2009 
2010  /*
2011  * Use a different formatting, whether the event log filter holds
2012  * only one log, or many logs (the latter case is WIP TODO!)
2013  */
2014  if (EventLogFilter->NumOfEventLogs <= 1)
2015  {
2016  StringCchPrintfExW(szWindowTitle,
2017  ARRAYSIZE(szWindowTitle),
2018  &lpTitleTemplateEnd,
2019  &cchRemaining,
2020  0,
2021  szTitleTemplate, szTitle, EventLog->LogName); /* i = number of characters written */
2022  dwMaxLength = (DWORD)cchRemaining;
2023  if (!EventLog->ComputerName)
2024  GetComputerNameW(lpTitleTemplateEnd, &dwMaxLength);
2025  else
2026  StringCchCopyW(lpTitleTemplateEnd, dwMaxLength, EventLog->ComputerName);
2027 
2028  StringCbPrintfW(szStatusText,
2029  sizeof(szStatusText),
2031  EventLog->LogName,
2032  0,
2033  0);
2034  }
2035  else
2036  {
2037  // TODO: Use a different title & implement filtering for multi-log filters !!
2038  // (EventLogFilter->NumOfEventLogs > 1)
2040  L"Many-logs filtering is not implemented yet!!",
2041  szTitle,
2043  }
2044 
2045  /* Set the window title */
2046  SetWindowTextW(hwndMainWindow, szWindowTitle);
2047 
2048  /* Update the status bar */
2049  StatusBar_SetText(hwndStatus, 0, szStatusText);
2050 
2051 
2052  /* Disable list view redraw */
2054 
2055  /* Clear the list view and free the cached records */
2057  FreeRecords();
2058 
2063 
2064  /* Do a loop over the logs enumerated in the filter */
2065  // FIXME: For now we only support 1 event log per filter!
2066  LogIndex = 0;
2067  // for (LogIndex = 0; LogIndex < EventLogFilter->NumOfEventLogs; ++LogIndex)
2068  {
2069 
2070  EventLog = EventLogFilter->EventLogs[LogIndex];
2071 
2072  /* Open the event log */
2073  if (EventLog->Permanent)
2074  hEventLog = OpenEventLogW(EventLog->ComputerName, EventLog->LogName);
2075  else
2076  hEventLog = OpenBackupEventLogW(EventLog->ComputerName, EventLog->LogName); // FileName
2077 
2078  if (hEventLog == NULL)
2079  {
2081  goto Cleanup;
2082  }
2083 
2084  // GetOldestEventLogRecord(hEventLog, &dwThisRecord);
2085 
2086  /* Get the total number of event log records */
2087  GetNumberOfEventLogRecords(hEventLog, &dwTotalRecords);
2088 
2089  if (dwTotalRecords > 0)
2090  {
2093  }
2094  else
2095  {
2098  }
2099 
2100  /* Set up the event records cache */
2101  g_RecordPtrs = HeapAlloc(hProcessHeap, HEAP_ZERO_MEMORY, dwTotalRecords * sizeof(*g_RecordPtrs));
2102  if (!g_RecordPtrs)
2103  {
2104  // ShowWin32Error(GetLastError());
2105  goto Quit;
2106  }
2107  g_TotalRecords = dwTotalRecords;
2108 
2110  goto Quit;
2111 
2112  LoadStringW(hInst, IDS_NOT_AVAILABLE, szNoUsername, ARRAYSIZE(szNoUsername));
2113  LoadStringW(hInst, IDS_NONE, szNoCategory, ARRAYSIZE(szNoCategory));
2114 
2116  uStepAt = (dwTotalRecords / 100) + 1;
2117 
2119 
2120  /* 0x7ffff is the maximum buffer size ReadEventLog will accept */
2121  dwWanted = 0x7ffff;
2122  pEvlr = HeapAlloc(hProcessHeap, 0, dwWanted);
2123 
2124  if (!pEvlr)
2125  goto Quit;
2126 
2127  while (dwStatus == ERROR_SUCCESS)
2128  {
2129  bResult = ReadEventLogW(hEventLog, dwFlags, 0, pEvlr, dwWanted, &dwRead, &dwNeeded);
2130  dwStatus = GetLastError();
2131 
2132  if (!bResult && dwStatus == ERROR_INSUFFICIENT_BUFFER)
2133  {
2134  pEvlr = HeapReAlloc(hProcessHeap, 0, pEvlr, dwNeeded);
2135  dwWanted = dwNeeded;
2136 
2137  if (!pEvlr)
2138  break;
2139 
2140  bResult = ReadEventLogW(hEventLog, dwFlags, 0, pEvlr, dwNeeded, &dwRead, &dwNeeded);
2141 
2142  if (!bResult)
2143  break;
2144  }
2145  else if (!bResult)
2146  {
2147  /* Exit on other errors (ERROR_HANDLE_EOF) */
2148  break;
2149  }
2150 
2151  pEvlrBuffer = (LPBYTE)pEvlr;
2152  pEvlrEnd = pEvlrBuffer + dwRead;
2153 
2154  while (pEvlrBuffer < pEvlrEnd)
2155  {
2156  PEVENTLOGRECORD pEvlrTmp = (PEVENTLOGRECORD)pEvlrBuffer;
2157  PWSTR lpszUsername, lpszCategoryName;
2158  g_RecordPtrs[dwCurrentRecord] = NULL;
2159 
2160  // ProgressBar_StepIt(hwndStatusProgress);
2161  uStep++;
2162  if (uStep % uStepAt == 0)
2163  {
2164  ++uPos;
2166  }
2167 
2169  goto Quit;
2170 
2171  /* Filter by event type */
2172  if (!FilterByType(EventLogFilter, pEvlrTmp))
2173  goto SkipEvent;
2174 
2175  /* Get the event source name and filter it */
2176  lpszSourceName = (LPWSTR)(pEvlrBuffer + sizeof(EVENTLOGRECORD));
2177  if (!FilterByString(EventLogFilter->Sources, lpszSourceName))
2178  goto SkipEvent;
2179 
2180  /* Get the computer name and filter it */
2181  lpszComputerName = (LPWSTR)(pEvlrBuffer + sizeof(EVENTLOGRECORD) + (wcslen(lpszSourceName) + 1) * sizeof(WCHAR));
2182  if (!FilterByString(EventLogFilter->ComputerNames, lpszComputerName))
2183  goto SkipEvent;
2184 
2185  /* Compute the event time */
2186  EventTimeToSystemTime(pEvlrTmp->TimeWritten, &time);
2187  GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &time, NULL, szLocalDate, ARRAYSIZE(szLocalDate));
2188  GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &time, NULL, szLocalTime, ARRAYSIZE(szLocalTime));
2189 
2190  /* Get the username that generated the event, and filter it */
2191  lpszUsername = GetEventUserName(pEvlrTmp, &pLastSid, szUsername) ? szUsername : szNoUsername;
2192 
2193  if (!FilterByString(EventLogFilter->Users, lpszUsername))
2194  goto SkipEvent;
2195 
2196  // TODO: Filter by event ID and category
2197  GetEventType(pEvlrTmp->EventType, szEventTypeText);
2198 
2199  lpszCategoryName = GetEventCategory(EventLog->LogName, lpszSourceName, pEvlrTmp, szCategory) ? szCategory : szNoCategory;
2200 
2201  StringCbPrintfW(szEventID, sizeof(szEventID), L"%u", (pEvlrTmp->EventID & 0xFFFF));
2202  StringCbPrintfW(szCategoryID, sizeof(szCategoryID), L"%u", pEvlrTmp->EventCategory);
2203 
2204  g_RecordPtrs[dwCurrentRecord] = HeapAlloc(hProcessHeap, 0, pEvlrTmp->Length);
2205  CopyMemory(g_RecordPtrs[dwCurrentRecord], pEvlrTmp, pEvlrTmp->Length);
2206 
2207  lviEventItem.mask = LVIF_IMAGE | LVIF_TEXT | LVIF_PARAM;
2208  lviEventItem.iItem = 0;
2209  lviEventItem.iSubItem = 0;
2210  lviEventItem.lParam = (LPARAM)g_RecordPtrs[dwCurrentRecord];
2211  lviEventItem.pszText = szEventTypeText;
2212 
2213  switch (pEvlrTmp->EventType)
2214  {
2215  case EVENTLOG_SUCCESS:
2217  lviEventItem.iImage = 0;
2218  break;
2219 
2220  case EVENTLOG_WARNING_TYPE:
2221  lviEventItem.iImage = 1;
2222  break;
2223 
2224  case EVENTLOG_ERROR_TYPE:
2225  lviEventItem.iImage = 2;
2226  break;
2227 
2229  lviEventItem.iImage = 3;
2230  break;
2231 
2233  lviEventItem.iImage = 4;
2234  break;
2235  }
2236 
2237  lviEventItem.iItem = ListView_InsertItem(hwndListView, &lviEventItem);
2238 
2239  ListView_SetItemText(hwndListView, lviEventItem.iItem, 1, szLocalDate);
2240  ListView_SetItemText(hwndListView, lviEventItem.iItem, 2, szLocalTime);
2241  ListView_SetItemText(hwndListView, lviEventItem.iItem, 3, lpszSourceName);
2242  ListView_SetItemText(hwndListView, lviEventItem.iItem, 4, lpszCategoryName);
2243  ListView_SetItemText(hwndListView, lviEventItem.iItem, 5, szEventID);
2244  ListView_SetItemText(hwndListView, lviEventItem.iItem, 6, lpszUsername);
2245  ListView_SetItemText(hwndListView, lviEventItem.iItem, 7, lpszComputerName);
2246 
2247 SkipEvent:
2248  pEvlrBuffer += pEvlrTmp->Length;
2249  dwCurrentRecord++;
2250  }
2251  }
2252 
2253 Quit:
2254 
2255  if (pEvlr)
2256  HeapFree(hProcessHeap, 0, pEvlr);
2257 
2258  /* Close the event log */
2259  CloseEventLog(hEventLog);
2260 
2261  } // end-for (LogIndex)
2262 
2263  /* All events loaded */
2264 
2265 Cleanup:
2266 
2269 
2270  // FIXME: Use something else instead of EventLog->LogName !!
2271 
2272  /*
2273  * Use a different formatting, whether the event log filter holds
2274  * only one log, or many logs (the latter case is WIP TODO!)
2275  */
2276  if (EventLogFilter->NumOfEventLogs <= 1)
2277  {
2278  StringCbPrintfW(szStatusText,
2279  sizeof(szStatusText),
2281  EventLog->LogName,
2282  dwTotalRecords,
2284  }
2285  else
2286  {
2287  // TODO: Use a different title & implement filtering for multi-log filters !!
2288  // (EventLogFilter->NumOfEventLogs > 1)
2289  }
2290 
2291  /* Update the status bar */
2292  StatusBar_SetText(hwndStatus, 0, szStatusText);
2293 
2294  /* Resume list view redraw */
2296 
2297  EventLogFilter_Release(EventLogFilter);
2298 
2301 
2302  return 0;
2303 }
2304 
2305 /*
2306  * The purpose of this thread is to serialize the creation of the events
2307  * enumeration thread, since the Event Log Viewer currently only supports
2308  * one view, one event list, one enumeration.
2309  */
2310 static DWORD WINAPI
2312 {
2313  HANDLE WaitHandles[2];
2314  DWORD WaitResult;
2315 
2316  WaitHandles[0] = hStartStopEnumEvent; // End-of-application event
2317  WaitHandles[1] = hStartEnumEvent; // Command event
2318 
2319  while (TRUE)
2320  {
2321  WaitResult = WaitForMultipleObjects(ARRAYSIZE(WaitHandles),
2322  WaitHandles,
2323  FALSE, // WaitAny
2324  INFINITE);
2325  switch (WaitResult)
2326  {
2327  case WAIT_OBJECT_0 + 0:
2328  {
2329  /* End-of-application event signaled, quit this thread */
2330 
2331  /* Stop the previous enumeration */
2332  if (hEnumEventsThread)
2333  {
2334  if (hStopEnumEvent)
2335  {
2338  // NOTE: The following is done by the enumeration thread just before terminating.
2339  // hStopEnumEvent = NULL;
2340  }
2341 
2344  }
2345 
2346  /* Clear the list view and free the cached records */
2348  FreeRecords();
2349 
2350  /* Reset the active filter */
2351  ActiveFilter = NULL;
2352 
2353  return 0;
2354  }
2355 
2356  case WAIT_OBJECT_0 + 1:
2357  {
2358  /* Restart a new enumeration if needed */
2359  PEVENTLOGFILTER EventLogFilter;
2360 
2361  /* Stop the previous enumeration */
2362  if (hEnumEventsThread)
2363  {
2364  if (hStopEnumEvent)
2365  {
2368  // NOTE: The following is done by the enumeration thread just before terminating.
2369  // hStopEnumEvent = NULL;
2370  }
2371 
2374  }
2375 
2376  /* Clear the list view and free the cached records */
2378  FreeRecords();
2379 
2380  /* Reset the active filter */
2381  ActiveFilter = NULL;
2382 
2383  EventLogFilter = InterlockedExchangePointer((PVOID*)&EnumFilter, NULL);
2384  if (!EventLogFilter)
2385  break;
2386 
2387  // Manual-reset event
2389  if (!hStopEnumEvent)
2390  break;
2391 
2393  0,
2395  (LPVOID)EventLogFilter,
2397  NULL);
2398  if (!hEnumEventsThread)
2399  {
2401  hStopEnumEvent = NULL;
2402  break;
2403  }
2404  // CloseHandle(hEnumEventsThread);
2406 
2407  break;
2408  }
2409 
2410  default:
2411  {
2412  /* Unknown command, must never go there! */
2413  return GetLastError();
2414  }
2415  }
2416  }
2417 
2418  return 0;
2419 }
2420 
2421 VOID
2423 {
2424  /* Signal the enumerator thread we want to enumerate events */
2425  InterlockedExchangePointer((PVOID*)&EnumFilter, EventLogFilter);
2427  return;
2428 }
2429 
2430 
2433 {
2434  TVITEMEXW tvItemEx;
2435  HTREEITEM hti;
2436 
2437  if (phti)
2438  *phti = NULL;
2439 
2440  /* Get index of selected item */
2442  if (hti == NULL)
2443  return NULL; // No filter
2444 
2445  tvItemEx.mask = TVIF_PARAM;
2446  tvItemEx.hItem = hti;
2447 
2448  TreeView_GetItem(hwndTreeView, &tvItemEx);
2449 
2450  if (phti)
2451  *phti = tvItemEx.hItem;
2452 
2453  return (PEVENTLOGFILTER)tvItemEx.lParam;
2454 }
2455 
2456 
2457 VOID
2459 {
2460  WIN32_FIND_DATAW FindData;
2461  HANDLE hFind;
2462  PEVENTLOG EventLog;
2463  PEVENTLOGFILTER EventLogFilter;
2464  SIZE_T cchFileName;
2465  HTREEITEM hItem = NULL;
2466 
2467  /* Check whether the file actually exists */
2468  hFind = FindFirstFileW(lpszFileName, &FindData);
2469  if (hFind == INVALID_HANDLE_VALUE)
2470  {
2472  return;
2473  }
2474  FindClose(hFind);
2475 
2476  /* Allocate a new event log entry */
2477  EventLog = AllocEventLog(NULL, lpszFileName, FALSE);
2478  if (EventLog == NULL)
2479  {
2481  return;
2482  }
2483 
2484  /* Allocate a new event log filter entry for this event log */
2485  EventLogFilter = AllocEventLogFilter(// LogName,
2486  TRUE, TRUE, TRUE, TRUE, TRUE,
2487  NULL, NULL, NULL,
2488  1, &EventLog);
2489  if (EventLogFilter == NULL)
2490  {
2492  EventLog_Free(EventLog);
2493  return;
2494  }
2495 
2496  /* Add the event log and the filter into their lists */
2497  InsertTailList(&EventLogList, &EventLog->ListEntry);
2498  InsertTailList(&EventLogFilterList, &EventLogFilter->ListEntry);
2499 
2500  /* Retrieve and cache the event log file */
2501  cchFileName = wcslen(lpszFileName) + 1;
2502  EventLog->FileName = HeapAlloc(GetProcessHeap(), 0, cchFileName * sizeof(WCHAR));
2503  if (EventLog->FileName)
2504  StringCchCopyW(EventLog->FileName, cchFileName, lpszFileName);
2505 
2507  (LPWSTR)lpszFileName,
2508  2, 3, (LPARAM)EventLogFilter);
2509 
2510  /* Select the event log */
2511  if (hItem)
2512  {
2513  // TreeView_Expand(hwndTreeView, htiUserLogs, TVE_EXPAND);
2516  }
2519 }
2520 
2521 VOID
2523 {
2524  WCHAR szFileName[MAX_PATH];
2525 
2526  ZeroMemory(szFileName, sizeof(szFileName));
2527 
2528  sfn.lpstrFile = szFileName;
2529  sfn.nMaxFile = ARRAYSIZE(szFileName);
2530 
2531  if (!GetOpenFileNameW(&sfn))
2532  return;
2534 
2536 }
2537 
2538 VOID
2540 {
2541  PEVENTLOG EventLog;
2542  HANDLE hEventLog;
2543  WCHAR szFileName[MAX_PATH];
2544 
2545  /* Bail out if there is no available filter */
2546  if (!EventLogFilter)
2547  return;
2548 
2549  ZeroMemory(szFileName, sizeof(szFileName));
2550 
2551  sfn.lpstrFile = szFileName;
2552  sfn.nMaxFile = ARRAYSIZE(szFileName);
2553 
2554  if (!GetSaveFileNameW(&sfn))
2555  return;
2556 
2557  EventLogFilter_AddRef(EventLogFilter);
2558 
2559  EventLog = EventLogFilter->EventLogs[0];
2560  hEventLog = OpenEventLogW(EventLog->ComputerName, EventLog->LogName);
2561 
2562  EventLogFilter_Release(EventLogFilter);
2563 
2564  if (!hEventLog)
2565  {
2567  return;
2568  }
2569 
2570  if (!BackupEventLogW(hEventLog, szFileName))
2572 
2573  CloseEventLog(hEventLog);
2574 }
2575 
2576 VOID
2578 {
2579  /* Bail out if there is no available filter */
2580  if (!EventLogFilter)
2581  return;
2582 
2583  if (InterlockedCompareExchangePointer((PVOID*)&ActiveFilter, NULL, NULL) == EventLogFilter)
2584  {
2585  /* Signal the enumerator thread we want to stop enumerating events */
2586  // EnumEvents(NULL);
2589  }
2590 
2591  /*
2592  * The deletion of the item automatically triggers a TVN_SELCHANGED
2593  * notification, that will reset the ActiveFilter (in case the item
2594  * selected is a filter). Otherwise we reset it there.
2595  */
2597 
2598  /* Remove the filter from the list */
2599  RemoveEntryList(&EventLogFilter->ListEntry);
2600  EventLogFilter_Release(EventLogFilter);
2601 
2602  // /* Select the default event log */
2603  // // TreeView_Expand(hwndTreeView, htiUserLogs, TVE_EXPAND);
2604  // TreeView_SelectItem(hwndTreeView, hItem);
2605  // TreeView_EnsureVisible(hwndTreeView, hItem);
2608 }
2609 
2610 
2611 BOOL
2613 {
2614  BOOL Success;
2615  PEVENTLOG EventLog;
2616  HANDLE hEventLog;
2617  WCHAR szFileName[MAX_PATH];
2618  WCHAR szMessage[MAX_LOADSTRING];
2619 
2620  /* Bail out if there is no available filter */
2621  if (!EventLogFilter)
2622  return FALSE;
2623 
2624  ZeroMemory(szFileName, sizeof(szFileName));
2625  ZeroMemory(szMessage, sizeof(szMessage));
2626 
2627  LoadStringW(hInst, IDS_CLEAREVENTS_MSG, szMessage, ARRAYSIZE(szMessage));
2628 
2629  sfn.lpstrFile = szFileName;
2630  sfn.nMaxFile = ARRAYSIZE(szFileName);
2631 
2633  {
2634  case IDCANCEL:
2635  return FALSE;
2636 
2637  case IDNO:
2638  sfn.lpstrFile = NULL;
2639  break;
2640 
2641  case IDYES:
2642  if (!GetSaveFileNameW(&sfn))
2643  return FALSE;
2644  break;
2645  }
2646 
2647  EventLogFilter_AddRef(EventLogFilter);
2648 
2649  EventLog = EventLogFilter->EventLogs[0];
2650  hEventLog = OpenEventLogW(EventLog->ComputerName, EventLog->LogName);
2651 
2652  EventLogFilter_Release(EventLogFilter);
2653 
2654  if (!hEventLog)
2655  {
2657  return FALSE;
2658  }
2659 
2660  Success = ClearEventLogW(hEventLog, sfn.lpstrFile);
2661  if (!Success)
2663 
2664  CloseEventLog(hEventLog);
2665  return Success;
2666 }
2667 
2668 
2669 VOID
2670 Refresh(IN PEVENTLOGFILTER EventLogFilter)
2671 {
2672  /* Bail out if there is no available filter */
2673  if (!EventLogFilter)
2674  return;
2675 
2676  /* Reenumerate the events through the filter */
2677  EnumEvents(EventLogFilter);
2678 }
2679 
2680 
2681 ATOM
2683 {
2684  WNDCLASSEXW wcex;
2685 
2686  wcex.cbSize = sizeof(wcex);
2687  wcex.style = 0;
2688  wcex.lpfnWndProc = WndProc;
2689  wcex.cbClsExtra = 0;
2690  wcex.cbWndExtra = 0;
2691  wcex.hInstance = hInstance;
2694  wcex.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); // COLOR_WINDOW + 1
2699  IMAGE_ICON,
2700  16,
2701  16,
2702  LR_SHARED);
2703 
2704  return RegisterClassExW(&wcex);
2705 }
2706 
2707 
2708 BOOL
2710  OUT PWCHAR lpModuleName, // TODO: Add IN DWORD BufLen
2711  OUT PDWORD pdwMessageID)
2712 {
2713  BOOL Success = FALSE;
2714  LONG Result;
2715  HKEY hLogKey;
2716  WCHAR *KeyPath;
2717  SIZE_T cbKeyPath;
2718  DWORD dwType, cbData;
2719  DWORD dwMessageID = 0;
2721 
2722  /* Use a default value for the message ID */
2723  *pdwMessageID = 0;
2724 
2725  cbKeyPath = (wcslen(EVENTLOG_BASE_KEY) + wcslen(lpLogName) + 1) * sizeof(WCHAR);
2726  KeyPath = HeapAlloc(GetProcessHeap(), 0, cbKeyPath);
2727  if (!KeyPath)
2728  {
2730  return FALSE;
2731  }
2732 
2733  StringCbCopyW(KeyPath, cbKeyPath, EVENTLOG_BASE_KEY);
2734  StringCbCatW(KeyPath, cbKeyPath, lpLogName);
2735 
2736  Result = RegOpenKeyExW(hkMachine, KeyPath, 0, KEY_QUERY_VALUE, &hLogKey);
2737  HeapFree(GetProcessHeap(), 0, KeyPath);
2738  if (Result != ERROR_SUCCESS)
2739  {
2741  return FALSE;
2742  }
2743 
2744  cbData = sizeof(szModuleName);
2745  Result = RegQueryValueExW(hLogKey,
2746  L"DisplayNameFile",
2747  NULL,
2748  &dwType,
2750  &cbData);
2751  if ((Result != ERROR_SUCCESS) || (dwType != REG_EXPAND_SZ && dwType != REG_SZ))
2752  {
2754  }
2755  else
2756  {
2757  /* NULL-terminate the string and expand it */
2758  szModuleName[cbData / sizeof(WCHAR) - 1] = UNICODE_NULL;
2760  Success = TRUE;
2761  }
2762 
2763  /*
2764  * If we have a 'DisplayNameFile', query for 'DisplayNameID';
2765  * otherwise it's not really useful. 'DisplayNameID' is optional.
2766  */
2767  if (Success)
2768  {
2769  cbData = sizeof(dwMessageID);
2770  Result = RegQueryValueExW(hLogKey,
2771  L"DisplayNameID",
2772  NULL,
2773  &dwType,
2774  (LPBYTE)&dwMessageID,
2775  &cbData);
2776  if ((Result != ERROR_SUCCESS) || (dwType != REG_DWORD))
2777  dwMessageID = 0;
2778 
2779  *pdwMessageID = dwMessageID;
2780  }
2781 
2782  RegCloseKey(hLogKey);
2783 
2784  return Success;
2785 }
2786 
2787 
2788 VOID
2790 {
2791  LONG Result;
2792  HKEY hEventLogKey, hLogKey;
2793  DWORD dwNumLogs = 0;
2794  DWORD dwIndex, dwMaxKeyLength;
2795  DWORD dwType;
2796  PEVENTLOG EventLog;
2797  PEVENTLOGFILTER EventLogFilter;
2798  LPWSTR LogName = NULL;
2800  DWORD lpcName;
2801  DWORD dwMessageID;
2803  HTREEITEM hRootNode = NULL, hItem = NULL, hItemDefault = NULL;
2804 
2806  {
2807  /* We are connected to some other computer, close the old connection */
2809  hkMachine = NULL;
2810  }
2811  if (!lpComputerName || !*lpComputerName)
2812  {
2813  /* Use the local computer registry */
2815  }
2816  else
2817  {
2818  /* Connect to the remote computer registry */
2820  if (Result != ERROR_SUCCESS)
2821  {
2822  /* Connection failed, display a message and bail out */
2823  hkMachine = NULL;
2825  return;
2826  }
2827  }
2828 
2829  /* Open the EventLog key */
2830  Result = RegOpenKeyExW(hkMachine, EVENTLOG_BASE_KEY, 0, KEY_READ, &hEventLogKey);
2831  if (Result != ERROR_SUCCESS)
2832  {
2833  return;
2834  }
2835 
2836  /* Retrieve the number of event logs enumerated as registry keys */
2837  Result = RegQueryInfoKeyW(hEventLogKey, NULL, NULL, NULL, &dwNumLogs, &dwMaxKeyLength,
2838  NULL, NULL, NULL, NULL, NULL, NULL);
2839  if (Result != ERROR_SUCCESS)
2840  {
2841  goto Quit;
2842  }
2843  if (!dwNumLogs)
2844  goto Quit;
2845 
2846  /* Take the NULL terminator into account */
2847  ++dwMaxKeyLength;
2848 
2849  /* Allocate the temporary buffer */
2850  LogName = HeapAlloc(GetProcessHeap(), 0, dwMaxKeyLength * sizeof(WCHAR));
2851  if (!LogName)
2852  goto Quit;
2853 
2854  /* Enumerate and retrieve each event log name */
2855  for (dwIndex = 0; dwIndex < dwNumLogs; dwIndex++)
2856  {
2857  lpcName = dwMaxKeyLength;
2858  Result = RegEnumKeyExW(hEventLogKey, dwIndex, LogName, &lpcName, NULL, NULL, NULL, NULL);
2859  if (Result != ERROR_SUCCESS)
2860  continue;
2861 
2862  /* Take the NULL terminator into account */
2863  ++lpcName;
2864 
2865  /* Allocate a new event log entry */
2866  EventLog = AllocEventLog(lpComputerName, LogName, TRUE);
2867  if (EventLog == NULL)
2868  continue;
2869 
2870  /* Allocate a new event log filter entry for this event log */
2871  EventLogFilter = AllocEventLogFilter(// LogName,
2872  TRUE, TRUE, TRUE, TRUE, TRUE,
2873  NULL, NULL, NULL,
2874  1, &EventLog);
2875  if (EventLogFilter == NULL)
2876  {
2877  EventLog_Free(EventLog);
2878  continue;
2879  }
2880 
2881  /* Add the event log and the filter into their lists */
2882  InsertTailList(&EventLogList, &EventLog->ListEntry);
2883  InsertTailList(&EventLogFilterList, &EventLogFilter->ListEntry);
2884 
2885  EventLog->FileName = NULL;
2886 
2887  /* Retrieve and cache the event log file */
2888  Result = RegOpenKeyExW(hEventLogKey,
2889  LogName,
2890  0,
2892  &hLogKey);
2893  if (Result == ERROR_SUCCESS)
2894  {
2895  lpcName = 0;
2896  Result = RegQueryValueExW(hLogKey,
2897  L"File",
2898  NULL,
2899  &dwType,
2900  NULL,
2901  &lpcName);
2902  if ((Result != ERROR_SUCCESS) || (dwType != REG_EXPAND_SZ && dwType != REG_SZ))
2903  {
2904  // Windows' EventLog uses some kind of default value, we do not.
2905  EventLog->FileName = NULL;
2906  }
2907  else
2908  {
2909  lpcName = ROUND_DOWN(lpcName, sizeof(WCHAR));
2910  EventLog->FileName = HeapAlloc(GetProcessHeap(), 0, lpcName);
2911  if (EventLog->FileName)
2912  {
2913  Result = RegQueryValueExW(hLogKey,
2914  L"File",
2915  NULL,
2916  &dwType,
2917  (LPBYTE)EventLog->FileName,
2918  &lpcName);
2919  if (Result != ERROR_SUCCESS)
2920  {
2921  HeapFree(GetProcessHeap(), 0, EventLog->FileName);
2922  EventLog->FileName = NULL;
2923  }
2924  else
2925  {
2926  EventLog->FileName[lpcName / sizeof(WCHAR) - 1] = UNICODE_NULL;
2927  }
2928  }
2929  }
2930 
2931  RegCloseKey(hLogKey);
2932  }
2933 
2934  /* Get the display name for the event log */
2935  lpDisplayName = NULL;
2936 
2938  if (GetDisplayNameFileAndID(LogName, szModuleName, &dwMessageID))
2939  {
2940  /* Retrieve the message string without appending extra newlines */
2941  lpDisplayName =
2945  dwMessageID,
2946  0,
2947  NULL);
2948  }
2949 
2950  /*
2951  * Select the correct tree root node, whether the log is a System
2952  * or an Application log. Default to Application log otherwise.
2953  */
2954  hRootNode = htiAppLogs;
2955  for (lpcName = 0; lpcName < ARRAYSIZE(SystemLogs); ++lpcName)
2956  {
2957  /* Check whether the log name is part of the system logs */
2958  if (wcsicmp(LogName, SystemLogs[lpcName]) == 0)
2959  {
2960  hRootNode = htiSystemLogs;
2961  break;
2962  }
2963  }
2964 
2965  hItem = TreeViewAddItem(hwndTreeView, hRootNode,
2966  (lpDisplayName ? lpDisplayName : LogName),
2967  2, 3, (LPARAM)EventLogFilter);
2968 
2969  /* Try to get the default event log: "Application" */
2970  if ((hItemDefault == NULL) && (wcsicmp(LogName, SystemLogs[0]) == 0))
2971  {
2972  hItemDefault = hItem;
2973  }
2974 
2975  /* Free the buffer allocated by FormatMessage */
2976  if (lpDisplayName)
2978  }
2979 
2980  HeapFree(GetProcessHeap(), 0, LogName);
2981 
2982 Quit:
2983  RegCloseKey(hEventLogKey);
2984 
2985  /* Select the default event log */
2986  if (hItemDefault)
2987  {
2988  // TreeView_Expand(hwndTreeView, hRootNode, TVE_EXPAND);
2989  TreeView_SelectItem(hwndTreeView, hItemDefault);
2990  TreeView_EnsureVisible(hwndTreeView, hItemDefault);
2991  }
2994 
2995  return;
2996 }
2997 
2998 VOID
3000 {
3002  PEVENTLOG EventLog;
3003 
3004  while (!IsListEmpty(&EventLogList))
3005  {
3007  EventLog = (PEVENTLOG)CONTAINING_RECORD(Entry, EVENTLOG, ListEntry);
3008  EventLog_Free(EventLog);
3009  }
3010 
3011  return;
3012 }
3013 
3014 VOID
3016 {
3018  PEVENTLOGFILTER EventLogFilter;
3019 
3020  while (!IsListEmpty(&EventLogFilterList))
3021  {
3023  EventLogFilter = (PEVENTLOGFILTER)CONTAINING_RECORD(Entry, EVENTLOGFILTER, ListEntry);
3024  EventLogFilter_Free(EventLogFilter);
3025  }
3026 
3027  ActiveFilter = NULL;
3028 
3029  return;
3030 }
3031 
3032 BOOL
3034 {
3035  RECT rcClient, rs;
3036  LONG StatusHeight;
3037  HIMAGELIST hSmall;
3038  LVCOLUMNW lvc = {0};
3039  WCHAR szTemp[256];
3040 
3041  /* Create the main window */
3042  rs = Settings.wpPos.rcNormalPosition;
3044  szTitle,
3046  rs.left, rs.top,
3047  (rs.right != CW_USEDEFAULT && rs.left != CW_USEDEFAULT) ? rs.right - rs.left : CW_USEDEFAULT,
3048  (rs.bottom != CW_USEDEFAULT && rs.top != CW_USEDEFAULT) ? rs.bottom - rs.top : CW_USEDEFAULT,
3049  NULL,
3050  NULL,
3051  hInstance,
3052  NULL);
3053  if (!hwndMainWindow)
3054  return FALSE;
3055 
3056  /* Create the status bar */
3057  hwndStatus = CreateWindowExW(0, // no extended styles
3058  STATUSCLASSNAMEW, // status bar
3059  L"", // no text
3060  WS_CHILD | WS_VISIBLE | CCS_BOTTOM | SBARS_SIZEGRIP, // styles
3061  0, 0, 0, 0, // x, y, cx, cy
3062  hwndMainWindow, // parent window
3063  (HMENU)100, // window ID
3064  hInstance, // instance
3065  NULL); // window data
3066 
3067  GetClientRect(hwndMainWindow, &rcClient);
3068  GetWindowRect(hwndStatus, &rs);
3069  StatusHeight = rs.bottom - rs.top;
3070 
3071  /* Create a progress bar in the status bar (hidden by default) */
3073  hwndStatusProgress = CreateWindowExW(0, // no extended styles
3074  PROGRESS_CLASSW, // status bar
3075  NULL, // no text
3076  WS_CHILD | PBS_SMOOTH, // styles
3077  rs.left, rs.top, // x, y
3078  rs.right - rs.left, rs.bottom - rs.top, // cx, cy
3079  hwndStatus, // parent window
3080  NULL, // window ID
3081  hInstance, // instance
3082  NULL); // window data
3083  /* Remove its static edge */
3087 
3088  /* Initialize the splitter default positions */
3089  nVSplitPos = Settings.nVSplitPos;
3090  nHSplitPos = Settings.nHSplitPos;
3091 
3092  /* Create the TreeView */
3094  WC_TREEVIEWW,
3095  NULL,
3096  // WS_CHILD | WS_VISIBLE | TVS_HASLINES | TVS_SHOWSELALWAYS,
3098  0, 0,
3099  nVSplitPos - SPLIT_WIDTH/2,
3100  (rcClient.bottom - rcClient.top) - StatusHeight,
3102  NULL,
3103  hInstance,
3104  NULL);
3105 
3106  /* Create the ImageList */
3109  ILC_COLOR32 | ILC_MASK, // ILC_COLOR24
3110  1, 1);
3111 
3112  /* Add event type icons to the ImageList: closed/opened folder, event log (normal/viewed) */
3117 
3118  /* Assign the ImageList to the Tree View */
3120 
3121  /* Add the event logs nodes */
3122  // "System Logs"
3125  // "Application Logs"
3126  LoadStringW(hInstance, IDS_EVENTLOG_APP, szTemp, ARRAYSIZE(szTemp));
3127  htiAppLogs = TreeViewAddItem(hwndTreeView, NULL, szTemp, 0, 1, (LPARAM)NULL);
3128  // "User Logs"
3129  LoadStringW(hInstance, IDS_EVENTLOG_USER, szTemp, ARRAYSIZE(szTemp));
3131 
3132  /* Create the Event details pane (optional) */
3134  if (hwndEventDetails)
3135  {
3139  nVSplitPos + SPLIT_WIDTH/2,
3140  nHSplitPos + SPLIT_WIDTH/2,
3141  (rcClient.right - rcClient.left) - nVSplitPos - SPLIT_WIDTH/2,
3142  (rcClient.bottom - rcClient.top) - nHSplitPos - SPLIT_WIDTH/2 - StatusHeight,
3144  }
3145 
3146  /* Create the ListView */
3148  WC_LISTVIEWW,
3149  NULL,
3151  nVSplitPos + SPLIT_WIDTH/2,
3152  0,
3153  (rcClient.right - rcClient.left) - nVSplitPos - SPLIT_WIDTH/2,
3154  hwndEventDetails && Settings.bShowDetailsPane
3155  ? nHSplitPos - SPLIT_WIDTH/2
3156  : (rcClient.bottom - rcClient.top) - StatusHeight,
3158  NULL,
3159  hInstance,
3160  NULL);
3161 
3162  /* Add the extended ListView styles */
3164 
3165  /* Create the ImageList */
3168  ILC_COLOR32 | ILC_MASK, // ILC_COLOR24
3169  1, 1);
3170 
3171  /* Add event type icons to the ImageList */
3177 
3178  /* Assign the ImageList to the List View */
3180 
3181  /* Now set up the listview with its columns */
3182  lvc.mask = LVCF_TEXT | LVCF_WIDTH;
3183  lvc.cx = 90;
3186  szTemp,
3187  ARRAYSIZE(szTemp));
3188  lvc.pszText = szTemp;
3190 
3191  lvc.cx = 70;
3194  szTemp,
3195  ARRAYSIZE(szTemp));
3196  lvc.pszText = szTemp;
3198 
3199  lvc.cx = 70;
3202  szTemp,
3203  ARRAYSIZE(szTemp));
3204  lvc.pszText = szTemp;
3206 
3207  lvc.cx = 150;
3210  szTemp,
3211  ARRAYSIZE(szTemp));
3212  lvc.pszText = szTemp;
3214 
3215  lvc.cx = 100;
3218  szTemp,
3219  ARRAYSIZE(szTemp));
3220  lvc.pszText = szTemp;
3222 
3223  lvc.cx = 60;
3226  szTemp,
3227  ARRAYSIZE(szTemp));
3228  lvc.pszText = szTemp;
3230 
3231  lvc.cx = 120;
3234  szTemp,
3235  ARRAYSIZE(szTemp));
3236  lvc.pszText = szTemp;
3238 
3239  lvc.cx = 100;
3242  szTemp,
3243  ARRAYSIZE(szTemp));
3244  lvc.pszText = szTemp;
3246 
3247  /* Initialize the save Dialog */
3248  ZeroMemory(&sfn, sizeof(sfn));
3250 
3252 
3253  sfn.lStructSize = sizeof(sfn);
3259  sfn.lpstrDefExt = L"evt";
3260 
3261  ShowWindow(hwndMainWindow, Settings.wpPos.showCmd);
3263 
3264  return TRUE;
3265 }
3266 
3268 {
3269  RECT rs;
3270  LONG StatusHeight;
3271  LONG_PTR dwExStyle;
3272  HDWP hdwp;
3273 
3274  /* Resize the status bar -- now done in WM_SIZE */
3275  // SendMessageW(hwndStatus, WM_SIZE, 0, 0);
3276  GetWindowRect(hwndStatus, &rs);
3277  StatusHeight = rs.bottom - rs.top;
3278 
3279  /*
3280  * Move the progress bar -- Take into account for extra size due to the static edge
3281  * (AdjustWindowRectEx() does not seem to work for the progress bar).
3282  */
3287  rs.left, rs.top, rs.right - rs.left, rs.bottom - rs.top,
3290 
3291  /*
3292  * TODO: Adjust the splitter positions:
3293  * - Vertical splitter (1) : fixed position from the left window side.
3294  * - Horizontal splitter (2): fixed position from the bottom window side.
3295  */
3297  nHSplitPos = min(max(nHSplitPos, SPLIT_WIDTH/2), cy - SPLIT_WIDTH/2 - StatusHeight); // FIXME!
3298 
3299  hdwp = BeginDeferWindowPos(3);
3300 
3301  if (hdwp)
3302  hdwp = DeferWindowPos(hdwp,
3303  hwndTreeView,
3304  HWND_TOP,
3305  0, 0,
3306  nVSplitPos - SPLIT_WIDTH/2,
3307  cy - StatusHeight,
3309 
3310  if (hdwp)
3311  hdwp = DeferWindowPos(hdwp,
3312  hwndListView,
3313  HWND_TOP,
3314  nVSplitPos + SPLIT_WIDTH/2, 0,
3315  cx - nVSplitPos - SPLIT_WIDTH/2,
3316  hwndEventDetails && Settings.bShowDetailsPane
3317  ? nHSplitPos - SPLIT_WIDTH/2
3318  : cy - StatusHeight,
3320 
3321  if (hwndEventDetails && Settings.bShowDetailsPane && hdwp)
3322  hdwp = DeferWindowPos(hdwp,
3324  HWND_TOP,
3325  nVSplitPos + SPLIT_WIDTH/2,
3326  nHSplitPos + SPLIT_WIDTH/2,
3327  cx - nVSplitPos - SPLIT_WIDTH/2,
3328  cy - nHSplitPos - SPLIT_WIDTH/2 - StatusHeight,
3330 
3331  if (hdwp)
3332  EndDeferWindowPos(hdwp);
3333 }
3334 
3335 
3338 {
3339  RECT rect;
3340 
3341  switch (uMsg)
3342  {
3343  case WM_CREATE:
3344  hMainMenu = GetMenu(hWnd);
3345  break;
3346 
3347  case WM_DESTROY:
3348  {
3350  PostQuitMessage(0);
3351  break;
3352  }
3353 
3354  case WM_NOTIFY:
3355  {
3357 
3358  if (hdr->hwndFrom == hwndListView)
3359  {
3360  switch (hdr->code)
3361  {
3362  case LVN_ITEMCHANGED:
3363  {
3365 
3366  if ( (pnmv->uChanged & LVIF_STATE) && /* The state has changed */
3367  (pnmv->uNewState & LVIS_SELECTED) /* The item has been (de)selected */ )
3368  {
3369  if (!hwndEventDetails)
3370  break;
3371 
3372  /* Verify the index of selected item */
3373  if (pnmv->iItem == -1)
3374  {
3375  MessageBoxW(hWnd,
3376  L"No selected items!",
3377  szTitle,
3378  MB_OK | MB_ICONERROR);
3379  break;
3380  }
3382  }
3383  break;
3384  }
3385 
3386 #ifdef LVN_ITEMACTIVATE
3387  case LVN_ITEMACTIVATE:
3388  {
3389  /* Get the index of the single focused selected item */
3391  INT iItem = lpnmitem->iItem;
3392  if (iItem != -1)
3394  break;
3395  }
3396 #else // LVN_ITEMACTIVATE
3397  case NM_DBLCLK:
3398  case NM_RETURN:
3399  {
3400  /* Get the index of the single focused selected item */
3402  if (iItem != -1)
3404  break;
3405  }
3406 #endif // LVN_ITEMACTIVATE
3407  }
3408  }
3409  else if (hdr->hwndFrom == hwndTreeView)
3410  {
3411  switch (hdr->code)
3412  {
3413  case TVN_BEGINLABELEDIT:
3414  {
3415  HTREEITEM hItem = ((LPNMTVDISPINFO)lParam)->item.hItem;
3416 
3417  /* Disable label editing for root nodes */
3418  return ((hItem == htiSystemLogs) ||
3419  (hItem == htiAppLogs) ||
3420  (hItem == htiUserLogs));
3421  }
3422 
3423  case TVN_ENDLABELEDIT:
3424  {
3425  TVITEMW item = ((LPNMTVDISPINFO)lParam)->item;
3426  HTREEITEM hItem = item.hItem;
3427 
3428  /* Disable label editing for root nodes */
3429  if ((hItem == htiSystemLogs) ||
3430  (hItem == htiAppLogs) ||
3431  (hItem == htiUserLogs))
3432  {
3433  return FALSE;
3434  }
3435 
3436  if (item.pszText)
3437  {
3438  LPWSTR pszText = item.pszText;
3439 
3440  /* Trim leading whitespace */
3441  while (*pszText && iswspace(*pszText))
3442  ++pszText;
3443 
3444  if (!*pszText)
3445  return FALSE;
3446 
3447  return TRUE;
3448  }
3449  else
3450  {
3451  return FALSE;
3452  }
3453  }
3454 
3455  case TVN_SELCHANGED:
3456  {
3457  PEVENTLOGFILTER EventLogFilter =
3458  (PEVENTLOGFILTER)((LPNMTREEVIEW)lParam)->itemNew.lParam;
3459 
3460  // FIXME: It might be nice to reference here the filter,
3461  // so that we don't have to reference/dereference it many times
3462  // in the other functions???
3463 
3464  // FIXME: This is a hack!!
3465  if (hwndEventDetails && EventLogFilter)
3466  {
3467  SendMessageW(hwndEventDetails, EVT_SETFILTER, 0, (LPARAM)EventLogFilter);
3468  }
3469 
3470  if (EventLogFilter)
3471  {
3472  /*
3473  * If we have selected a filter, enable the menu commands;
3474  * they will possibly be updated after events enumeration.
3475  */
3481  }
3482  else
3483  {
3489  }
3490 
3491  /*
3492  * The enumeration thread that is triggered by EnumEvents
3493  * will set a new value for the 'ActiveFilter'.
3494  */
3495  if (EventLogFilter)
3496  EnumEvents(EventLogFilter);
3497 
3498  break;
3499  }
3500  }
3501  }
3502  break;
3503  }
3504 
3505  case WM_COMMAND:
3506  {
3507  /* Parse the menu selections */
3508  switch (LOWORD(wParam))
3509  {
3510  case IDM_OPEN_EVENTLOG:
3511  OpenUserEventLog();
3512  break;
3513 
3514  case IDM_SAVE_EVENTLOG:
3516  break;
3517 
3518  case IDM_CLOSE_EVENTLOG:
3519  {
3520  HTREEITEM hti;
3521  PEVENTLOGFILTER EventLogFilter = GetSelectedFilter(&hti);
3522  CloseUserEventLog(EventLogFilter, hti);
3523  break;
3524  }
3525 
3526  case IDM_CLEAR_EVENTS:
3527  {
3528  PEVENTLOGFILTER EventLogFilter = GetSelectedFilter(NULL);
3529  if (EventLogFilter && ClearEvents(EventLogFilter))
3530  Refresh(EventLogFilter);
3531  break;
3532  }
3533 
3534  case IDM_RENAME_EVENTLOG:
3535  if (GetFocus() == hwndTreeView)
3537  break;
3538 
3539  case IDM_EVENTLOG_SETTINGS:
3540  {
3541  PEVENTLOGFILTER EventLogFilter = GetSelectedFilter(NULL);
3542  // TODO: Check the returned value?
3543  if (EventLogFilter)
3544  EventLogProperties(hInst, hWnd, EventLogFilter);
3545  break;
3546  }
3547 
3548  case IDM_LIST_NEWEST:
3549  case IDM_LIST_OLDEST:
3550  {
3551  BOOL bNewest = (LOWORD(wParam) == IDM_LIST_NEWEST);
3553 
3554  if (bNewest != Settings.bNewestEventsFirst)
3555  {
3556  Settings.bNewestEventsFirst = bNewest;
3558  }
3559  break;
3560  }
3561 
3562  case IDM_EVENT_DETAILS:
3563  {
3564  INT iItem;
3565  PEVENTLOGFILTER EventLogFilter;
3566 
3567  /* Get the index of the single focused selected item */
3569  if (iItem == -1)
3570  {
3579  break;
3580  }
3581 
3582  EventLogFilter = GetSelectedFilter(NULL);
3583  if (EventLogFilter)
3584  {
3585  EVENTDETAIL_INFO DetailInfo = {EventLogFilter, iItem};
3586 
3587  EventLogFilter_AddRef(EventLogFilter);
3590  hWnd,
3591  EventDetails,
3592  (LPARAM)&DetailInfo);
3593  EventLogFilter_Release(EventLogFilter);
3594  }
3595  break;
3596  }
3597 
3598  case IDM_REFRESH:
3600  break;
3601 
3603  {
3604  Settings.bShowDetailsPane = !Settings.bShowDetailsPane;
3606  MF_BYCOMMAND | (Settings.bShowDetailsPane ? MF_CHECKED : MF_UNCHECKED));
3607 
3608  GetClientRect(hWnd, &rect);
3609  if (Settings.bShowDetailsPane)
3610  {
3611  ResizeWnd(rect.right - rect.left, rect.bottom - rect.top);
3613  }
3614  else
3615  {
3617  ResizeWnd(rect.right - rect.left, rect.bottom - rect.top);
3618  }
3619 
3620  break;
3621  }
3622 
3623  case IDM_LIST_GRID_LINES:
3624  {
3625  Settings.bShowGrid = !Settings.bShowGrid;
3627  MF_BYCOMMAND | (Settings.bShowGrid ? MF_CHECKED : MF_UNCHECKED));
3628 
3630  break;
3631  }
3632 
3633  case IDM_SAVE_SETTINGS:
3634  {
3635  Settings.bSaveSettings = !Settings.bSaveSettings;
3637  MF_BYCOMMAND | (Settings.bSaveSettings ? MF_CHECKED : MF_UNCHECKED));
3638  break;
3639  }
3640 
3641  case IDM_ABOUT:
3642  {
3643  HICON hIcon;
3644  WCHAR szCopyright[MAX_LOADSTRING];
3645 
3647  LoadStringW(hInst, IDS_COPYRIGHT, szCopyright, ARRAYSIZE(szCopyright));
3648  ShellAboutW(hWnd, szTitle, szCopyright, hIcon);
3650  break;
3651  }
3652 
3653  case IDM_HELP:
3654  MessageBoxW(hWnd,
3655  L"Help not implemented yet!",
3656  szTitle,
3658  break;
3659 
3660  case IDM_EXIT:
3662  break;
3663 
3664  default:
3665  return DefWindowProcW(hWnd, uMsg, wParam, lParam);
3666  }
3667  break;
3668  }
3669 
3670  case WM_INITMENU:
3671  {
3672  if ((HMENU)wParam != hMainMenu)
3673  break;
3674 
3676  Settings.bNewestEventsFirst ? IDM_LIST_NEWEST : IDM_LIST_OLDEST,
3677  MF_BYCOMMAND);
3678 
3679  if (!hwndEventDetails)
3680  {
3683  }
3685  MF_BYCOMMAND | (Settings.bShowDetailsPane ? MF_CHECKED : MF_UNCHECKED));
3686 
3688  MF_BYCOMMAND | (Settings.bShowGrid ? MF_CHECKED : MF_UNCHECKED));
3689 
3691  MF_BYCOMMAND | (Settings.bSaveSettings ? MF_CHECKED : MF_UNCHECKED));
3692 
3693  break;
3694  }
3695 
3696 #if 0
3697  case WM_INITMENUPOPUP:
3698  lParam = lParam;
3699  break;
3700 #endif
3701 
3702  case WM_CONTEXTMENU:
3703  {
3704  RECT rc;
3705  HTREEITEM hItem;
3706  TVHITTESTINFO hInfo = {0};
3707 
3708  INT xPos = GET_X_LPARAM(lParam);
3709  INT yPos = GET_Y_LPARAM(lParam);
3710 
3712  hInfo.pt.x = xPos - rc.left;
3713  hInfo.pt.y = yPos - rc.top;
3714 
3715  hItem = TreeView_HitTest(hwndTreeView, &hInfo);
3716  if (hItem)
3717  {
3719 
3721  {
3723 
3724  DWORD dwCmdID = TrackPopupMenuEx(hCtxMenu,
3726  xPos, yPos, hWnd, NULL);
3728  }
3729  }
3730  break;
3731  }
3732 
3733  case WM_SETCURSOR:
3734  {
3735  POINT pt;
3736 
3737  if (LOWORD(lParam) != HTCLIENT)
3738  goto Default;
3739 
3740  GetCursorPos(&pt);
3741  ScreenToClient(hWnd, &pt);
3742 
3743  /* Set the cursor for the vertical splitter */
3744  if (pt.x >= nVSplitPos - SPLIT_WIDTH/2 && pt.x < nVSplitPos + SPLIT_WIDTH/2 + 1)
3745  {
3746  RECT rs;
3747  GetClientRect(hWnd, &rect);
3748  GetWindowRect(hwndStatus, &rs);
3749  if (pt.y >= rect.top && pt.y < rect.bottom - (rs.bottom - rs.top))
3750  {
3752  return TRUE;
3753  }
3754  }
3755  else
3756  /* Set the cursor for the horizontal splitter, if the Event details pane is displayed */
3757  if (hwndEventDetails && Settings.bShowDetailsPane &&
3758  (pt.y >= nHSplitPos - SPLIT_WIDTH/2 && pt.y < nHSplitPos + SPLIT_WIDTH/2 + 1))
3759  {
3760  // RECT rs;
3761  GetClientRect(hWnd, &rect);
3762  // GetWindowRect(hwndStatus, &rs);
3763  if (pt.x >= nVSplitPos + SPLIT_WIDTH/2 + 1 /* rect.left + (rs.bottom - rs.top) */ &&
3764  pt.x < rect.right)
3765  {
3767  return TRUE;
3768  }
3769  }
3770  goto Default;
3771  }
3772 
3773  case WM_LBUTTONDOWN:
3774  {
3775  INT x = GET_X_LPARAM(lParam);
3776  INT y = GET_Y_LPARAM(lParam);
3777 
3778  /* Reset the splitter state */
3779  bSplit = 0;
3780 
3781  /* Capture the cursor for the vertical splitter */
3782  if (x >= nVSplitPos - SPLIT_WIDTH/2 && x < nVSplitPos + SPLIT_WIDTH/2 + 1)
3783  {
3784  bSplit = 1;
3785  SetCapture(hWnd);
3786  }
3787  else
3788  /* Capture the cursor for the horizontal splitter, if the Event details pane is displayed */
3789  if (hwndEventDetails && Settings.bShowDetailsPane &&
3790  (y >= nHSplitPos - SPLIT_WIDTH/2 && y < nHSplitPos + SPLIT_WIDTH/2 + 1))
3791  {
3792  bSplit = 2;
3793  SetCapture(hWnd);
3794  }
3795  break;
3796  }
3797 
3798  case WM_LBUTTONUP:
3799  case WM_RBUTTONDOWN:
3800  {
3801  if (GetCapture() != hWnd)
3802  break;
3803 
3804  /* Adjust the correct splitter position */
3805  if (bSplit == 1)
3807  else if (bSplit == 2)
3809 
3810  /* If we are splitting, resize the windows */
3811  if (bSplit != 0)
3812  {
3813  GetClientRect(hWnd, &rect);
3814  ResizeWnd(rect.right - rect.left, rect.bottom - rect.top);
3815  }
3816 
3817  /* Reset the splitter state */
3818  bSplit = 0;
3819 
3820  ReleaseCapture();
3821  break;
3822  }
3823 
3824  case WM_MOUSEMOVE:
3825  {
3826  if (GetCapture() != hWnd)
3827  break;
3828 
3829  /* Move the correct splitter */
3830  if (bSplit == 1)
3831  {
3832  INT x = GET_X_LPARAM(lParam);
3833 
3834  GetClientRect(hWnd, &rect);
3835 
3836  x = min(max(x, SPLIT_WIDTH/2), rect.right - rect.left - SPLIT_WIDTH/2);
3837  if (nVSplitPos != x)
3838  {
3839  nVSplitPos = x;
3840  ResizeWnd(rect.right - rect.left, rect.bottom - rect.top);
3841  }
3842  }
3843  else if (bSplit == 2)
3844  {
3845  RECT rs;
3846  INT y = GET_Y_LPARAM(lParam);
3847 
3848  GetClientRect(hWnd, &rect);
3849  GetWindowRect(hwndStatus, &rs);
3850 
3851  y = min(max(y, SPLIT_WIDTH/2), rect.bottom - rect.top - SPLIT_WIDTH/2 - (rs.bottom - rs.top));
3852  if (nHSplitPos != y)
3853  {
3854  nHSplitPos = y;
3855  ResizeWnd(rect.right - rect.left, rect.bottom - rect.top);
3856  }
3857  }
3858  break;
3859  }
3860 
3861  case WM_SIZE:
3862  {
3863  if (wParam != SIZE_MINIMIZED)
3864  {
3867  break;
3868  }
3869  /* Fall through the default case */
3870  }
3871 
3872  default: Default:
3873  return DefWindowProcW(hWnd, uMsg, wParam, lParam);
3874  }
3875 
3876  return 0;
3877 }
3878 
3879 
3880 static
3881 VOID
3883 {
3884  LPWSTR lpLogName = EventLog->LogName;
3885 
3886  DWORD Result, dwType;
3887  DWORD dwMaxSize = 0, dwRetention = 0;
3888  BOOL Success;
3889  WIN32_FIND_DATAW FileInfo; // WIN32_FILE_ATTRIBUTE_DATA
3891  WCHAR wszBuf[MAX_PATH];
3892  WCHAR szTemp[MAX_LOADSTRING];
3893  LPWSTR FileName;
3894 
3895  HKEY hLogKey;
3896  WCHAR *KeyPath;
3897  DWORD cbData;
3898  SIZE_T cbKeyPath;
3899 
3900  if (EventLog->Permanent)
3901  {
3902 
3903  cbKeyPath = (wcslen(EVENTLOG_BASE_KEY) + wcslen(lpLogName) + 1) * sizeof(WCHAR);
3904  KeyPath = HeapAlloc(GetProcessHeap(), 0, cbKeyPath);
3905  if (!KeyPath)
3906  {
3908  goto Quit;
3909  }
3910 
3911  StringCbCopyW(KeyPath, cbKeyPath, EVENTLOG_BASE_KEY);
3912  StringCbCatW(KeyPath, cbKeyPath, lpLogName);
3913 
3914  Result = RegOpenKeyExW(hkMachine, KeyPath, 0, KEY_QUERY_VALUE, &hLogKey);
3915  HeapFree(GetProcessHeap(), 0, KeyPath);
3916  if (Result != ERROR_SUCCESS)
3917  {
3919  goto Quit;
3920  }
3921 
3922 
3923  cbData = sizeof(dwMaxSize);
3924  Result = RegQueryValueExW(hLogKey,
3925  L"MaxSize",
3926  NULL,
3927  &dwType,
3928  (LPBYTE)&dwMaxSize,
3929  &cbData);
3930  if ((Result != ERROR_SUCCESS) || (dwType != REG_DWORD))
3931  {
3932  // dwMaxSize = 512 * 1024; /* 512 kBytes */
3933  /* Workstation: 512 KB; Server: 16384 KB */
3934  dwMaxSize = 16384 * 1024;
3935  }
3936  /* Convert in KB */
3937  dwMaxSize /= 1024;
3938 
3939  cbData = sizeof(dwRetention);
3940  Result = RegQueryValueExW(hLogKey,
3941  L"Retention",
3942  NULL,
3943  &dwType,
3944  (LPBYTE)&dwRetention,
3945  &cbData);
3946  if ((Result != ERROR_SUCCESS) || (dwType != REG_DWORD))
3947  {
3948  /* By default, it is 604800 (secs) == 7 days. On Server, the retention is zeroed out. */
3949  dwRetention = 0;
3950  }
3951  /* Convert in days, rounded up */
3952  if (dwRetention != INFINITE)
3953  dwRetention = (dwRetention + 24*3600 - 1) / (24*3600);
3954 
3955  RegCloseKey(hLogKey);
3956 
3957  }
3958 
3959 Quit:
3960 
3961  SetDlgItemTextW(hDlg, IDC_DISPLAYNAME, lpLogName); // FIXME!
3962  SetDlgItemTextW(hDlg, IDC_LOGNAME, lpLogName);
3963 
3964  FileName = EventLog->FileName;
3965  if (FileName && *FileName)
3966  {
3967  /* Expand the file name. If the log file is on a remote computer, retrieve the network share form of the file name. */
3968  GetExpandedFilePathName(EventLog->ComputerName, FileName, wszBuf, ARRAYSIZE(wszBuf));
3969  FileName = wszBuf;
3970  }
3971  else
3972  {
3973  FileName = L"";
3974  }
3976 
3977  if (FileName && *FileName)
3978  {
3979  /*
3980  * The general problem here (and in the shell as well) is that
3981  * GetFileAttributesEx fails for files that are opened without
3982  * shared access. To retrieve file information for those we need
3983  * to use something else: FindFirstFile, on the full file name.
3984  */
3988  if (!Success)
3989  {
3991  Success = (hFind != INVALID_HANDLE_VALUE);
3992  if (Success)
3993  FindClose(hFind);
3994  }
3995  }
3996  else
3997  {
3998  Success = FALSE;
3999  }
4000 
4001  /* Starting there, FileName becomes invalid because we are reusing wszBuf */
4002 
4003  if (Success)
4004  {
4005  FileSize.u.LowPart = FileInfo.nFileSizeLow;
4006  FileSize.u.HighPart = FileInfo.nFileSizeHigh;
4007  if (FormatFileSizeWithBytes(&FileSize, wszBuf, ARRAYSIZE(wszBuf)))
4008  SetDlgItemTextW(hDlg, IDC_SIZE_LABEL, wszBuf);
4009 
4010  LoadStringW(hInst, IDS_NOT_AVAILABLE, szTemp, ARRAYSIZE(szTemp));
4011 
4012  if (GetFileTimeString(&FileInfo.ftCreationTime, wszBuf, ARRAYSIZE(wszBuf)))
4013  SetDlgItemTextW(hDlg, IDC_CREATED_LABEL, wszBuf);
4014  else
4015  SetDlgItemTextW(hDlg, IDC_CREATED_LABEL, szTemp);
4016 
4017  if (GetFileTimeString(&FileInfo.ftLastWriteTime, wszBuf, ARRAYSIZE(wszBuf)))
4018  SetDlgItemTextW(hDlg, IDC_MODIFIED_LABEL, wszBuf);
4019  else
4020  SetDlgItemTextW(hDlg, IDC_MODIFIED_LABEL, szTemp);
4021 
4022  if (GetFileTimeString(&FileInfo.ftLastAccessTime, wszBuf, ARRAYSIZE(wszBuf)))
4023  SetDlgItemTextW(hDlg, IDC_ACCESSED_LABEL, wszBuf);
4024  else
4025  SetDlgItemTextW(hDlg, IDC_MODIFIED_LABEL, szTemp);
4026  }
4027  else
4028  {
4029  LoadStringW(hInst, IDS_NOT_AVAILABLE, szTemp, ARRAYSIZE(szTemp));
4030 
4031  SetDlgItemTextW(hDlg, IDC_SIZE_LABEL, szTemp);
4032  SetDlgItemTextW(hDlg, IDC_CREATED_LABEL, szTemp);
4033  SetDlgItemTextW(hDlg, IDC_MODIFIED_LABEL, szTemp);
4034  SetDlgItemTextW(hDlg, IDC_ACCESSED_LABEL, szTemp);
4035  }
4036 
4037  if (EventLog->Permanent)
4038  {
4041 
4042  SetDlgItemInt(hDlg, IDC_EDIT_MAXLOGSIZE, dwMaxSize, FALSE);
4043  SetDlgItemInt(hDlg, IDC_EDIT_EVENTS_AGE, (dwRetention == 0) ? 7 : dwRetention, FALSE);
4044 
4045  if (dwRetention == 0)
4046  {
4050  }
4051  else if (dwRetention == INFINITE)
4052  {
4056  }
4057  else
4058  {
4062  }
4063  }
4064  else
4065  {
4066  // TODO: Hide the unused controls! Or, just use another type of property sheet!
4067  }
4068 }
4069 
4070 static
4071 VOID
4073 {
4074  LPWSTR lpLogName = EventLog->LogName;
4075 
4076  LONG Result;
4077  DWORD dwMaxSize = 0, dwRetention = 0;
4078  HKEY hLogKey;
4079  WCHAR *KeyPath;
4080  SIZE_T cbKeyPath;
4081 
4082  if (!EventLog->Permanent)
4083  return;
4084 
4085  cbKeyPath = (wcslen(EVENTLOG_BASE_KEY) + wcslen(lpLogName) + 1) * sizeof(WCHAR);
4086  KeyPath = HeapAlloc(GetProcessHeap(), 0, cbKeyPath);
4087  if (!KeyPath)
4088  {
4090  return;
4091  }
4092 
4093  StringCbCopyW(KeyPath, cbKeyPath, EVENTLOG_BASE_KEY);
4094  StringCbCatW(KeyPath, cbKeyPath, lpLogName);
4095 
4096  Result = RegOpenKeyExW(hkMachine, KeyPath, 0, KEY_SET_VALUE, &hLogKey);
4097  HeapFree(GetProcessHeap(), 0, KeyPath);
4098  if (Result != ERROR_SUCCESS)
4099  {
4101  return;
4102  }
4103 
4104  dwMaxSize = GetDlgItemInt(hDlg, IDC_EDIT_MAXLOGSIZE, NULL, FALSE) * 1024;
4105  RegSetValueExW(hLogKey,
4106  L"MaxSize",
4107  0,
4108  REG_DWORD,
4109  (LPBYTE)&dwMaxSize,
4110  sizeof(dwMaxSize));
4111 
4113  dwRetention = 0;
4114  else if (IsDlgButtonChecked(hDlg, IDC_NO_OVERWRITE) == BST_CHECKED)
4115  dwRetention = INFINITE;
4116  else // if (IsDlgButtonChecked(hDlg, IDC_OVERWRITE_OLDER_THAN) == BST_CHECKED)
4117  dwRetention = GetDlgItemInt(hDlg, IDC_EDIT_EVENTS_AGE, NULL, FALSE) * (24*3600);
4118 
4119  RegSetValueExW(hLogKey,
4120  L"Retention",
4121  0,
4122  REG_DWORD,
4123  (LPBYTE)&dwRetention,
4124  sizeof(dwRetention));
4125 
4126  RegCloseKey(hLogKey);
4127 }
4128 
4129 /* Message handler for EventLog Properties dialog */
4132 {
4133  PEVENTLOG EventLog;
4134  WCHAR szText[MAX_LOADSTRING];
4135 
4136  EventLog = (PEVENTLOG)GetWindowLongPtrW(hDlg, DWLP_USER);
4137 
4138  switch (uMsg)
4139  {
4140  case WM_INITDIALOG:
4141  {
4142  EventLog = (PEVENTLOG)((LPPROPSHEETPAGE)lParam)->lParam;
4143  SetWindowLongPtrW(hDlg, DWLP_USER, (LONG_PTR)EventLog);
4144 
4145  InitPropertiesDlg(hDlg, EventLog);
4146 
4147  PropSheet_UnChanged(GetParent(hDlg), hDlg);
4148  return (INT_PTR)TRUE;
4149  }
4150 
4151  case WM_DESTROY:
4152  return (INT_PTR)TRUE;
4153 
4154  case WM_NOTIFY:
4155  switch (((LPNMHDR)lParam)->code)
4156  {
4157  case PSN_APPLY:
4158  PropSheet_UnChanged(GetParent(hDlg), hDlg);
4159  SavePropertiesDlg(hDlg, EventLog);
4160  return (INT_PTR)TRUE;
4161  }
4162  break;
4163 
4164  case WM_COMMAND:
4165  switch (LOWORD(wParam))
4166  {
4167  case IDOK:
4168  case IDCANCEL:
4169  EndDialog(hDlg, LOWORD(wParam));
4170  return (INT_PTR)TRUE;
4171 
4172  case ID_CLEARLOG:
4173  {
4174  PEVENTLOGFILTER EventLogFilter = GetSelectedFilter(NULL);
4175  if (EventLogFilter && ClearEvents(EventLogFilter))
4176  {
4177  Refresh(EventLogFilter);
4178  InitPropertiesDlg(hDlg, EventLog);
4179  }
4180  return (INT_PTR)TRUE;
4181  }
4182 
4183  case IDC_EDIT_EVENTS_AGE:
4184  case IDC_EDIT_MAXLOGSIZE:
4185  if (HIWORD(wParam) == EN_CHANGE)
4186  {
4187  PropSheet_Changed(GetParent(hDlg), hDlg);
4188  }
4189  return (INT_PTR)TRUE;
4190 
4192  {
4196  PropSheet_Changed(GetParent(hDlg), hDlg);
4197  return (INT_PTR)TRUE;
4198  }
4199 
4201  {
4205  PropSheet_Changed(GetParent(hDlg), hDlg);
4206  return (INT_PTR)TRUE;
4207  }
4208 
4209  case IDC_NO_OVERWRITE:
4210  {
4214  PropSheet_Changed(GetParent(hDlg), hDlg);
4215  return (INT_PTR)TRUE;
4216  }
4217 
4218  case IDC_RESTOREDEFAULTS:
4219  {
4220  LoadStringW(hInst, IDS_RESTOREDEFAULTS, szText, _countof(szText));
4221 
4222  if (MessageBoxW(hDlg, szText, szTitle, MB_YESNO | MB_ICONQUESTION) == IDYES)
4223  {
4225  /* Workstation: 512 KB; Server: 16384 KB */
4226  SetDlgItemInt(hDlg, IDC_EDIT_MAXLOGSIZE, 5120, FALSE);
4230  PropSheet_Changed(GetParent(hDlg), hDlg);
4231  }
4232  return (INT_PTR)TRUE;
4233  }
4234 
4235  case IDHELP:
4236  MessageBoxW(hDlg,
4237  L"Help not implemented yet!",
4238  szTitle,
4240  return (INT_PTR)TRUE;
4241 
4242  default:
4243  break;
4244  }
4245  break;
4246  }
4247 
4248  return (INT_PTR)FALSE;
4249 }
4250 
4251 INT_PTR
4253 {
4254  INT_PTR ret = 0;
4255  PROPSHEETHEADERW psh;
4256  PROPSHEETPAGEW psp[1]; // 2
4257 
4258  /*
4259  * Bail out if there is no available filter, or if the filter
4260  * contains more than one log.
4261  */
4262  if (!EventLogFilter)
4263  return 0;
4264 
4265  EventLogFilter_AddRef(EventLogFilter);
4266 
4267  if (EventLogFilter->NumOfEventLogs > 1 ||
4268  EventLogFilter->EventLogs[0] == NULL)
4269  {
4270  goto Quit;
4271  }
4272 
4273  /* Header */
4274  psh.dwSize = sizeof(psh);
4275  psh.dwFlags = PSH_PROPSHEETPAGE /*| PSH_USEICONID */ | PSH_PROPTITLE | PSH_HASHELP /*| PSH_NOCONTEXTHELP */ /*| PSH_USECALLBACK */;
4276  psh.hInstance = hInstance;
4277  psh.hwndParent = hWndParent;
4278  // psh.pszIcon = MAKEINTRESOURCEW(IDI_APPICON); // Disabled because it only sets the small icon; the big icon is a stretched version of the small one.
4279  psh.pszCaption = EventLogFilter->EventLogs[0]->LogName;
4280  psh.nStartPage = 0;
4281  psh.ppsp = psp;
4282  psh.nPages = ARRAYSIZE(psp);
4283  // psh.pfnCallback = PropSheetCallback;
4284 
4285  /* Log properties page */
4286  psp[0].dwSize = sizeof(psp[0]);
4287  psp[0].dwFlags = PSP_HASHELP;
4288  psp[0].hInstance = hInstance;
4290  psp[0].pfnDlgProc = EventLogPropProc;
4291  psp[0].lParam = (LPARAM)EventLogFilter->EventLogs[0];
4292 
4293 #if 0
4294  /* TODO: Log sources page */
4295  psp[1].dwSize = sizeof(psp[1]);
4296  psp[1].dwFlags = PSP_HASHELP;
4297  psp[1].hInstance = hInstance;
4299  psp[1].pfnDlgProc = GeneralPageWndProc;
4300  psp[1].lParam = (LPARAM)EventLogFilter->EventLogs[0];
4301 #endif
4302 
4303  /* Create the property sheet */
4304  ret = PropertySheetW(&psh);
4305 
4306 Quit:
4307  EventLogFilter_Release(EventLogFilter);
4308  return ret;
4309 }
4310 
4311 /* Message handler for Event Details dialog */
4312 static HWND hWndDetailsCtrl = NULL; // May go into the DWLP_USER
4313 static HWND hWndGrip = NULL;
4314 static INT cxMin, cyMin; // In window coordinates
4315 static INT cxOld, cyOld; // In client coordinates
4316 
4319 {
4320  switch (uMsg)
4321  {
4322  case WM_INITDIALOG:
4323  {
4324  LONG_PTR dwStyle;
4325  RECT rcWnd, rect;
4326  INT iEventItem;
4327 
4329  if (!hWndDetailsCtrl)
4330  {
4331  EndDialog(hDlg, 0);
4332  return (INT_PTR)TRUE;
4333  }
4334 
4335  /* Create a size grip if the dialog has a sizing border */
4336  GetClientRect(hDlg, &rcWnd);
4337  dwStyle = GetWindowLongPtrW(hDlg, GWL_STYLE);
4338  if (dwStyle & WS_THICKFRAME /* == WS_SIZEBOX */)
4339  {
4340  INT sbVXSize = GetSystemMetrics(SM_CXVSCROLL);
4341  INT sbHYSize = GetSystemMetrics(SM_CYHSCROLL);
4342 
4344  NULL,
4346  rcWnd.right - sbVXSize,
4347  rcWnd.bottom - sbHYSize,
4348  sbVXSize, sbHYSize,
4349  hDlg,
4350  NULL,
4351  hInst,
4352  NULL);
4353  }
4354 
4355  // SetWindowLongPtrW(hDlg, DWLP_USER, (LONG_PTR)hWndDetailsCtrl);
4356 
4357  /*
4358  * Compute the minimum window size (in window coordinates) by
4359  * adding the widths/heights of the "Help" and "Close" buttons,
4360  * together with the margins, and add some minimal spacing
4361  * between the buttons.
4362  */
4363  GetWindowRect(hDlg, &rcWnd);
4364  cxMin = cyMin = 0;
4365 
4366  GetWindowRect(GetDlgItem(hDlg, IDHELP), &rect);
4367  cxMin += (rect.right - rect.left) + (rect.left - rcWnd.left); // == (rect.right - rcWnd.left);
4368  cyMin += (rect.bottom - rect.top) + (rcWnd.bottom - rect.bottom); // == (rcWnd.bottom - rect.top);
4369 
4370  GetWindowRect(GetDlgItem(hDlg, IDOK), &rect);
4371  cxMin += (rect.right - rect.left) + (rcWnd.right - rect.right); // == (rcWnd.right - rect.left);
4372  cyMin += (rect.bottom - rect.top) + (rcWnd.bottom - rect.bottom); // == (rcWnd.bottom - rect.top);
4373 
4374  /*
4375  * Convert the window rect from window to client coordinates
4376  * in order to retrieve the sizes of the left and top margins,
4377  * and add some extra space.
4378  */
4379  MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rcWnd, sizeof(RECT)/sizeof(POINT));
4380 
4381  cxMin += -2*rcWnd.left; // Minimal spacing between the buttons == 2 * left margin
4382  cyMin += -rcWnd.top + 12; // Add some space on top
4383 
4384  GetClientRect(hDlg, &rcWnd);
4385  cxOld = rcWnd.right - rcWnd.left;
4386  cyOld = rcWnd.bottom - rcWnd.top;
4387 
4388  /* Show event info in dialog control */
4389  iEventItem = (lParam != 0 ? ((PEVENTDETAIL_INFO)lParam)->iEventItem : 0);
4390  SendMessageW(hWndDetailsCtrl, EVT_DISPLAY, 0, (LPARAM)iEventItem);
4391 
4392  // SetWindowPos(hWndDetailsCtrl, NULL,
4393  // 0, 0,
4394  // (rcWnd.right - rcWnd.left),
4395  // (rcWnd.bottom - rcWnd.top),
4396  // SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
4397 
4398  /*
4399  * Hide the placeholder static control and show the event details
4400  * control instead. Note that the placeholder is here so far just
4401  * to get the dimensions right in the dialog resource editor.
4402  * I plan to remove it and use a custom control with a suitable
4403  * window class for it, that would create the event details control
4404  * instead.
4405  */
4408  return (INT_PTR)TRUE;
4409  }
4410 
4411  case WM_DESTROY:
4415  return (INT_PTR)TRUE;
4416 
4417  case WM_COMMAND:
4418  switch (LOWORD(wParam))
4419  {
4420  case IDOK:
4421  case IDCANCEL:
4422  EndDialog(hDlg, LOWORD(wParam));
4423  return (INT_PTR)TRUE;
4424 
4425  case IDHELP:
4426  MessageBoxW(hDlg,
4427  L"Help not implemented yet!",
4428  szTitle,
4430  return (INT_PTR)TRUE;
4431 
4432  default:
4433  break;
4434  }
4435  break;
4436 
4437  case WM_SETCURSOR:
4438  if (((HWND)wParam == hWndGrip) && (LOWORD(lParam) == HTCLIENT))
4439  {
4442  return (INT_PTR)TRUE;
4443  }
4444  break;
4445 
4446  case WM_SIZING:
4447  {
4448  /* Forbid resizing the dialog smaller than its minimal size */
4449  PRECT dragRect = (PRECT)lParam;
4450 
4451  if ((wParam == WMSZ_LEFT) || (wParam == WMSZ_TOPLEFT) || (wParam == WMSZ_BOTTOMLEFT))
4452  {
4453  if (dragRect->right - dragRect->left < cxMin)
4454  dragRect->left = dragRect->right - cxMin;
4455  }
4456 
4457  if ((wParam == WMSZ_RIGHT) || (wParam == WMSZ_TOPRIGHT) || (wParam == WMSZ_BOTTOMRIGHT))
4458  {
4459  if (dragRect->right - dragRect->left < cxMin)
4460  dragRect->right = dragRect->left + cxMin;
4461  }
4462 
4463  if ((wParam == WMSZ_TOP) || (wParam == WMSZ_TOPLEFT) || (wParam == WMSZ_TOPRIGHT))
4464  {
4465  if (dragRect->bottom - dragRect->top < cyMin)
4466  dragRect->top = dragRect->bottom - cyMin;
4467  }
4468 
4470  {
4471  if (dragRect->bottom - dragRect->top < cyMin)
4472  dragRect->bottom = dragRect->top + cyMin;
4473  }
4474 
4476  return (INT_PTR)TRUE;
4477  }
4478 
4479  case WM_SIZE:
4480  {
4481  INT cx = LOWORD(lParam);
4482  INT cy = HIWORD(lParam);
4483 
4484  HDWP hdwp;
4485  HWND hItemWnd;
4486  RECT rect;
4487 
4488  hdwp = BeginDeferWindowPos(4);
4489 
4490  /* Resize the event details control window */
4491 
4492  hItemWnd = hWndDetailsCtrl;
4493  GetWindowRect(hItemWnd, &rect);
4494  MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
4495 
4496  if (hdwp)
4497  hdwp = DeferWindowPos(hdwp,
4498  hItemWnd,
4499  HWND_TOP,
4500  0, 0,
4501  (rect.right - rect.left) + (cx - cxOld),
4502  (rect.bottom - rect.top) + (cy - cyOld),
4504 
4505  /* Move the buttons */
4506 
4507  hItemWnd = GetDlgItem(hDlg, IDHELP);
4508  GetWindowRect(hItemWnd, &rect);
4509  MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
4510 
4511  if (hdwp)
4512  hdwp = DeferWindowPos(hdwp,
4513  hItemWnd,
4514  HWND_TOP,
4515  rect.left,
4516  rect.top + (cy - cyOld),
4517  0, 0,
4519 
4520  hItemWnd = GetDlgItem(hDlg, IDOK);
4521  GetWindowRect(hItemWnd, &rect);
4522  MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
4523 
4524  if (hdwp)
4525  hdwp = DeferWindowPos(hdwp,
4526  hItemWnd,
4527  HWND_TOP,
4528  rect.left + (cx - cxOld),
4529  rect.top + (cy - cyOld),
4530  0, 0,
4532 
4533  /* Move the size grip */
4534  if (hWndGrip && hdwp)
4535  {
4537  MapWindowPoints(HWND_DESKTOP /*NULL*/, hDlg, (LPPOINT)&rect, sizeof(RECT)/sizeof(POINT));
4538 
4539  hdwp = DeferWindowPos(hdwp,
4540  hWndGrip,
4541  HWND_TOP,
4542  rect.left + (cx - cxOld),
4543  rect.top + (cy - cyOld),
4544  0, 0,
4546  }
4547 
4548  if (hdwp)
4549  EndDeferWindowPos(hdwp);
4550 
4551  /* Hide the size grip if we are in maximized mode */
4552  if (hWndGrip)
4554 
4555  cxOld = cx;
4556  cyOld = cy;
4557 
4559  return (INT_PTR)TRUE;
4560  }
4561  }
4562 
4563  return (INT_PTR)FALSE;
4564 }
struct _LARGE_INTEGER::@2253 u
_SEH2_TRY
Definition: create.c:4226
#define IDC_SIZEWE
Definition: winuser.h:689
#define WS_CLIPSIBLINGS
Definition: pedump.c:618
LPWSTR lpThousandSep
Definition: winnls.h:646
#define STATUSCLASSNAMEW
Definition: commctrl.h:1936