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