[EVENTVWR]
authorKamil Hornicek <kamil.hornicek@reactos.org>
Tue, 7 Feb 2017 22:33:02 +0000 (22:33 +0000)
committerKamil Hornicek <kamil.hornicek@reactos.org>
Tue, 7 Feb 2017 22:33:02 +0000 (22:33 +0000)
- Attempt to speed up loading and displaying logs in EnumEventsThread:
- Rewriting the code fix an infinite loop that could happen under low memory conditions (this potentially speeds things up quite a lot).
- Remove ListView subclassing - sorry Hermès but not only it keeps constantly redrawing under Windows but also adds too much overhead.
- Don't read the event log records one by one but in chunks of roughly 0x7ffff bytes (maximum size allowed).
- Use poor man's "caching" of the event user name (optimizing/caching rest of the stuff in the loop didn't yield any significant speed up).

Result: Loading ~40000 records under Win7 went from 4.5 minutes to 65 seconds.

svn path=/trunk/; revision=73749

reactos/base/applications/mscutils/eventvwr/eventvwr.c

index 6ab4218..31cecd4 100644 (file)
@@ -124,7 +124,6 @@ VOID FreeLogFilterList(VOID);
 
 ATOM MyRegisterClass(HINSTANCE);
 BOOL InitInstance(HINSTANCE, int);
-VOID ExitInstance(HINSTANCE);
 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
 INT_PTR EventLogProperties(HINSTANCE, HWND, PEVENTLOGFILTER);
 INT_PTR CALLBACK EventDetails(HWND, UINT, WPARAM, LPARAM);
@@ -227,8 +226,6 @@ Cleanup:
     if (hStartStopEnumEvent)
         CloseHandle(hStartStopEnumEvent);
 
-    ExitInstance(hInstance);
-
 Quit:
     FreeLibrary(hRichEdit);
 
@@ -1091,9 +1088,10 @@ Quit:
     if (!Success)
     {
         if (pevlr->EventCategory != 0)
+        {
             StringCchPrintfW(CategoryName, MAX_PATH, L"(%lu)", pevlr->EventCategory);
-        else
-            LoadStringW(hInst, IDS_NONE, CategoryName, MAX_PATH);
+            Success = TRUE;
+        }
     }
 
     return Success;
