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