diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt
index 966e7ad8fc..dbd8b04619 100644
--- a/.github/actions/spell-check/expect.txt
+++ b/.github/actions/spell-check/expect.txt
@@ -541,6 +541,7 @@ EUQ
evenodd
eventlog
everytime
+evt
EWXFORCE
EWXFORCEIFHUNG
EWXLOGOFF
@@ -954,6 +955,7 @@ IVector
IView
IVirtual
IWeb
+IXml
ixx
IZone
IZoom
@@ -1785,6 +1787,7 @@ sln
SMALLICON
SMTO
snd
+snwprintf
softline
somil
Soref
@@ -1944,6 +1947,7 @@ THISCOMPONENT
thre
tif
TILEDWINDOW
+timediff
TIMERID
timeunion
timeutil
@@ -2127,6 +2131,7 @@ webpack
webpage
website
wekyb
+Wevtapi
Whichdoes
whitespaces
WIC
@@ -2156,6 +2161,7 @@ windowwalker
winerror
WINEVENT
winexe
+winevt
winforms
winfx
winget
diff --git a/tools/BugReportTool/BugReportTool/BugReportTool.vcxproj b/tools/BugReportTool/BugReportTool/BugReportTool.vcxproj
index 562580b463..ed31bd4245 100644
--- a/tools/BugReportTool/BugReportTool/BugReportTool.vcxproj
+++ b/tools/BugReportTool/BugReportTool/BugReportTool.vcxproj
@@ -30,15 +30,18 @@
Console
+ Wevtapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
TurnOffAllWarnings
+
+
@@ -52,9 +55,11 @@
+
+
diff --git a/tools/BugReportTool/BugReportTool/BugReportTool.vcxproj.filters b/tools/BugReportTool/BugReportTool/BugReportTool.vcxproj.filters
index 920c9eff95..d7099bdaac 100644
--- a/tools/BugReportTool/BugReportTool/BugReportTool.vcxproj.filters
+++ b/tools/BugReportTool/BugReportTool/BugReportTool.vcxproj.filters
@@ -11,6 +11,8 @@
+
+
@@ -26,5 +28,7 @@
+
+
\ No newline at end of file
diff --git a/tools/BugReportTool/BugReportTool/EventViewer.cpp b/tools/BugReportTool/BugReportTool/EventViewer.cpp
new file mode 100644
index 0000000000..cdf24ffe5e
--- /dev/null
+++ b/tools/BugReportTool/BugReportTool/EventViewer.cpp
@@ -0,0 +1,189 @@
+#include "EventViewer.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "XmlDocumentEx.h"
+
+namespace
+{
+ std::vector processes =
+ {
+ L"PowerToys.exe",
+ L"ColorPickerUI.exe",
+ L"PowerToys.Espresso.exe"
+ L"FancyZonesEditor.exe",
+ L"PowerToys.KeyboardManagerEngine.exe",
+ L"PowerToys.KeyboardManagerEditor.exe",
+ L"PowerLauncher.exe",
+ L"PowerToys.ShortcutGuide.exe"
+ };
+
+ // Batch size for number of events queried at once
+ constexpr int BATCH_SIZE = 50;
+
+ class EventViewerReporter
+ {
+ private:
+ // Report last 30 days
+ const long long PERIOD = 10 * 24 * 3600ll * 1000;
+
+ const std::wstring QUERY = L"" \
+ L" " \
+ L" " \
+ L" " \
+ L"";
+
+ std::wstring GetQuery(std::wstring processName)
+ {
+ wchar_t buff[1000];
+ memset(buff, 0, sizeof(buff));
+ _snwprintf_s(buff, sizeof(buff), QUERY.c_str(), PERIOD, processName.c_str());
+ return buff;
+ }
+
+ std::wofstream report;
+ EVT_HANDLE hResults;
+
+ void PrintEvent(EVT_HANDLE hEvent)
+ {
+ DWORD status = ERROR_SUCCESS;
+ DWORD dwBufferSize = 0;
+ DWORD dwBufferUsed = 0;
+ DWORD dwPropertyCount = 0;
+ LPWSTR pRenderedContent = NULL;
+
+ // The EvtRenderEventXml flag tells EvtRender to render the event as an XML string.
+ if (!EvtRender(NULL, hEvent, EvtRenderEventXml, dwBufferSize, pRenderedContent, &dwBufferUsed, &dwPropertyCount))
+ {
+ if (ERROR_INSUFFICIENT_BUFFER == (status = GetLastError()))
+ {
+ dwBufferSize = dwBufferUsed;
+ pRenderedContent = (LPWSTR)malloc(dwBufferSize);
+ if (pRenderedContent)
+ {
+ EvtRender(NULL, hEvent, EvtRenderEventXml, dwBufferSize, pRenderedContent, &dwBufferUsed, &dwPropertyCount);
+ }
+ }
+
+ if (ERROR_SUCCESS != (status = GetLastError()))
+ {
+ report << std::endl << L"EvtRender failed with " << get_last_error_or_default(GetLastError()) << std::endl << std::endl;
+ if (pRenderedContent)
+ {
+ free(pRenderedContent);
+ }
+ return;
+ }
+ }
+
+ XmlDocumentEx doc;
+ doc.LoadXml(pRenderedContent);
+ std::wstring formattedXml = L"";
+ try
+ {
+ formattedXml = doc.GetFormatedXml();
+ }
+ catch (...)
+ {
+ formattedXml = pRenderedContent;
+ }
+
+ report << std::endl << formattedXml << std::endl;
+ if (pRenderedContent)
+ {
+ free(pRenderedContent);
+ }
+ }
+
+ // Enumerate all the events in the result set.
+ void PrintResults(EVT_HANDLE hResults)
+ {
+ DWORD status = ERROR_SUCCESS;
+ EVT_HANDLE hEvents[BATCH_SIZE];
+ DWORD dwReturned = 0;
+
+ while (true)
+ {
+ // Get a block of events from the result set.
+ if (!EvtNext(hResults, BATCH_SIZE, hEvents, INFINITE, 0, &dwReturned))
+ {
+ if (ERROR_NO_MORE_ITEMS != (status = GetLastError()))
+ {
+ report << L"EvtNext failed with " << status << std::endl;
+ }
+
+ break;
+ }
+
+ // For each event, call the PrintEvent function which renders the
+ // event for display. PrintEvent is shown in RenderingEvents.
+ for (DWORD i = 0; i < dwReturned; i++)
+ {
+ PrintEvent(hEvents[i]);
+ }
+ }
+
+ for (DWORD i = 0; i < dwReturned; i++)
+ {
+ if (nullptr != hEvents[i])
+ EvtClose(hEvents[i]);
+ }
+ }
+
+ public:
+ EventViewerReporter(const std::filesystem::path& tmpDir, std::wstring processName)
+ {
+ auto query = GetQuery(processName);
+ auto reportPath = tmpDir;
+ reportPath.append(L"EventViewer-" + processName + L".xml");
+ report = std::wofstream(reportPath);
+
+ hResults = EvtQuery(NULL, NULL, GetQuery(processName).c_str(), EvtQueryChannelPath);
+ if (NULL == hResults)
+ {
+ report << "Failed to report info for " << processName << ". " << get_last_error_or_default(GetLastError()) << std::endl;
+ return;
+ }
+ }
+
+ ~EventViewerReporter()
+ {
+ if (hResults)
+ {
+ EvtClose(hResults);
+ hResults = nullptr;
+ }
+ }
+
+ void Report()
+ {
+ try
+ {
+ if (hResults)
+ {
+ PrintResults(hResults);
+ }
+ }
+ catch (...)
+ {
+ report << "Failed to report info" << std::endl;
+ }
+ }
+ };
+}
+
+void EventViewer::ReportEventViewerInfo(const std::filesystem::path& tmpDir)
+{
+ for (auto& process : processes)
+ {
+ EventViewerReporter(tmpDir, process).Report();
+ }
+}
diff --git a/tools/BugReportTool/BugReportTool/EventViewer.h b/tools/BugReportTool/BugReportTool/EventViewer.h
new file mode 100644
index 0000000000..0d50f4253e
--- /dev/null
+++ b/tools/BugReportTool/BugReportTool/EventViewer.h
@@ -0,0 +1,7 @@
+#pragma once
+#include
+
+namespace EventViewer
+{
+ void ReportEventViewerInfo(const std::filesystem::path& tmpDir);
+}
diff --git a/tools/BugReportTool/BugReportTool/Main.cpp b/tools/BugReportTool/BugReportTool/Main.cpp
index 603748630a..dd20e2c5f8 100644
--- a/tools/BugReportTool/BugReportTool/Main.cpp
+++ b/tools/BugReportTool/BugReportTool/Main.cpp
@@ -16,6 +16,8 @@
#include "ReportMonitorInfo.h"
#include "RegistryUtils.h"
+#include "EventViewer.h"
+
using namespace std;
using namespace std::filesystem;
using namespace winrt::Windows::Data::Json;
@@ -331,6 +333,9 @@ int wmain(int argc, wchar_t* argv[], wchar_t*)
// Write compatibility tab info to the temporary folder
ReportCompatibilityTab(tmpDir);
+ // Write event viewer logs info to the temporary folder
+ EventViewer::ReportEventViewerInfo(tmpDir);
+
ReportBootstrapperLog(tmpDir);
// Zip folder
diff --git a/tools/BugReportTool/BugReportTool/XmlDocumentEx.cpp b/tools/BugReportTool/BugReportTool/XmlDocumentEx.cpp
new file mode 100644
index 0000000000..d2e71705b1
--- /dev/null
+++ b/tools/BugReportTool/BugReportTool/XmlDocumentEx.cpp
@@ -0,0 +1,56 @@
+#include "XmlDocumentEx.h"
+
+#include
+
+std::wstring XmlDocumentEx::GetFormatedXml()
+{
+ stream.clear();
+ Print(FirstChild(), 0);
+ return stream.str();
+}
+
+void XmlDocumentEx::Print(winrt::Windows::Data::Xml::Dom::IXmlNode node, int indentation)
+{
+ for (int i = 0; i < indentation; i++)
+ {
+ stream << " ";
+ }
+
+ PrintTagWithAttributes(node);
+ if (!node.HasChildNodes())
+ {
+ stream << L"<\\" << node.NodeName().c_str() << ">" << std::endl;
+ return;
+ }
+
+ if (node.ChildNodes().Size() == 1 && !node.FirstChild().HasChildNodes())
+ {
+ stream << node.InnerText().c_str() << L"<\\" << node.NodeName().c_str() << ">" << std::endl;
+ return;
+ }
+
+ stream << std::endl;
+ auto child = node.FirstChild();
+ do
+ {
+ Print(child, indentation + 2);
+ } while (child = child.NextSibling());
+
+ for (int i = 0; i < indentation; i++)
+ {
+ stream << " ";
+ }
+ stream << L"<\\" << node.NodeName().c_str() << ">" << std::endl;
+}
+
+void XmlDocumentEx::PrintTagWithAttributes(winrt::Windows::Data::Xml::Dom::IXmlNode node)
+{
+ stream << L"<" << node.NodeName().c_str();
+ for (int i = 0; i < (int)node.Attributes().Size(); i++)
+ {
+ auto attr = node.Attributes().GetAt(i);
+ stream << L" " << attr.NodeName().c_str() << L"='" << attr.InnerText().c_str() << L"'";
+ }
+
+ stream << L">";
+}
diff --git a/tools/BugReportTool/BugReportTool/XmlDocumentEx.h b/tools/BugReportTool/BugReportTool/XmlDocumentEx.h
new file mode 100644
index 0000000000..65db387ae2
--- /dev/null
+++ b/tools/BugReportTool/BugReportTool/XmlDocumentEx.h
@@ -0,0 +1,13 @@
+#pragma once
+#include
+
+class XmlDocumentEx : public winrt::Windows::Data::Xml::Dom::XmlDocument
+{
+private:
+ std::wstringstream stream;
+ void Print(winrt::Windows::Data::Xml::Dom::IXmlNode node, int indentation);
+ void PrintTagWithAttributes(winrt::Windows::Data::Xml::Dom::IXmlNode node);
+
+public:
+ std::wstring GetFormatedXml();
+};