@@ -1263,18 +1261,30 @@ GetEventType(IN WORD dwEventType,
 
 BOOL
 GetEventUserName(IN PEVENTLOGRECORD pelr,
+                 IN OUT PSID pLastSid,
                  OUT PWCHAR pszUser) // TODO: Add IN DWORD BufLen
 {
-    PSID pSid;
+    PSID pCurrentSid;
     PWSTR StringSid;
     WCHAR szName[1024];
     WCHAR szDomain[1024];
     SID_NAME_USE peUse;
     DWORD cchName = ARRAYSIZE(szName);
     DWORD cchDomain = ARRAYSIZE(szDomain);
+    BOOL Success;
 
     /* Point to the SID */
-    pSid = (PSID)((LPBYTE)pelr + pelr->UserSidOffset);
+    pCurrentSid = (PSID)((LPBYTE)pelr + pelr->UserSidOffset);
+
+    if (!IsValidSid(pCurrentSid))
+    {
+        pLastSid = NULL;
+        return FALSE;
+    }
+    else if (pLastSid && EqualSid(pLastSid, pCurrentSid))
+    {
+        return TRUE;
+    }
 
     /* User SID */
     if (pelr->UserSidLength > 0)
@@ -1286,7 +1296,7 @@ GetEventUserName(IN PEVENTLOGRECORD pelr,
          * 'pszUser', otherwise we return an error.
          */
         if (LookupAccountSidW(NULL, // FIXME: Use computer name? From the particular event?
-                              pSid,
+                              pCurrentSid,
                               szName,
                               &cchName,
                               szDomain,
@@ -1294,13 +1304,11 @@ GetEventUserName(IN PEVENTLOGRECORD pelr,
                               &peUse))
         {
             StringCchCopyW(pszUser, MAX_PATH, szName);
-            return TRUE;
+            Success = TRUE;
         }
-        else if (ConvertSidToStringSidW(pSid, &StringSid))
+        else if (ConvertSidToStringSidW(pCurrentSid, &StringSid))
         {
-            BOOL Success;
-
-            /* Copy the string only if the user-provided buffer is small enough */
+            /* Copy the string only if the user-provided buffer is big enough */
             if (wcslen(StringSid) + 1 <= MAX_PATH) // + 1 for NULL-terminator
             {
                 StringCchCopyW(pszUser, MAX_PATH, StringSid);
@@ -1314,12 +1322,12 @@ GetEventUserName(IN PEVENTLOGRECORD pelr,
 
             /* Free the allocated buffer */
             LocalFree(StringSid);
-
-            return Success;
         }
     }
 
-    return FALSE;
+    pLastSid = Success ? pCurrentSid : NULL;
+
+    return Success;
 }
 
 
@@ -1422,14 +1430,18 @@ EnumEventsThread(IN LPVOID lpParameter)
 
     ULONG LogIndex;
     HANDLE hEventLog;
-    PEVENTLOGRECORD pevlr, pevlrTmp = NULL;
-    DWORD dwRead, dwNeeded; // , dwThisRecord;
+    PEVENTLOGRECORD pEvlr;
+    PBYTE pEvlrEnd;
+    PBYTE pEvlrBuffer;
+    DWORD dwWanted, dwRead, dwNeeded, dwStatus = ERROR_SUCCESS;
     DWORD dwTotalRecords = 0, dwCurrentRecord = 0;
     DWORD dwFlags, dwMaxLength;
     size_t cchRemaining;
     LPWSTR lpszSourceName;
     LPWSTR lpszComputerName;
     BOOL bResult = TRUE; /* Read succeeded */
+    HANDLE hProcessHeap = GetProcessHeap();
+    PSID pLastSid = NULL;
 
     UINT uStep = 0, uStepAt = 0, uPos = 0;
 
@@ -1441,7 +1453,9 @@ EnumEventsThread(IN LPVOID lpParameter)
     WCHAR szEventTypeText[MAX_LOADSTRING];
     WCHAR szCategoryID[MAX_PATH];
     WCHAR szUsername[MAX_PATH];
+    WCHAR szNoUsername[MAX_PATH];
     WCHAR szCategory[MAX_PATH];
+    WCHAR szNoCategory[MAX_PATH];
     PWCHAR lpTitleTemplateEnd;
 
     SYSTEMTIME time;
@@ -1500,131 +1514,119 @@ EnumEventsThread(IN LPVOID lpParameter)
     }
 
     /* Set up the event records cache */
-    g_RecordPtrs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwTotalRecords * sizeof(*g_RecordPtrs));
+    g_RecordPtrs = HeapAlloc(hProcessHeap, HEAP_ZERO_MEMORY, dwTotalRecords * sizeof(*g_RecordPtrs));
     if (!g_RecordPtrs)
     {
         // ShowLastWin32Error();
-        goto Cleanup;
+        goto Quit;
     }
     g_TotalRecords = dwTotalRecords;
 
     if (WaitForSingleObject(hStopEnumEvent, 0) == WAIT_OBJECT_0)
         goto Quit;
 
+    LoadStringW(hInst, IDS_NOT_AVAILABLE, szNoUsername, ARRAYSIZE(szNoUsername));
+    LoadStringW(hInst, IDS_NONE, szNoCategory, ARRAYSIZE(szNoCategory));
+
     ProgressBar_SetRange(hwndStatusProgress, MAKELPARAM(0, 100));
     uStepAt = (dwTotalRecords / 100) + 1;
 
-    dwFlags = EVENTLOG_SEQUENTIAL_READ |
-             (NewestEventsFirst ? EVENTLOG_FORWARDS_READ
-                                : EVENTLOG_BACKWARDS_READ);
+    dwFlags = EVENTLOG_SEQUENTIAL_READ | (NewestEventsFirst ? EVENTLOG_FORWARDS_READ : EVENTLOG_BACKWARDS_READ);
+
+    /* 0x7ffff is the maximum buffer size ReadEventLog will accept */
+    dwWanted = 0x7ffff;
+    pEvlr = HeapAlloc(hProcessHeap, 0, dwWanted);
+
+    if (!pEvlr)
+        goto Quit;
 
-    while (dwCurrentRecord < dwTotalRecords)
+    while (dwStatus == ERROR_SUCCESS)
     {
-        //
-        // NOTE: We always allocate the minimum size for 1 record, and if
-        // the ReadEventLog call fails (it always will anyway), we reallocate
-        // the record pointer to be able to hold just 1 record, and then we
-        // redo that for each event.
-        // This is obviously not at all efficient (in terms of numbers of
-        // ReadEventLog calls), since ReadEventLog can fill the buffer with
-        // as many records they can fit completely in the buffer.
-        //
+        bResult =  ReadEventLogW(hEventLog, dwFlags, 0, pEvlr, dwWanted, &dwRead, &dwNeeded);
+        dwStatus = GetLastError();
 
-        pevlr = HeapAlloc(GetProcessHeap(), 0, sizeof(*pevlr));
-        if (!pevlr)
+        if (!bResult && dwStatus == ERROR_INSUFFICIENT_BUFFER)
         {
-            /* Cannot allocate, just skip the event */
-            g_RecordPtrs[dwCurrentRecord] = NULL;
-            // --dwTotalRecords;
-            continue;
+            pEvlr = HeapReAlloc(hProcessHeap, 0, pEvlr, dwNeeded);
+            dwWanted = dwNeeded;
+
+            if (!pEvlr)
+                break;
+
+            bResult =  ReadEventLogW(hEventLog, dwFlags, 0, pEvlr, dwNeeded, &dwRead, &dwNeeded);
+
+            if (!bResult)
+                break;
         }
-        g_RecordPtrs[dwCurrentRecord] = pevlr;
-
-        bResult = ReadEventLogW(hEventLog,  // Event log handle
-                                dwFlags,    // Sequential read
-                                0,          // Ignored for sequential read
-                                pevlr,      // Pointer to buffer
-                                sizeof(*pevlr),   // Size of buffer
-                                &dwRead,    // Number of bytes read
-                                &dwNeeded); // Bytes in the next record
-        if (!bResult && (GetLastError () == ERROR_INSUFFICIENT_BUFFER))
+        else if (!bResult)
         {
-            pevlrTmp = HeapReAlloc(GetProcessHeap(), 0, pevlr, dwNeeded);
-            if (!pevlrTmp)
-            {
-                /* Cannot reallocate, just skip the event */
-                HeapFree(GetProcessHeap(), 0, pevlr);
-                g_RecordPtrs[dwCurrentRecord] = NULL;
-                // --dwTotalRecords;
-                continue;
-            }
-            pevlr = pevlrTmp;
-            g_RecordPtrs[dwCurrentRecord] = pevlr;
-
-            ReadEventLogW(hEventLog,  // event log handle
-                          dwFlags,    // read flags
-                          0,          // offset; default is 0
-                          pevlr,      // pointer to buffer
-                          dwNeeded,   // size of buffer
-                          &dwRead,    // number of bytes read
-                          &dwNeeded); // bytes in next record
+            /* exit on other errors (ERROR_HANDLE_EOF) */
+            break;
         }
 
-        while (dwRead > 0)
+        pEvlrBuffer = (LPBYTE)pEvlr;
+        pEvlrEnd = pEvlrBuffer + dwRead;
+
+        while (pEvlrBuffer < pEvlrEnd)
         {
+            PEVENTLOGRECORD pEvlrTmp = (PEVENTLOGRECORD)pEvlrBuffer;
+            PWSTR lpszUsername, lpszCategoryName;
+            g_RecordPtrs[dwCurrentRecord] = NULL;
+
             // ProgressBar_StepIt(hwndStatusProgress);
             uStep++;
-            if(uStep % uStepAt == 0)
+            if (uStep % uStepAt == 0)
             {
                 ++uPos;
                 ProgressBar_SetPos(hwndStatusProgress, uPos);
             }
 
-            if (WaitForSingleObject(hStopEnumEvent, 0) == WAIT_OBJECT_0)
+           if (WaitForSingleObject(hStopEnumEvent, 0) == WAIT_OBJECT_0)
                 goto Quit;
 
             /* Filter by event type */
-            if (!FilterByType(EventLogFilter, pevlr))
+            if (!FilterByType(EventLogFilter, pEvlrTmp))
                 goto SkipEvent;
 
             /* Get the event source name and filter it */
-            lpszSourceName = (LPWSTR)((LPBYTE)pevlr + sizeof(EVENTLOGRECORD));
+            lpszSourceName = (LPWSTR)(pEvlrBuffer + sizeof(EVENTLOGRECORD));
             if (!FilterByString(EventLogFilter->Sources, lpszSourceName))
                 goto SkipEvent;
 
             /* Get the computer name and filter it */
-            lpszComputerName = (LPWSTR)((LPBYTE)pevlr + sizeof(EVENTLOGRECORD) + (wcslen(lpszSourceName) + 1) * sizeof(WCHAR));
+            lpszComputerName = (LPWSTR)((LPBYTE)pEvlrBuffer + sizeof(EVENTLOGRECORD) + (wcslen(lpszSourceName) + 1) * sizeof(WCHAR));
             if (!FilterByString(EventLogFilter->ComputerNames, lpszComputerName))
                 goto SkipEvent;
 
             /* Compute the event time */
-            EventTimeToSystemTime(pevlr->TimeWritten, &time);
+            EventTimeToSystemTime(pEvlrTmp->TimeWritten, &time);
             GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &time, NULL, szLocalDate, ARRAYSIZE(szLocalDate));
             GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &time, NULL, szLocalTime, ARRAYSIZE(szLocalTime));
 
-            LoadStringW(hInst, IDS_NOT_AVAILABLE, szUsername, ARRAYSIZE(szUsername));
-            LoadStringW(hInst, IDS_NONE, szCategory, ARRAYSIZE(szCategory));
-
             /* Get the username that generated the event, and filter it */
-            GetEventUserName(pevlr, szUsername);
-            if (!FilterByString(EventLogFilter->Users, szUsername))
+            lpszUsername = GetEventUserName(pEvlrTmp, pLastSid, szUsername) ? szUsername : szNoUsername;
+
+            if (!FilterByString(EventLogFilter->Users, lpszUsername))
                 goto SkipEvent;
 
             // TODO: Filter by event ID and category
+            GetEventType(pEvlrTmp->EventType, szEventTypeText);
 
-            GetEventType(pevlr->EventType, szEventTypeText);
-            GetEventCategory(EventLog->LogName, lpszSourceName, pevlr, szCategory);
+            lpszCategoryName = GetEventCategory(EventLog->LogName, lpszSourceName, pEvlrTmp, szCategory) ? szCategory : szNoCategory;
 
-            StringCbPrintfW(szEventID, sizeof(szEventID), L"%u", (pevlr->EventID & 0xFFFF));
-            StringCbPrintfW(szCategoryID, sizeof(szCategoryID), L"%u", pevlr->EventCategory);
+            StringCbPrintfW(szEventID, sizeof(szEventID), L"%u", (pEvlrTmp->EventID & 0xFFFF));
+            StringCbPrintfW(szCategoryID, sizeof(szCategoryID), L"%u", pEvlrTmp->EventCategory);
+
+            g_RecordPtrs[dwCurrentRecord] = HeapAlloc(hProcessHeap, 0, pEvlrTmp->Length);
+            RtlCopyMemory(g_RecordPtrs[dwCurrentRecord], pEvlrTmp, pEvlrTmp->Length);
 
             lviEventItem.mask = LVIF_IMAGE | LVIF_TEXT | LVIF_PARAM;
             lviEventItem.iItem = 0;
             lviEventItem.iSubItem = 0;
-            lviEventItem.lParam = (LPARAM)pevlr;
+            lviEventItem.lParam = (LPARAM)g_RecordPtrs[dwCurrentRecord];
             lviEventItem.pszText = szEventTypeText;
 
-            switch (pevlr->EventType)
+            switch (pEvlrTmp->EventType)
             {
                 case EVENTLOG_SUCCESS:
                 case EVENTLOG_INFORMATION_TYPE:
@@ -1653,21 +1655,22 @@ EnumEventsThread(IN LPVOID lpParameter)
             ListView_SetItemText(hwndListView, lviEventItem.iItem, 1, szLocalDate);
             ListView_SetItemText(hwndListView, lviEventItem.iItem, 2, szLocalTime);
             ListView_SetItemText(hwndListView, lviEventItem.iItem, 3, lpszSourceName);
-            ListView_SetItemText(hwndListView, lviEventItem.iItem, 4, szCategory);
+            ListView_SetItemText(hwndListView, lviEventItem.iItem, 4, lpszCategoryName);
             ListView_SetItemText(hwndListView, lviEventItem.iItem, 5, szEventID);
-            ListView_SetItemText(hwndListView, lviEventItem.iItem, 6, szUsername);
+            ListView_SetItemText(hwndListView, lviEventItem.iItem, 6, lpszUsername);
             ListView_SetItemText(hwndListView, lviEventItem.iItem, 7, lpszComputerName);
 
 SkipEvent:
-            dwRead -= pevlr->Length;
-            pevlr = (PEVENTLOGRECORD)((LPBYTE) pevlr + pevlr->Length);
+            pEvlrBuffer += pEvlrTmp->Length;
+            dwCurrentRecord++;
         }
-
-        dwCurrentRecord++;
     }
 
 Quit:
 
+    if (pEvlr)
+        HeapFree(hProcessHeap, 0, pEvlr);
+
     /* Close the event log */
     CloseEventLog(hEventLog);
 
@@ -2405,135 +2408,6 @@ FreeLogFilterList(VOID)
     return;
 }
 
