/ tools / BugReportTool / BugReportTool / RegistryUtils.cpp
RegistryUtils.cpp
  1  #include "RegistryUtils.h"
  2  #include <common/utils/winapi_error.h>
  3  #include <map>
  4  
  5  using namespace std;
  6  
  7  extern std::vector<std::wstring> processes;
  8  
  9  namespace
 10  {
 11      vector<pair<HKEY, wstring>> registryKeys = {
 12      { HKEY_CLASSES_ROOT, L"CLSID\\{DD5CACDA-7C2E-4997-A62A-04A597B58F76}" },
 13      { HKEY_CLASSES_ROOT, L"powertoys" },
 14      { HKEY_CLASSES_ROOT, L"CLSID\\{ddee2b8a-6807-48a6-bb20-2338174ff779}" },
 15      { HKEY_CLASSES_ROOT, L"CLSID\\{36B27788-A8BB-4698-A756-DF9F11F64F84}" },
 16      { HKEY_CLASSES_ROOT, L"CLSID\\{45769bcc-e8fd-42d0-947e-02beef77a1f5}" },
 17      { HKEY_CLASSES_ROOT, L"AppID\\{CF142243-F059-45AF-8842-DBBE9783DB14}" },
 18      { HKEY_CLASSES_ROOT, L"CLSID\\{07665729-6243-4746-95b7-79579308d1b2}" },
 19      { HKEY_CLASSES_ROOT, L"CLSID\\{ec52dea8-7c9f-4130-a77b-1737d0418507}" },
 20      { HKEY_CLASSES_ROOT, L"CLSID\\{dd8de316-7b01-48e7-ba21-e92c646704af}" },
 21      { HKEY_CLASSES_ROOT, L"CLSID\\{8AA07897-C30B-4543-865B-00A0E5A1B32D}" },
 22      { HKEY_CLASSES_ROOT, L"CLSID\\{BCC13D15-9720-4CC4-8371-EA74A274741E}" },
 23      { HKEY_CLASSES_ROOT, L"CLSID\\{BFEE99B4-B74D-4348-BCA5-E757029647FF}" },
 24      { HKEY_CLASSES_ROOT, L"CLSID\\{c28761a0-8420-43ad-bff3-40400543e2d4}" },
 25      { HKEY_CLASSES_ROOT, L"CLSID\\{8BC8AFC2-4E7C-4695-818E-8C1FFDCEA2AF}" },
 26      { HKEY_CLASSES_ROOT, L"CLSID\\{51B4D7E5-7568-4234-B4BB-47FB3C016A69}\\InprocServer32" },
 27      { HKEY_CLASSES_ROOT, L"CLSID\\{0440049F-D1DC-4E46-B27B-98393D79486B}" },
 28      { HKEY_CLASSES_ROOT, L"AllFileSystemObjects\\ShellEx\\ContextMenuHandlers\\PowerRenameExt" },
 29      { HKEY_CLASSES_ROOT, L".svg\\shellex\\{8895b1c6-b41f-4c1c-a562-0d564250836f}" },
 30      { HKEY_CLASSES_ROOT, L".svg\\shellex\\{E357FCCD-A995-4576-B01F-234630154E96}" },
 31      { HKEY_CLASSES_ROOT, L".md\\shellex\\{8895b1c6-b41f-4c1c-a562-0d564250836f}" },
 32      { HKEY_CLASSES_ROOT, L".pdf\\shellex\\{8895b1c6-b41f-4c1c-a562-0d564250836f}" },
 33      { HKEY_CLASSES_ROOT, L".pdf\\shellex\\{E357FCCD-A995-4576-B01F-234630154E96}" },
 34      { HKEY_CLASSES_ROOT, L".qoi\\shellex\\{8895b1c6-b41f-4c1c-a562-0d564250836f}" },
 35      { HKEY_CLASSES_ROOT, L".qoi\\shellex\\{E357FCCD-A995-4576-B01F-234630154E96}" },
 36      { HKEY_CLASSES_ROOT, L".gcode\\shellex\\{8895b1c6-b41f-4c1c-a562-0d564250836f}" },
 37      { HKEY_CLASSES_ROOT, L".gcode\\shellex\\{E357FCCD-A995-4576-B01F-234630154E96}" },
 38      { HKEY_CLASSES_ROOT, L".bgcode\\shellex\\{8895b1c6-b41f-4c1c-a562-0d564250836f}" },
 39      { HKEY_CLASSES_ROOT, L".bgcode\\shellex\\{E357FCCD-A995-4576-B01F-234630154E96}" },
 40      { HKEY_CLASSES_ROOT, L".stl\\shellex\\{E357FCCD-A995-4576-B01F-234630154E96}" }
 41      };
 42  
 43      vector<tuple<HKEY, wstring, wstring>> registryValues = {
 44          { HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\PreviewHandlers", L"{ddee2b8a-6807-48a6-bb20-2338174ff779}" },
 45          { HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\PreviewHandlers", L"{45769bcc-e8fd-42d0-947e-02beef77a1f5}" },
 46          { HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\PreviewHandlers", L"{07665729-6243-4746-95b7-79579308d1b2}" },
 47          { HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\PreviewHandlers", L"{ec52dea8-7c9f-4130-a77b-1737d0418507}" },
 48          { HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\PreviewHandlers", L"{dd8de316-7b01-48e7-ba21-e92c646704af}" },
 49          { HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\PreviewHandlers", L"{8AA07897-C30B-4543-865B-00A0E5A1B32D}" },
 50          { HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Internet Explorer\\Main\\FeatureControl\\FEATURE_BROWSER_EMULATION", L"prevhost.exe" },
 51          { HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Internet Explorer\\Main\\FeatureControl\\FEATURE_BROWSER_EMULATION", L"dllhost.exe" }
 52      };
 53  
 54      // Is there a Windows API for this?
 55      std::unordered_map<HKEY, wstring> hKeyToString = {
 56          { HKEY_CLASSES_ROOT, L"HKEY_CLASSES_ROOT" },
 57          { HKEY_CURRENT_USER, L"HKEY_CURRENT_USER" },
 58          { HKEY_LOCAL_MACHINE, L"HKEY_LOCAL_MACHINE" },
 59          { HKEY_PERFORMANCE_DATA, L"HKEY_PERFORMANCE_DATA" },
 60          { HKEY_PERFORMANCE_NLSTEXT, L"HKEY_PERFORMANCE_NLSTEXT"},
 61          { HKEY_PERFORMANCE_TEXT, L"HKEY_PERFORMANCE_TEXT"},
 62          { HKEY_USERS, L"HKEY_USERS"},
 63      };
 64  
 65      vector<pair<wstring, wstring>> QueryValues(HKEY key)
 66      {
 67          DWORD cValues;
 68          RegQueryInfoKeyW(key, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, &cValues, nullptr, nullptr, nullptr, nullptr);
 69          TCHAR achValue[255];
 70          DWORD cchValue = 255;
 71          LPBYTE value;
 72          vector<pair<wstring, wstring>> results;
 73          // Values
 74          if (cValues)
 75          {
 76              for (DWORD i = 0, retCode = ERROR_SUCCESS; i < cValues; i++)
 77              {
 78                  cchValue = 255;
 79                  achValue[0] = '\0';
 80                  value = new BYTE[16383];
 81                  retCode = RegEnumValueW(key, i, achValue, &cchValue, NULL, NULL, value, &cchValue);
 82  
 83                  if (retCode == ERROR_SUCCESS)
 84                  {
 85                      results.push_back({ achValue, (LPCTSTR)value });
 86                  }
 87              }
 88          }
 89  
 90          return results;
 91      }
 92  
 93      void QueryKey(HKEY key, wostream& stream, int indent = 1)
 94      {
 95          TCHAR achKey[255];
 96          DWORD cbName;
 97          TCHAR achClass[MAX_PATH] = TEXT("");
 98          DWORD cchClassName = MAX_PATH;
 99          DWORD cSubKeys = 0;
100          DWORD cbMaxSubKey;
101          DWORD cchMaxClass;
102          DWORD cValues;
103          DWORD cchMaxValue;
104          DWORD cbMaxValueData;
105  
106          DWORD i, retCode;
107  
108          TCHAR achValue[255];
109          DWORD cchValue = 255;
110          LPBYTE value;
111  
112          // Get the class name and the value count. 
113          retCode = RegQueryInfoKeyW(key, achClass, &cchClassName, NULL, &cSubKeys, &cbMaxSubKey, &cchMaxClass, &cValues, &cchMaxValue, &cbMaxValueData, NULL, NULL);
114  
115          // Values
116          if (cValues)
117          {
118              for (i = 0, retCode = ERROR_SUCCESS; i < cValues; i++)
119              {
120                  cchValue = 255;
121                  achValue[0] = '\0';
122                  value = new BYTE[16383];
123                  retCode = RegEnumValueW(key, i, achValue, &cchValue, NULL, NULL, value, &cchValue);
124  
125                  if (retCode == ERROR_SUCCESS)
126                  {
127                      stream << wstring(indent, '\t');
128                      if (achValue[0] == '\0')
129                      {
130                          stream << "Default";
131                      }
132                      else
133                      {
134                          stream << achValue;
135                      }
136  
137                      stream << " > " << reinterpret_cast<LPCTSTR>(value) << "\n";
138                  }
139                  else
140                  {
141                      stream << "Error " << retCode << "\n";
142                  }
143              }
144          }
145  
146          // Keys
147          if (cSubKeys)
148          {
149              std::vector<wstring> vecKeys;
150              vecKeys.reserve(cSubKeys);
151  
152              for (i = 0; i < cSubKeys; ++i)
153              {
154                  cbName = 255;
155                  retCode = RegEnumKeyExW(key, i, achKey, &cbName, NULL, NULL, NULL, NULL);
156                  if (retCode == ERROR_SUCCESS)
157                  {
158                      vecKeys.push_back(achKey);
159                  }
160              }
161  
162              // Parsing subkeys recursively
163              for (auto& child : vecKeys)
164              {
165                  HKEY hTestKey;
166                  if (RegOpenKeyExW(key, child.c_str(), 0, KEY_READ, &hTestKey) == ERROR_SUCCESS)
167                  {
168                      stream << wstring(indent, '\t') << child << "\n";
169                      QueryKey(hTestKey, stream, indent + 1);
170                      RegCloseKey(hTestKey);
171                  }
172                  else
173                  {
174                      stream << "Error " << retCode << "\n";
175                  }
176              }
177          }
178      }
179  }
180  
181  void ReportCompatibilityTab(HKEY key, wofstream& report)
182  {
183      map<wstring, wstring> flags;
184      for (auto app : processes)
185      {
186          flags[app] = L"";
187      }
188  
189      try
190      {
191          HKEY outKey;
192          LONG result = RegOpenKeyExW(key, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Layers", 0, KEY_READ, &outKey);
193          if (result == ERROR_SUCCESS)
194          {
195              auto values = QueryValues(outKey);
196              for (auto value : values)
197              {
198                  for (auto app : processes)
199                  {
200                      if (value.first.find(app) != wstring::npos)
201                      {
202                          flags[app] += value.second;
203                      }
204                  }
205              }
206          }
207          else
208          {
209              report << "Failed to get the report. " << get_last_error_or_default(GetLastError());
210              return;
211          }
212      }
213      catch (...)
214      {
215          report << "Failed to get the report";
216          return;
217      }
218  
219      for (auto flag : flags)
220      {
221          report << flag.first << ": " << flag.second << endl;
222      }
223  }
224  
225  void ReportCompatibilityTab(const std::filesystem::path& tmpDir)
226  {
227      auto reportPath = tmpDir;
228      reportPath.append(L"compatibility-tab-info.txt");
229      wofstream report(reportPath);
230      report << "Current user report" << endl;
231      ReportCompatibilityTab(HKEY_CURRENT_USER, report);
232      report << endl << endl;
233      report << "Local machine report" << endl;
234      ReportCompatibilityTab(HKEY_LOCAL_MACHINE, report);
235  }
236  
237  void ReportRegistry(const filesystem::path& tmpDir)
238  {
239      auto registryReportPath = tmpDir;
240      registryReportPath.append("registry-report-info.txt");
241  
242      wofstream registryReport(registryReportPath);
243      try
244      {
245          for (auto [rootKey, subKey] : registryKeys)
246          {
247              registryReport << hKeyToString[rootKey] << "\\" << subKey << "\n";
248  
249              HKEY outKey;
250              LONG result = RegOpenKeyExW(rootKey, subKey.c_str(), 0, KEY_READ, &outKey);
251              if (result == ERROR_SUCCESS)
252              {
253                  QueryKey(outKey, registryReport);
254                  RegCloseKey(rootKey);
255              }
256              else
257              {
258                  registryReport << "ERROR " << result << "\n";
259              }
260  
261              registryReport << "\n";
262          }
263  
264          for (auto [rootKey, subKey, value] : registryValues)
265          {
266              registryReport << hKeyToString[rootKey] << "\\" << subKey << "\n";
267  
268              // Reading size
269              DWORD dataSize = 0;
270              DWORD flags = RRF_RT_ANY;
271              DWORD type;
272              LONG result = RegGetValueW(rootKey, subKey.c_str(), value.c_str(), flags, &type, NULL, &dataSize);
273              if (result == ERROR_SUCCESS)
274              {
275                  // Reading value
276                  if (type == REG_SZ) // string
277                  {
278                      std::wstring data(dataSize / sizeof(wchar_t) + 1, L' ');
279                      result = RegGetValueW(rootKey, subKey.c_str(), value.c_str(), flags, &type, &data[0], &dataSize);
280                      if (result == ERROR_SUCCESS)
281                      {
282                          registryReport << "\t" << value << " > " << data << "\n";
283                      }
284                      else
285                      {
286                          registryReport << "ERROR " << result << "\n";
287                      }
288                  }
289                  else
290                  {
291                      DWORD data = 0;
292                      dataSize = sizeof(data);
293                      result = RegGetValueW(rootKey, subKey.c_str(), value.c_str(), flags, &type, &data, &dataSize);
294                      if (result == ERROR_SUCCESS)
295                      {
296                          registryReport << "\t" << value << " > " << data << "\n";
297                      }
298                      else
299                      {
300                          registryReport << "ERROR " << result << "\n";
301                      }
302                  }
303  
304                  RegCloseKey(rootKey);
305              }
306              else
307              {
308                  registryReport << "ERROR " << result << "\n";
309              }
310  
311              registryReport << "\n";
312          }
313      }
314      catch (...)
315      {
316          printf("Failed to get registry keys\n");
317      }
318  }