Bug report tool for registry (#9213)
* wip * Improved registry bug reporting * Don't use macros * Ignore spelling of NLSTEXT in macro * Various improvements * Move functions to separate files * Rename result file to registry-report-info.txt * Rename a poorly named function in ReportMonitorInfo.cpp * Restrict scope of symbols in these .cpp files Co-authored-by: Davide <davide.giacometti@outlook.it>
This commit is contained in:
parent
b74afd3a80
commit
0b12798922
1
.github/actions/spell-check/expect.txt
vendored
1
.github/actions/spell-check/expect.txt
vendored
|
@ -1446,6 +1446,7 @@ nielslaute
|
|||
NIF
|
||||
NLD
|
||||
NLog
|
||||
NLSTEXT
|
||||
NMLVEMPTYMARKUP
|
||||
NOACTIVATE
|
||||
noactive
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
</ClCompile>
|
||||
<ClCompile Include="ReportMonitorInfo.cpp" />
|
||||
<ClCompile Include="Main.cpp" />
|
||||
<ClCompile Include="ReportRegistry.cpp" />
|
||||
<ClCompile Include="ZipTools\zipfolder.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -53,6 +54,7 @@
|
|||
<ClInclude Include="..\..\..\deps\cziplib\src\zip.h" />
|
||||
<ClInclude Include="ReportMonitorInfo.h" />
|
||||
<ClInclude Include="..\..\..\common\utils\json.h" />
|
||||
<ClInclude Include="ReportRegistry.h" />
|
||||
<ClInclude Include="ZipTools\zipfolder.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\deps\cziplib\src\zip.c" />
|
||||
<ClCompile Include="ReportMonitorInfo.cpp" />
|
||||
<ClCompile Include="ReportRegistry.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="ZipTools">
|
||||
|
@ -24,5 +25,6 @@
|
|||
<ClInclude Include="..\..\..\deps\cziplib\src\miniz.h" />
|
||||
<ClInclude Include="..\..\..\deps\cziplib\src\zip.h" />
|
||||
<ClInclude Include="ReportMonitorInfo.h" />
|
||||
<ClInclude Include="ReportRegistry.h" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -13,6 +13,7 @@
|
|||
#include <common/utils/exec.h>
|
||||
|
||||
#include "ReportMonitorInfo.h"
|
||||
#include "ReportRegistry.h"
|
||||
using namespace std;
|
||||
using namespace std::filesystem;
|
||||
using namespace winrt::Windows::Data::Json;
|
||||
|
@ -148,27 +149,6 @@ void hideUserPrivateInfo(const filesystem::path& dir)
|
|||
}
|
||||
}
|
||||
|
||||
void reportMonitorInfo(const filesystem::path& tmpDir)
|
||||
{
|
||||
auto monitorReportPath = tmpDir;
|
||||
monitorReportPath.append("monitor-report-info.txt");
|
||||
|
||||
try
|
||||
{
|
||||
wofstream monitorReport(monitorReportPath);
|
||||
monitorReport << "GetSystemMetrics = " << GetSystemMetrics(SM_CMONITORS) << '\n';
|
||||
report(monitorReport);
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
printf("Failed to report monitor info. %s\n", ex.what());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
printf("Failed to report monitor info\n");
|
||||
}
|
||||
}
|
||||
|
||||
void reportWindowsVersion(const filesystem::path& tmpDir)
|
||||
{
|
||||
auto versionReportPath = tmpDir;
|
||||
|
@ -285,6 +265,9 @@ int wmain(int argc, wchar_t* argv[], wchar_t*)
|
|||
// Write dotnet installation info to the temporary folder
|
||||
reportDotNetInstallationInfo(tmpDir);
|
||||
|
||||
// Write registry to the temporary folder
|
||||
reportRegistry(tmpDir);
|
||||
|
||||
// Zip folder
|
||||
auto zipPath = path::path(saveZipPath);
|
||||
std::string reportFilename{"PowerToysReport_"};
|
||||
|
@ -304,4 +287,4 @@ int wmain(int argc, wchar_t* argv[], wchar_t*)
|
|||
|
||||
deleteFolder(tmpDir);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,56 +1,82 @@
|
|||
#pragma once
|
||||
#include "ReportMonitorInfo.h"
|
||||
#include <Windows.h>
|
||||
#include <filesystem>
|
||||
#include "../../../src/common/utils/winapi_error.h"
|
||||
using namespace std;
|
||||
|
||||
int report(std::wostream& os)
|
||||
namespace
|
||||
{
|
||||
struct capture
|
||||
int buildMonitorInfoReport(std::wostream& os)
|
||||
{
|
||||
std::wostream* os = nullptr;
|
||||
};
|
||||
|
||||
auto callback = [](HMONITOR monitor, HDC, RECT*, LPARAM prm) -> BOOL {
|
||||
std::wostream& os = *((capture*)prm)->os;
|
||||
MONITORINFOEX mi;
|
||||
mi.cbSize = sizeof(mi);
|
||||
|
||||
if (GetMonitorInfoW(monitor, &mi))
|
||||
struct capture
|
||||
{
|
||||
os << "GetMonitorInfo OK\n";
|
||||
DISPLAY_DEVICE displayDevice = { sizeof(displayDevice) };
|
||||
std::wostream* os = nullptr;
|
||||
};
|
||||
|
||||
DWORD i = 0;
|
||||
while (EnumDisplayDevicesW(mi.szDevice, i++, &displayDevice, EDD_GET_DEVICE_INTERFACE_NAME))
|
||||
auto callback = [](HMONITOR monitor, HDC, RECT*, LPARAM prm) -> BOOL {
|
||||
std::wostream& os = *((capture*)prm)->os;
|
||||
MONITORINFOEX mi;
|
||||
mi.cbSize = sizeof(mi);
|
||||
|
||||
if (GetMonitorInfoW(monitor, &mi))
|
||||
{
|
||||
const bool active = displayDevice.StateFlags & DISPLAY_DEVICE_ACTIVE;
|
||||
const bool mirroring = displayDevice.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER;
|
||||
os << "EnumDisplayDevices OK:\n"
|
||||
<< "\tMirroring = " << mirroring << '\n'
|
||||
<< "\tActive = " << active << '\n'
|
||||
<< "\tDeviceID = " << displayDevice.DeviceID << '\n'
|
||||
<< "\tDeviceKey = " << displayDevice.DeviceKey << '\n'
|
||||
<< "\tDeviceName = " << displayDevice.DeviceName << '\n'
|
||||
<< "\tDeviceString = " << displayDevice.DeviceString << '\n';
|
||||
os << "GetMonitorInfo OK\n";
|
||||
DISPLAY_DEVICE displayDevice = { sizeof(displayDevice) };
|
||||
|
||||
DWORD i = 0;
|
||||
while (EnumDisplayDevicesW(mi.szDevice, i++, &displayDevice, EDD_GET_DEVICE_INTERFACE_NAME))
|
||||
{
|
||||
const bool active = displayDevice.StateFlags & DISPLAY_DEVICE_ACTIVE;
|
||||
const bool mirroring = displayDevice.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER;
|
||||
os << "EnumDisplayDevices OK:\n"
|
||||
<< "\tMirroring = " << mirroring << '\n'
|
||||
<< "\tActive = " << active << '\n'
|
||||
<< "\tDeviceID = " << displayDevice.DeviceID << '\n'
|
||||
<< "\tDeviceKey = " << displayDevice.DeviceKey << '\n'
|
||||
<< "\tDeviceName = " << displayDevice.DeviceName << '\n'
|
||||
<< "\tDeviceString = " << displayDevice.DeviceString << '\n';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto message = get_last_error_message(GetLastError());
|
||||
os << "GetMonitorInfo FAILED: " << (message.has_value() ? message.value() : L"") << '\n';
|
||||
}
|
||||
return TRUE;
|
||||
};
|
||||
capture c;
|
||||
c.os = &os;
|
||||
if (EnumDisplayMonitors(nullptr, nullptr, callback, (LPARAM)&c))
|
||||
{
|
||||
os << "EnumDisplayMonitors OK\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
auto message = get_last_error_message(GetLastError());
|
||||
os << "GetMonitorInfo FAILED: " << (message.has_value() ? message.value() : L"") << '\n';
|
||||
os << "EnumDisplayMonitors FAILED: " << (message.has_value() ? message.value() : L"") << '\n';
|
||||
}
|
||||
return TRUE;
|
||||
};
|
||||
capture c;
|
||||
c.os = &os;
|
||||
if (EnumDisplayMonitors(nullptr, nullptr, callback, (LPARAM)&c))
|
||||
{
|
||||
os << "EnumDisplayMonitors OK\n";
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
}
|
||||
|
||||
void reportMonitorInfo(const filesystem::path& tmpDir)
|
||||
{
|
||||
auto monitorReportPath = tmpDir;
|
||||
monitorReportPath.append("monitor-report-info.txt");
|
||||
|
||||
try
|
||||
{
|
||||
auto message = get_last_error_message(GetLastError());
|
||||
os << "EnumDisplayMonitors FAILED: " << (message.has_value() ? message.value() : L"") << '\n';
|
||||
wofstream monitorReport(monitorReportPath);
|
||||
monitorReport << "GetSystemMetrics = " << GetSystemMetrics(SM_CMONITORS) << '\n';
|
||||
buildMonitorInfoReport(monitorReport);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
printf("Failed to report monitor info. %s\n", ex.what());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
printf("Failed to report monitor info\n");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#pragma once
|
||||
#include <fstream>
|
||||
|
||||
int report(std::wostream& os);
|
||||
void reportMonitorInfo(const std::filesystem::path& tmpDir);
|
||||
|
|
207
tools/BugReportTool/BugReportTool/ReportRegistry.cpp
Normal file
207
tools/BugReportTool/BugReportTool/ReportRegistry.cpp
Normal file
|
@ -0,0 +1,207 @@
|
|||
#include "ReportRegistry.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace
|
||||
{
|
||||
vector<pair<HKEY, wstring>> registryKeys = {
|
||||
{ HKEY_CLASSES_ROOT, L"Software\\Classes\\CLSID\\{DD5CACDA-7C2E-4997-A62A-04A597B58F76}" },
|
||||
{ HKEY_CLASSES_ROOT, L"powertoys" },
|
||||
{ HKEY_CLASSES_ROOT, L"CLSID\\{ddee2b8a-6807-48a6-bb20-2338174ff779}" },
|
||||
{ HKEY_CLASSES_ROOT, L"CLSID\\{36B27788-A8BB-4698-A756-DF9F11F64F84}" },
|
||||
{ HKEY_CLASSES_ROOT, L"CLSID\\{45769bcc-e8fd-42d0-947e-02beef77a1f5}" },
|
||||
{ HKEY_CLASSES_ROOT, L"AppID\\{CF142243-F059-45AF-8842-DBBE9783DB14}" },
|
||||
{ HKEY_CLASSES_ROOT, L"CLSID\\{51B4D7E5-7568-4234-B4BB-47FB3C016A69}\\InprocServer32" },
|
||||
{ HKEY_CLASSES_ROOT, L"CLSID\\{0440049F-D1DC-4E46-B27B-98393D79486B}" },
|
||||
{ HKEY_CLASSES_ROOT, L"AllFileSystemObjects\\ShellEx\\ContextMenuHandlers\\PowerRenameExt" },
|
||||
{ HKEY_CURRENT_USER, L"SOFTWARE\\Classes\\AppUserModelId\\PowerToysRun" },
|
||||
{ HKEY_CLASSES_ROOT, L".svg\\shellex\\{8895b1c6-b41f-4c1c-a562-0d564250836f}" },
|
||||
{ HKEY_CLASSES_ROOT, L".svg\\shellex\\{E357FCCD-A995-4576-B01F-234630154E96}" },
|
||||
{ HKEY_CLASSES_ROOT, L".md\\shellex\\{8895b1c6-b41f-4c1c-a562-0d564250836f}" }
|
||||
};
|
||||
|
||||
vector<tuple<HKEY, wstring, wstring>> registryValues = {
|
||||
{ HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\PreviewHandlers", L"{ddee2b8a-6807-48a6-bb20-2338174ff779}" },
|
||||
{ HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\PreviewHandlers", L"{45769bcc-e8fd-42d0-947e-02beef77a1f5}" },
|
||||
{ HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Internet Explorer\\Main\\FeatureControl\\FEATURE_BROWSER_EMULATION", L"prevhost.exe" },
|
||||
{ HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Internet Explorer\\Main\\FeatureControl\\FEATURE_BROWSER_EMULATION", L"dllhost.exe" }
|
||||
};
|
||||
|
||||
// Is there a Windows API for this?
|
||||
std::unordered_map<HKEY, wstring> hKeyToString = {
|
||||
{ HKEY_CLASSES_ROOT, L"HKEY_CLASSES_ROOT" },
|
||||
{ HKEY_CURRENT_USER, L"HKEY_CURRENT_USER" },
|
||||
{ HKEY_LOCAL_MACHINE, L"HKEY_LOCAL_MACHINE" },
|
||||
{ HKEY_PERFORMANCE_DATA, L"HKEY_PERFORMANCE_DATA" },
|
||||
{ HKEY_PERFORMANCE_NLSTEXT, L"HKEY_PERFORMANCE_NLSTEXT"},
|
||||
{ HKEY_PERFORMANCE_TEXT, L"HKEY_PERFORMANCE_TEXT"},
|
||||
{ HKEY_USERS, L"HKEY_USERS"},
|
||||
};
|
||||
|
||||
void queryKey(HKEY key, wofstream& stream, int indent = 1)
|
||||
{
|
||||
TCHAR achKey[255];
|
||||
DWORD cbName;
|
||||
TCHAR achClass[MAX_PATH] = TEXT("");
|
||||
DWORD cchClassName = MAX_PATH;
|
||||
DWORD cSubKeys = 0;
|
||||
DWORD cbMaxSubKey;
|
||||
DWORD cchMaxClass;
|
||||
DWORD cValues;
|
||||
DWORD cchMaxValue;
|
||||
DWORD cbMaxValueData;
|
||||
|
||||
DWORD i, retCode;
|
||||
|
||||
TCHAR achValue[255];
|
||||
DWORD cchValue = 255;
|
||||
LPBYTE value;
|
||||
|
||||
// Get the class name and the value count.
|
||||
retCode = RegQueryInfoKeyW(key, achClass, &cchClassName, NULL, &cSubKeys, &cbMaxSubKey, &cchMaxClass, &cValues, &cchMaxValue, &cbMaxValueData, NULL, NULL);
|
||||
|
||||
// Values
|
||||
if (cValues)
|
||||
{
|
||||
for (i = 0, retCode = ERROR_SUCCESS; i < cValues; i++)
|
||||
{
|
||||
cchValue = 255;
|
||||
achValue[0] = '\0';
|
||||
value = new BYTE[16383];
|
||||
retCode = RegEnumValueW(key, i, achValue, &cchValue, NULL, NULL, value, &cchValue);
|
||||
|
||||
if (retCode == ERROR_SUCCESS)
|
||||
{
|
||||
stream << wstring(indent, '\t');
|
||||
if (achValue[0] == '\0')
|
||||
{
|
||||
stream << "Default";
|
||||
}
|
||||
else
|
||||
{
|
||||
stream << achValue;
|
||||
}
|
||||
|
||||
stream << " > " << (LPCTSTR)value << "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
stream << "Error " << retCode << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Keys
|
||||
if (cSubKeys)
|
||||
{
|
||||
std::vector<wstring> vecKeys;
|
||||
vecKeys.reserve(cSubKeys);
|
||||
|
||||
for (i = 0; i < cSubKeys; ++i)
|
||||
{
|
||||
cbName = 255;
|
||||
retCode = RegEnumKeyExW(key, i, achKey, &cbName, NULL, NULL, NULL, NULL);
|
||||
if (retCode == ERROR_SUCCESS)
|
||||
{
|
||||
vecKeys.push_back(achKey);
|
||||
}
|
||||
}
|
||||
|
||||
// Parsing subkeys recursively
|
||||
for (auto& child : vecKeys)
|
||||
{
|
||||
HKEY hTestKey;
|
||||
if (RegOpenKeyExW(key, child.c_str(), 0, KEY_READ, &hTestKey) == ERROR_SUCCESS)
|
||||
{
|
||||
stream << wstring(indent, '\t') << child << "\n";
|
||||
queryKey(hTestKey, stream, indent + 1);
|
||||
RegCloseKey(hTestKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
stream << "Error " << retCode << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void reportRegistry(const filesystem::path& tmpDir)
|
||||
{
|
||||
auto registryReportPath = tmpDir;
|
||||
registryReportPath.append("registry-report-info.txt");
|
||||
|
||||
wofstream registryReport(registryReportPath);
|
||||
try
|
||||
{
|
||||
for (auto [rootKey, subKey] : registryKeys)
|
||||
{
|
||||
registryReport << hKeyToString[rootKey] << "\\" << subKey << "\n";
|
||||
|
||||
HKEY outKey;
|
||||
LONG result = RegOpenKeyExW(rootKey, subKey.c_str(), 0, KEY_READ, &outKey);
|
||||
if (result == ERROR_SUCCESS)
|
||||
{
|
||||
queryKey(outKey, registryReport);
|
||||
RegCloseKey(rootKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
registryReport << "ERROR " << result << "\n";
|
||||
}
|
||||
registryReport << "\n";
|
||||
}
|
||||
|
||||
for (auto [rootKey, subKey, value] : registryValues)
|
||||
{
|
||||
registryReport << hKeyToString[rootKey] << "\\" << subKey << "\n";
|
||||
|
||||
// Reading size
|
||||
DWORD dataSize = 0;
|
||||
DWORD flags = RRF_RT_ANY;
|
||||
DWORD type;
|
||||
LONG result = RegGetValueW(rootKey, subKey.c_str(), value.c_str(), flags, &type, NULL, &dataSize);
|
||||
if (result == ERROR_SUCCESS)
|
||||
{
|
||||
// Reading value
|
||||
if (type == REG_SZ) // string
|
||||
{
|
||||
std::wstring data(dataSize / sizeof(wchar_t) + 1, L' ');
|
||||
result = RegGetValueW(rootKey, subKey.c_str(), value.c_str(), flags, &type, &data[0], &dataSize);
|
||||
if (result == ERROR_SUCCESS)
|
||||
{
|
||||
registryReport << "\t" << value << " > " << data << "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
registryReport << "ERROR " << result << "\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DWORD data = 0;
|
||||
DWORD dataSize = sizeof(data);
|
||||
LONG retCode = RegGetValueW(rootKey, subKey.c_str(), value.c_str(), flags, &type, &data, &dataSize);
|
||||
if (result == ERROR_SUCCESS)
|
||||
{
|
||||
registryReport << "\t" << value << " > " << data << "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
registryReport << "ERROR " << result << "\n";
|
||||
}
|
||||
}
|
||||
RegCloseKey(rootKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
registryReport << "ERROR " << result << "\n";
|
||||
}
|
||||
registryReport << "\n";
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
printf("Failed to get registry keys\n");
|
||||
}
|
||||
}
|
10
tools/BugReportTool/BugReportTool/ReportRegistry.h
Normal file
10
tools/BugReportTool/BugReportTool/ReportRegistry.h
Normal file
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
#include <unordered_map>
|
||||
#include <Windows.h>
|
||||
|
||||
void reportRegistry(const std::filesystem::path& tmpDir);
|
Loading…
Reference in a new issue