-
-/*
- * ListView subclassing to handle WM_PAINT messages before and after they are
- * handled by the ListView window itself. We cannot use at this level the
- * custom-drawn notifications that are more suitable for drawing elements
- * inside the ListView.
- */
-static WNDPROC orgListViewWndProc = NULL;
-static BOOL IsLoading = FALSE;
-
-LRESULT CALLBACK
-ListViewWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
-{
-    switch (uMsg)
-    {
-        case LVM_PROGRESS:
-        {
-            /* TRUE: Create the dialog; FALSE: Destroy the dialog */
-            IsLoading = !!(BOOL)lParam;
-            return IsLoading;
-        }
-
-        case WM_PAINT:
-        {
-            /* This code is adapted from: http://www.codeproject.com/Articles/216/Indicating-an-empty-ListView */
-
-            int nItemCount;
-
-            PAINTSTRUCT ps;
-            HDC hDC;
-            HWND hwndHeader;
-            RECT rc, rcH;
-            COLORREF crTextOld, crTextBkOld;
-            NONCLIENTMETRICSW ncm;
-            HFONT hFont, hFontOld;
-            LPWSTR lpszString;
-
-            nItemCount = ListView_GetItemCount(hWnd);
-            if (!IsLoading && nItemCount > 0)
-                break;
-
-            /*
-             * NOTE:
-             * We could have used lpNMCustomDraw->nmcd.rc for the rectangle,
-             * but this one actually holds the rectangle of the list view
-             * that is being currently repainted, so that it can be smaller
-             * than the list view proper. This is especially true when using
-             * COMCTL32.DLL version <= 6.0 .
-             */
-
-            GetClientRect(hWnd, &rc);
-            hwndHeader = ListView_GetHeader(hWnd);
-            if (hwndHeader)
-            {
-                /* Note that we could also use Header_GetItemRect() */
-                GetClientRect(hwndHeader, &rcH);
-                rc.top += rcH.bottom;
-            }
-
-            /* Add some space between the top of the list view and the text */
-            rc.top += 10;
-
-            BeginPaint(hWnd, &ps);
-            /*
-             * NOTE: Using a secondary hDC (and not the ps.hdc) gives the strange
-             * property that the text is always recentered on the current view of
-             * the window, instead of being scrolled together with the contents of
-             * the list view...
-             */
-            // hDC = ps.hdc;
-            hDC = GetDC(hWnd);
-
-            /*
-             * NOTE: We could have kept lpNMCustomDraw->clrText and
-             * lpNMCustomDraw->clrTextBk, but they usually do not contain
-             * the correct default colors for the items / default text.
-             */
-            crTextOld =
-                SetTextColor(hDC, GetSysColor(COLOR_WINDOWTEXT));
-            crTextBkOld =
-                SetBkColor(hDC, GetSysColor(COLOR_WINDOW));
-
-            // FIXME: Cache the font?
-            ncm.cbSize = sizeof(ncm);
-            hFont = NULL;
-            if (SystemParametersInfoW(SPI_GETNONCLIENTMETRICS,
-                                      sizeof(ncm), &ncm, 0))
-            {
-                hFont = CreateFontIndirectW(&ncm.lfMessageFont);
-            }
-            if (!hFont)
-                hFont = GetStockFont(DEFAULT_GUI_FONT);
-
-            hFontOld = (HFONT)SelectObject(hDC, hFont);
-
-            FillRect(hDC, &rc, GetSysColorBrush(COLOR_WINDOW));
-
-            if (nItemCount <= 0)
-                lpszString = szEmptyList;
-            else // if (IsLoading)
-                lpszString = szLoadingWait;
-
-            DrawTextW(hDC,
-                      lpszString,
-                      -1,
-                      &rc,
-                      DT_CENTER | DT_WORDBREAK | DT_NOPREFIX | DT_NOCLIP);
-
-            SelectObject(hDC, hFontOld);
-            if (hFont)
-                DeleteObject(hFont);
-
-            SetBkColor(hDC, crTextBkOld);
-            SetTextColor(hDC, crTextOld);
-
-            ReleaseDC(hWnd, hDC);
-            EndPaint(hWnd, &ps);
-
-            break;
-        }
-
-        // case WM_ERASEBKGND:
-        //     break;
-    }
-
-    /* Continue with default message processing */
-    return CallWindowProcW(orgListViewWndProc, hWnd, uMsg, wParam, lParam);
-}
-
 BOOL
 InitInstance(HINSTANCE hInstance,
              int nCmdShow)
