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