/ tools / BugReportTool / BugReportTool / EventViewer.cpp
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)&lt;%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)&lt;%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  }