EventViewer.cpp
1 #include "EventViewer.h" 2 3 #include <windows.h> 4 #include <sddl.h> 5 #include <stdio.h> 6 #include <winevt.h> 7 #include <fstream> 8 #include <string> 9 #include <common/utils/winapi_error.h> 10 11 #include "XmlDocumentEx.h" 12 13 extern std::vector<std::wstring> processes; 14 15 namespace 16 { 17 // Batch size for number of events queried at once 18 constexpr int BATCH_SIZE = 50; 19 20 class EventViewerReporter 21 { 22 private: 23 // Report last 30 days 24 const long long PERIOD = 10 * 24 * 3600ll * 1000; 25 26 const std::wstring QUERY_BY_PROCESS = L"<QueryList>" \ 27 L" <Query Id='0'>" \ 28 L" <Select Path='Application'>" \ 29 L" *[System[TimeCreated[timediff(@SystemTime)<%I64u]]] " \ 30 L" and *[EventData[Data and (Data='%s')]]" \ 31 L" </Select>" \ 32 L" </Query>" \ 33 L"</QueryList>"; 34 35 const std::wstring QUERY_BY_CHANNEL = L"<QueryList>" \ 36 L" <Query Id='0'>" \ 37 L" <Select Path='%s'>" \ 38 L" *[System[TimeCreated[timediff(@SystemTime)<%I64u]]]" \ 39 L" </Select>" \ 40 L" </Query>" \ 41 L"</QueryList>"; 42 43 44 std::wstring GetQuery(std::wstring processName) 45 { 46 wchar_t buff[1000]; 47 memset(buff, 0, sizeof(buff)); 48 _snwprintf_s(buff, sizeof(buff), QUERY_BY_PROCESS.c_str(), PERIOD, processName.c_str()); 49 return buff; 50 } 51 52 std::wstring GetQueryByChannel(std::wstring channelName) 53 { 54 wchar_t buff[1000]; 55 memset(buff, 0, sizeof(buff)); 56 _snwprintf_s(buff, sizeof(buff), QUERY_BY_CHANNEL.c_str(), channelName.c_str(), PERIOD); 57 return buff; 58 } 59 60 std::wofstream report; 61 EVT_HANDLE hResults; 62 bool isChannel; 63 64 bool ShouldIncludeEvent(const std::wstring& eventXml) 65 { 66 if (!isChannel) 67 { 68 return true; // Include all events if no filtering 69 } 70 71 // Check if the event contains PowerToys or CommandPalette 72 return (eventXml.find(L"PowerToys") != std::wstring::npos || 73 eventXml.find(L"CommandPalette") != std::wstring::npos); 74 } 75 76 void PrintEvent(EVT_HANDLE hEvent) 77 { 78 DWORD status = ERROR_SUCCESS; 79 DWORD dwBufferSize = 0; 80 DWORD dwBufferUsed = 0; 81 DWORD dwPropertyCount = 0; 82 LPWSTR pRenderedContent = NULL; 83 84 // The EvtRenderEventXml flag tells EvtRender to render the event as an XML string. 85 if (!EvtRender(NULL, hEvent, EvtRenderEventXml, dwBufferSize, pRenderedContent, &dwBufferUsed, &dwPropertyCount)) 86 { 87 if (ERROR_INSUFFICIENT_BUFFER == (status = GetLastError())) 88 { 89 dwBufferSize = dwBufferUsed; 90 pRenderedContent = static_cast<LPWSTR>(malloc(dwBufferSize)); 91 if (pRenderedContent) 92 { 93 EvtRender(NULL, hEvent, EvtRenderEventXml, dwBufferSize, pRenderedContent, &dwBufferUsed, &dwPropertyCount); 94 } 95 } 96 97 if (ERROR_SUCCESS != (status = GetLastError())) 98 { 99 report << std::endl << L"EvtRender failed with " << get_last_error_or_default(GetLastError()) << std::endl << std::endl; 100 if (pRenderedContent) 101 { 102 free(pRenderedContent); 103 } 104 return; 105 } 106 } 107 108 // Apply filtering if needed 109 std::wstring eventContent(pRenderedContent); 110 if (!ShouldIncludeEvent(eventContent)) 111 { 112 if (pRenderedContent) 113 { 114 free(pRenderedContent); 115 } 116 return; // Skip this event 117 } 118 119 XmlDocumentEx doc; 120 doc.LoadXml(pRenderedContent); 121 std::wstring formattedXml = L""; 122 try 123 { 124 formattedXml = doc.GetFormatedXml(); 125 } 126 catch (...) 127 { 128 formattedXml = pRenderedContent; 129 } 130 131 report << std::endl << formattedXml << std::endl; 132 if (pRenderedContent) 133 { 134 free(pRenderedContent); 135 } 136 } 137 138 // Enumerate all the events in the result set. 139 void PrintResults(EVT_HANDLE results) 140 { 141 DWORD status = ERROR_SUCCESS; 142 EVT_HANDLE hEvents[BATCH_SIZE]; 143 DWORD dwReturned = 0; 144 145 while (true) 146 { 147 // Get a block of events from the result set. 148 if (!EvtNext(results, BATCH_SIZE, hEvents, INFINITE, 0, &dwReturned)) 149 { 150 if (ERROR_NO_MORE_ITEMS != (status = GetLastError())) 151 { 152 report << L"EvtNext failed with " << status << std::endl; 153 } 154 155 break; 156 } 157 158 // For each event, call the PrintEvent function which renders the 159 // event for display. PrintEvent is shown in RenderingEvents. 160 for (DWORD i = 0; i < dwReturned; i++) 161 { 162 PrintEvent(hEvents[i]); 163 } 164 } 165 166 for (DWORD i = 0; i < dwReturned; i++) 167 { 168 if (nullptr != hEvents[i]) 169 EvtClose(hEvents[i]); 170 } 171 } 172 173 public: 174 EventViewerReporter(const std::filesystem::path& tmpDir, std::wstring queryName, std::wstring fileName, bool isChannel) 175 :isChannel(isChannel) 176 { 177 std::wstring query = L""; 178 if (isChannel) 179 { 180 query = GetQueryByChannel(queryName); 181 } 182 else 183 { 184 query = GetQuery(queryName); 185 } 186 187 auto reportPath = tmpDir; 188 reportPath.append(L"EventViewer-" + fileName + L".xml"); 189 report = std::wofstream(reportPath); 190 191 hResults = EvtQuery(NULL, NULL, query.c_str(), EvtQueryChannelPath); 192 if (NULL == hResults) 193 { 194 report << "Failed to report info for " << queryName << ". " << get_last_error_or_default(GetLastError()) << std::endl; 195 return; 196 } 197 } 198 199 ~EventViewerReporter() 200 { 201 if (hResults) 202 { 203 EvtClose(hResults); 204 hResults = nullptr; 205 } 206 } 207 208 void Report() 209 { 210 try 211 { 212 if (hResults) 213 { 214 PrintResults(hResults); 215 } 216 } 217 catch (...) 218 { 219 report << "Failed to report info" << std::endl; 220 } 221 } 222 }; 223 } 224 225 void EventViewer::ReportEventViewerInfo(const std::filesystem::path& tmpDir) 226 { 227 for (auto& process : processes) 228 { 229 EventViewerReporter(tmpDir, process, process, false).Report(); 230 } 231 } 232 233 void EventViewer::ReportAppXDeploymentLogs(const std::filesystem::path& tmpDir) 234 { 235 EventViewerReporter(tmpDir, L"Microsoft-Windows-AppXDeploymentServer/Operational", L"AppXDeploymentServerEventLog", true).Report(); 236 }