@@ -2748,11 +2622,6 @@ InitInstance(HINSTANCE hInstance,
     lvc.pszText = szTemp;
     ListView_InsertColumn(hwndListView, 7, &lvc);
 
-    /* Subclass the ListView */
-    // orgListViewWndProc = SubclassWindow(hwndListView, ListViewWndProc);
-    orgListViewWndProc = (WNDPROC)(LONG_PTR)GetWindowLongPtrW(hwndListView, GWLP_WNDPROC);
-    SetWindowLongPtrW(hwndListView, GWLP_WNDPROC, (LONG_PTR)ListViewWndProc);
-
     /* Initialize the save Dialog */
     ZeroMemory(&sfn, sizeof(sfn));
     ZeroMemory(szSaveFilter, sizeof(szSaveFilter));
@@ -2773,15 +2642,6 @@ InitInstance(HINSTANCE hInstance,
     return TRUE;
 }
 
-VOID
-ExitInstance(HINSTANCE hInstance)
-{
-    /* Restore the original ListView WndProc */
-    // SubclassWindow(hwndListView, orgListViewWndProc);
-    SetWindowLongPtrW(hwndListView, GWLP_WNDPROC, (LONG_PTR)orgListViewWndProc);
-    orgListViewWndProc = NULL;
-}
-
 VOID ResizeWnd(INT cx, INT cy)
 {
     HDWP hdwp;