cmWindowsRegistry.cxx 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file LICENSE.rst or https://cmake.org/licensing for details. */
  3. #include "cmConfigure.h" // IWYU pragma: keep
  4. #include "cmWindowsRegistry.h"
  5. #include <cctype>
  6. #include <cstddef>
  7. #include <type_traits>
  8. #include <unordered_map>
  9. #include <utility>
  10. #include <cmext/string_view>
  11. #include "cmsys/RegularExpression.hxx"
  12. #if defined(_WIN32) && !defined(__CYGWIN__)
  13. # include <algorithm>
  14. # include <cstring>
  15. # include <exception>
  16. # include <iterator>
  17. # include <vector>
  18. # include <cm/memory>
  19. # include <windows.h>
  20. # include "cmMakefile.h"
  21. # include "cmStringAlgorithms.h"
  22. # include "cmValue.h"
  23. #endif
  24. namespace {
  25. // Case-independent string comparison
  26. int Strucmp(cm::string_view l, cm::string_view r)
  27. {
  28. if (l.empty() && r.empty()) {
  29. return 0;
  30. }
  31. if (l.empty() || r.empty()) {
  32. return static_cast<int>(l.size() - r.size());
  33. }
  34. int lc;
  35. int rc;
  36. cm::string_view::size_type li = 0;
  37. cm::string_view::size_type ri = 0;
  38. do {
  39. lc = std::tolower(l[li++]);
  40. rc = std::tolower(r[ri++]);
  41. } while (lc == rc && li < l.size() && ri < r.size());
  42. return lc == rc ? static_cast<int>(l.size() - r.size()) : lc - rc;
  43. }
  44. #if defined(_WIN32) && !defined(__CYGWIN__)
  45. bool Is64BitWindows()
  46. {
  47. # if defined(_WIN64)
  48. // 64-bit programs run only on Win64
  49. return true;
  50. # else
  51. // 32-bit programs run on both 32-bit and 64-bit Windows, so we must check.
  52. BOOL isWow64 = false;
  53. return IsWow64Process(GetCurrentProcess(), &isWow64) && isWow64;
  54. # endif
  55. }
  56. // Helper to translate Windows registry value type to enum ValueType
  57. cm::optional<cmWindowsRegistry::ValueType> ToValueType(DWORD type)
  58. {
  59. using ValueType = cmWindowsRegistry::ValueType;
  60. static std::unordered_map<DWORD, ValueType> ValueTypes{
  61. { REG_SZ, ValueType::Reg_SZ },
  62. { REG_EXPAND_SZ, ValueType::Reg_EXPAND_SZ },
  63. { REG_MULTI_SZ, ValueType::Reg_MULTI_SZ },
  64. { REG_DWORD, ValueType::Reg_DWORD },
  65. { REG_QWORD, ValueType::Reg_QWORD }
  66. };
  67. auto it = ValueTypes.find(type);
  68. return it == ValueTypes.end()
  69. ? cm::nullopt
  70. : cm::optional<cmWindowsRegistry::ValueType>{ it->second };
  71. }
  72. // class registry_exception
  73. class registry_error : public std::exception
  74. {
  75. public:
  76. registry_error(std::string msg)
  77. : What(std::move(msg))
  78. {
  79. }
  80. ~registry_error() override = default;
  81. char const* what() const noexcept override { return What.c_str(); }
  82. private:
  83. std::string What;
  84. };
  85. // Class KeyHandler
  86. class KeyHandler
  87. {
  88. public:
  89. using View = cmWindowsRegistry::View;
  90. using ValueTypeSet = cmWindowsRegistry::ValueTypeSet;
  91. KeyHandler(HKEY hkey)
  92. : Handler(hkey)
  93. {
  94. }
  95. ~KeyHandler() { RegCloseKey(this->Handler); }
  96. static KeyHandler OpenKey(cm::string_view rootKey, cm::string_view subKey,
  97. View view);
  98. static KeyHandler OpenKey(cm::string_view key, View view);
  99. std::string ReadValue(
  100. cm::string_view name,
  101. ValueTypeSet supportedTypes = cmWindowsRegistry::AllTypes,
  102. cm::string_view separator = "\0"_s);
  103. std::vector<std::string> GetValueNames();
  104. std::vector<std::string> GetSubKeys();
  105. private:
  106. static std::string FormatSystemError(LSTATUS status);
  107. static std::wstring ToWide(cm::string_view str);
  108. static std::string ToNarrow(wchar_t const* str, int size = -1);
  109. HKEY Handler;
  110. };
  111. KeyHandler KeyHandler::OpenKey(cm::string_view rootKey, cm::string_view subKey,
  112. View view)
  113. {
  114. if (view == View::Reg64 && !Is64BitWindows()) {
  115. throw registry_error("No 64bit registry on Windows32.");
  116. }
  117. HKEY hRootKey;
  118. if (rootKey == "HKCU"_s || rootKey == "HKEY_CURRENT_USER"_s) {
  119. hRootKey = HKEY_CURRENT_USER;
  120. } else if (rootKey == "HKLM"_s || rootKey == "HKEY_LOCAL_MACHINE"_s) {
  121. hRootKey = HKEY_LOCAL_MACHINE;
  122. } else if (rootKey == "HKCR"_s || rootKey == "HKEY_CLASSES_ROOT"_s) {
  123. hRootKey = HKEY_CLASSES_ROOT;
  124. } else if (rootKey == "HKCC"_s || rootKey == "HKEY_CURRENT_CONFIG"_s) {
  125. hRootKey = HKEY_CURRENT_CONFIG;
  126. } else if (rootKey == "HKU"_s || rootKey == "HKEY_USERS"_s) {
  127. hRootKey = HKEY_USERS;
  128. } else {
  129. throw registry_error(cmStrCat(rootKey, ": invalid root key."));
  130. }
  131. // Update path format
  132. auto key = ToWide(subKey);
  133. std::replace(key.begin(), key.end(), L'/', L'\\');
  134. REGSAM options = KEY_READ;
  135. if (Is64BitWindows()) {
  136. options |= view == View::Reg64 ? KEY_WOW64_64KEY : KEY_WOW64_32KEY;
  137. }
  138. HKEY hKey;
  139. LSTATUS status;
  140. if ((status = RegOpenKeyExW(hRootKey, key.c_str(), 0, options, &hKey)) !=
  141. ERROR_SUCCESS) {
  142. throw registry_error(FormatSystemError(status));
  143. }
  144. return KeyHandler(hKey);
  145. }
  146. KeyHandler KeyHandler::OpenKey(cm::string_view key, View view)
  147. {
  148. auto start = key.find_first_of("\\/"_s);
  149. return OpenKey(key.substr(0, start),
  150. start == cm::string_view::npos ? cm::string_view{ ""_s }
  151. : key.substr(start + 1),
  152. view);
  153. }
  154. std::string KeyHandler::FormatSystemError(LSTATUS status)
  155. {
  156. std::string formattedMessage{ "Windows Registry: unexpected error." };
  157. LPWSTR message = nullptr;
  158. DWORD size = 1024;
  159. if (FormatMessageW(
  160. FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, nullptr,
  161. status, 0, reinterpret_cast<LPWSTR>(&message), size, nullptr) != 0) {
  162. try {
  163. formattedMessage = cmTrimWhitespace(ToNarrow(message));
  164. } catch (...) {
  165. // ignore any exception because this method can be called
  166. // as part of the raise of an exception
  167. }
  168. }
  169. LocalFree(message);
  170. return formattedMessage;
  171. }
  172. std::wstring KeyHandler::ToWide(cm::string_view str)
  173. {
  174. std::wstring wstr;
  175. if (str.empty()) {
  176. return wstr;
  177. }
  178. auto const wlength =
  179. MultiByteToWideChar(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.data(),
  180. int(str.size()), nullptr, 0);
  181. if (wlength > 0) {
  182. auto wdata = cm::make_unique<wchar_t[]>(wlength);
  183. auto const r =
  184. MultiByteToWideChar(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.data(),
  185. int(str.size()), wdata.get(), wlength);
  186. if (r > 0) {
  187. wstr = std::wstring(wdata.get(), wlength);
  188. } else {
  189. throw registry_error(FormatSystemError(GetLastError()));
  190. }
  191. } else {
  192. throw registry_error(FormatSystemError(GetLastError()));
  193. }
  194. return wstr;
  195. }
  196. std::string KeyHandler::ToNarrow(wchar_t const* wstr, int size)
  197. {
  198. std::string str;
  199. if (size == 0 || (size == -1 && wstr[0] == L'\0')) {
  200. return str;
  201. }
  202. auto const length =
  203. WideCharToMultiByte(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, wstr, size,
  204. nullptr, 0, nullptr, nullptr);
  205. if (length > 0) {
  206. auto data = cm::make_unique<char[]>(length);
  207. auto const r =
  208. WideCharToMultiByte(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, wstr, size,
  209. data.get(), length, nullptr, nullptr);
  210. if (r > 0) {
  211. if (size == -1) {
  212. str = std::string(data.get());
  213. } else {
  214. str = std::string(data.get(), length);
  215. }
  216. } else {
  217. throw registry_error(FormatSystemError(GetLastError()));
  218. }
  219. } else {
  220. throw registry_error(FormatSystemError(GetLastError()));
  221. }
  222. return str;
  223. }
  224. std::string KeyHandler::ReadValue(cm::string_view name,
  225. ValueTypeSet supportedTypes,
  226. cm::string_view separator)
  227. {
  228. LSTATUS status;
  229. DWORD size;
  230. // pick-up maximum size for value
  231. if ((status = RegQueryInfoKeyW(this->Handler, nullptr, nullptr, nullptr,
  232. nullptr, nullptr, nullptr, nullptr, nullptr,
  233. &size, nullptr, nullptr)) != ERROR_SUCCESS) {
  234. throw registry_error(this->FormatSystemError(status));
  235. }
  236. auto data = cm::make_unique<BYTE[]>(size);
  237. DWORD type;
  238. auto valueName = this->ToWide(name);
  239. if ((status = RegQueryValueExW(this->Handler, valueName.c_str(), nullptr,
  240. &type, data.get(), &size)) != ERROR_SUCCESS) {
  241. throw registry_error(this->FormatSystemError(status));
  242. }
  243. auto valueType = ToValueType(type);
  244. if (!valueType || !supportedTypes.contains(*valueType)) {
  245. throw registry_error(cmStrCat(type, ": unsupported type."));
  246. }
  247. switch (type) {
  248. case REG_SZ:
  249. return this->ToNarrow(reinterpret_cast<wchar_t*>(data.get()));
  250. break;
  251. case REG_EXPAND_SZ: {
  252. auto expandSize = ExpandEnvironmentStringsW(
  253. reinterpret_cast<wchar_t*>(data.get()), nullptr, 0);
  254. auto expandData = cm::make_unique<wchar_t[]>(expandSize + 1);
  255. if (ExpandEnvironmentStringsW(reinterpret_cast<wchar_t*>(data.get()),
  256. expandData.get(), expandSize + 1) == 0) {
  257. throw registry_error(this->FormatSystemError(GetLastError()));
  258. } else {
  259. return this->ToNarrow(expandData.get());
  260. }
  261. } break;
  262. case REG_DWORD:
  263. return std::to_string(*reinterpret_cast<std::uint32_t*>(data.get()));
  264. break;
  265. case REG_QWORD:
  266. return std::to_string(*reinterpret_cast<std::uint64_t*>(data.get()));
  267. break;
  268. case REG_MULTI_SZ: {
  269. // replace separator with semicolon
  270. auto sep = this->ToWide(separator)[0];
  271. std::replace(reinterpret_cast<wchar_t*>(data.get()),
  272. reinterpret_cast<wchar_t*>(data.get()) +
  273. (size / sizeof(wchar_t)) - 1,
  274. sep, L';');
  275. return this->ToNarrow(reinterpret_cast<wchar_t*>(data.get()));
  276. } break;
  277. default:
  278. throw registry_error(cmStrCat(type, ": unsupported type."));
  279. }
  280. }
  281. std::vector<std::string> KeyHandler::GetValueNames()
  282. {
  283. LSTATUS status;
  284. DWORD maxSize;
  285. // pick-up maximum size for value names
  286. if ((status = RegQueryInfoKeyW(
  287. this->Handler, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
  288. nullptr, &maxSize, nullptr, nullptr, nullptr)) != ERROR_SUCCESS) {
  289. throw registry_error(this->FormatSystemError(status));
  290. }
  291. // increment size for final null
  292. auto data = cm::make_unique<wchar_t[]>(++maxSize);
  293. DWORD index = 0;
  294. DWORD size = maxSize;
  295. std::vector<std::string> valueNames;
  296. while ((status = RegEnumValueW(this->Handler, index, data.get(), &size,
  297. nullptr, nullptr, nullptr, nullptr)) ==
  298. ERROR_SUCCESS) {
  299. auto name = this->ToNarrow(data.get());
  300. valueNames.push_back(name.empty() ? "(default)" : name);
  301. size = maxSize;
  302. ++index;
  303. }
  304. if (status != ERROR_NO_MORE_ITEMS) {
  305. throw registry_error(this->FormatSystemError(status));
  306. }
  307. return valueNames;
  308. }
  309. std::vector<std::string> KeyHandler::GetSubKeys()
  310. {
  311. LSTATUS status;
  312. DWORD size;
  313. // pick-up maximum size for subkeys
  314. if ((status = RegQueryInfoKeyW(
  315. this->Handler, nullptr, nullptr, nullptr, nullptr, &size, nullptr,
  316. nullptr, nullptr, nullptr, nullptr, nullptr)) != ERROR_SUCCESS) {
  317. throw registry_error(this->FormatSystemError(status));
  318. }
  319. // increment size for final null
  320. auto data = cm::make_unique<wchar_t[]>(++size);
  321. DWORD index = 0;
  322. std::vector<std::string> subKeys;
  323. while ((status = RegEnumKeyW(this->Handler, index, data.get(), size)) ==
  324. ERROR_SUCCESS) {
  325. subKeys.push_back(this->ToNarrow(data.get()));
  326. ++index;
  327. }
  328. if (status != ERROR_NO_MORE_ITEMS) {
  329. throw registry_error(this->FormatSystemError(status));
  330. }
  331. return subKeys;
  332. }
  333. #endif
  334. // ExpressionParser: Helper to parse expression holding multiple
  335. // registry specifications
  336. class ExpressionParser
  337. {
  338. public:
  339. ExpressionParser(cm::string_view expression)
  340. : Expression(expression)
  341. , Separator(";"_s)
  342. , RegistryFormat{
  343. "\\[({.+})?(HKCU|HKEY_CURRENT_USER|HKLM|HKEY_LOCAL_MACHINE|HKCR|HKEY_"
  344. "CLASSES_"
  345. "ROOT|HKCC|HKEY_CURRENT_CONFIG|HKU|HKEY_USERS)[/\\]?([^]]*)\\]"
  346. }
  347. {
  348. }
  349. bool Find()
  350. {
  351. // reset result members
  352. this->RootKey = cm::string_view{};
  353. this->SubKey = cm::string_view{};
  354. this->ValueName = cm::string_view{};
  355. auto result = this->RegistryFormat.find(this->Expression);
  356. if (result) {
  357. auto separator = cm::string_view{
  358. this->Expression.data() + this->RegistryFormat.start(1),
  359. this->RegistryFormat.end(1) - this->RegistryFormat.start(1)
  360. };
  361. if (separator.empty()) {
  362. separator = this->Separator;
  363. } else {
  364. separator = separator.substr(1, separator.length() - 2);
  365. }
  366. this->RootKey = cm::string_view{
  367. this->Expression.data() + this->RegistryFormat.start(2),
  368. this->RegistryFormat.end(2) - this->RegistryFormat.start(2)
  369. };
  370. this->SubKey = cm::string_view{
  371. this->Expression.data() + this->RegistryFormat.start(3),
  372. this->RegistryFormat.end(3) - this->RegistryFormat.start(3)
  373. };
  374. auto pos = this->SubKey.find(separator);
  375. if (pos != cm::string_view::npos) {
  376. // split in ValueName and SubKey
  377. this->ValueName = this->SubKey.substr(pos + separator.size());
  378. if (Strucmp(this->ValueName, "(default)") == 0) {
  379. // handle magic name for default value
  380. this->ValueName = ""_s;
  381. }
  382. this->SubKey = this->SubKey.substr(0, pos);
  383. } else {
  384. this->ValueName = ""_s;
  385. }
  386. }
  387. return result;
  388. }
  389. #if defined(_WIN32) && !defined(__CYGWIN__)
  390. void Replace(std::string const& value)
  391. {
  392. this->Expression.replace(
  393. this->RegistryFormat.start(),
  394. this->RegistryFormat.end() - this->RegistryFormat.start(), value);
  395. }
  396. cm::string_view GetRootKey() const { return this->RootKey; }
  397. cm::string_view GetSubKey() const { return this->SubKey; }
  398. cm::string_view GetValueName() const { return this->ValueName; }
  399. std::string const& GetExpression() const { return this->Expression; }
  400. #endif
  401. private:
  402. std::string Expression;
  403. cm::string_view Separator;
  404. cmsys::RegularExpression RegistryFormat;
  405. cm::string_view RootKey;
  406. cm::string_view SubKey;
  407. cm::string_view ValueName;
  408. };
  409. }
  410. // class cmWindowsRegistry
  411. cmWindowsRegistry::ValueTypeSet const cmWindowsRegistry::SimpleTypes =
  412. cmWindowsRegistry::ValueTypeSet{ cmWindowsRegistry::ValueType::Reg_SZ,
  413. cmWindowsRegistry::ValueType::Reg_EXPAND_SZ,
  414. cmWindowsRegistry::ValueType::Reg_DWORD,
  415. cmWindowsRegistry::ValueType::Reg_QWORD };
  416. cmWindowsRegistry::ValueTypeSet const cmWindowsRegistry::AllTypes =
  417. cmWindowsRegistry::SimpleTypes + cmWindowsRegistry::ValueType::Reg_MULTI_SZ;
  418. cmWindowsRegistry::cmWindowsRegistry(cmMakefile& makefile,
  419. ValueTypeSet const& supportedTypes)
  420. #if defined(_WIN32) && !defined(__CYGWIN__)
  421. : SupportedTypes(supportedTypes)
  422. #else
  423. : LastError("No Registry on this platform.")
  424. #endif
  425. {
  426. #if defined(_WIN32) && !defined(__CYGWIN__)
  427. if (cmValue targetSize = makefile.GetDefinition("CMAKE_SIZEOF_VOID_P")) {
  428. this->TargetSize = targetSize == "8" ? 64 : 32;
  429. }
  430. #else
  431. (void)makefile;
  432. (void)supportedTypes;
  433. #endif
  434. }
  435. cm::optional<cmWindowsRegistry::View> cmWindowsRegistry::ToView(
  436. cm::string_view name)
  437. {
  438. static std::unordered_map<cm::string_view, cmWindowsRegistry::View>
  439. ViewDefinitions{
  440. { "BOTH"_s, View::Both }, { "HOST"_s, View::Host },
  441. { "TARGET"_s, View::Target }, { "32"_s, View::Reg32 },
  442. { "64"_s, View::Reg64 }, { "32_64"_s, View::Reg32_64 },
  443. { "64_32"_s, View::Reg64_32 }
  444. };
  445. auto it = ViewDefinitions.find(name);
  446. return it == ViewDefinitions.end()
  447. ? cm::nullopt
  448. : cm::optional<cmWindowsRegistry::View>{ it->second };
  449. }
  450. // define hash structure required by std::unordered_map
  451. namespace std {
  452. template <>
  453. struct hash<cmWindowsRegistry::View>
  454. {
  455. size_t operator()(cmWindowsRegistry::View v) const noexcept
  456. {
  457. return static_cast<
  458. typename underlying_type<cmWindowsRegistry::View>::type>(v);
  459. }
  460. };
  461. }
  462. cm::string_view cmWindowsRegistry::FromView(View view)
  463. {
  464. static std::unordered_map<cmWindowsRegistry::View, cm::string_view>
  465. ViewDefinitions{
  466. { View::Both, "BOTH"_s }, { View::Host, "HOST"_s },
  467. { View::Target, "TARGET"_s }, { View::Reg32, "32"_s },
  468. { View::Reg64, "64"_s }, { View::Reg32_64, "32_64"_s },
  469. { View::Reg64_32, "64_32"_s }
  470. };
  471. auto it = ViewDefinitions.find(view);
  472. return it == ViewDefinitions.end() ? ""_s : it->second;
  473. }
  474. cm::string_view cmWindowsRegistry::GetLastError() const
  475. {
  476. return this->LastError;
  477. }
  478. #if defined(_WIN32) && !defined(__CYGWIN__)
  479. std::vector<cmWindowsRegistry::View> cmWindowsRegistry::ComputeViews(View view)
  480. {
  481. switch (view) {
  482. case View::Both:
  483. switch (this->TargetSize) {
  484. case 64:
  485. return std::vector<View>{ View::Reg64, View::Reg32 };
  486. break;
  487. case 32:
  488. return Is64BitWindows()
  489. ? std::vector<View>{ View::Reg32, View::Reg64 }
  490. : std::vector<View>{ View::Reg32 };
  491. break;
  492. default:
  493. // No language specified, fallback to host architecture
  494. return Is64BitWindows()
  495. ? std::vector<View>{ View::Reg64, View::Reg32 }
  496. : std::vector<View>{ View::Reg32 };
  497. break;
  498. }
  499. break;
  500. case View::Target:
  501. switch (this->TargetSize) {
  502. case 64:
  503. return std::vector<View>{ View::Reg64 };
  504. break;
  505. case 32:
  506. return std::vector<View>{ View::Reg32 };
  507. break;
  508. default:
  509. break;
  510. }
  511. CM_FALLTHROUGH;
  512. case View::Host:
  513. return std::vector<View>{ Is64BitWindows() ? View::Reg64 : View::Reg32 };
  514. break;
  515. case View::Reg64_32:
  516. return Is64BitWindows() ? std::vector<View>{ View::Reg64, View::Reg32 }
  517. : std::vector<View>{ View::Reg32 };
  518. break;
  519. case View::Reg32_64:
  520. return Is64BitWindows() ? std::vector<View>{ View::Reg32, View::Reg64 }
  521. : std::vector<View>{ View::Reg32 };
  522. break;
  523. default:
  524. return std::vector<View>{ view };
  525. break;
  526. }
  527. }
  528. #endif
  529. cm::optional<std::string> cmWindowsRegistry::ReadValue(
  530. cm::string_view key, cm::string_view name, View view,
  531. cm::string_view separator)
  532. {
  533. #if defined(_WIN32) && !defined(__CYGWIN__)
  534. // compute list of registry views
  535. auto views = this->ComputeViews(view);
  536. if (Strucmp(name, "(default)") == 0) {
  537. // handle magic name for default value
  538. name = ""_s;
  539. }
  540. if (separator.empty()) {
  541. separator = "\0"_s;
  542. }
  543. for (auto v : views) {
  544. try {
  545. this->LastError.clear();
  546. auto handler = KeyHandler::OpenKey(key, v);
  547. return handler.ReadValue(name, this->SupportedTypes, separator);
  548. } catch (registry_error const& e) {
  549. this->LastError = e.what();
  550. continue;
  551. }
  552. }
  553. #else
  554. (void)key;
  555. (void)name;
  556. (void)view;
  557. (void)separator;
  558. #endif
  559. return cm::nullopt;
  560. }
  561. cm::optional<std::vector<std::string>> cmWindowsRegistry::GetValueNames(
  562. cm::string_view key, View view)
  563. {
  564. #if defined(_WIN32) && !defined(__CYGWIN__)
  565. this->LastError.clear();
  566. // compute list of registry views
  567. auto views = this->ComputeViews(view);
  568. std::vector<std::string> valueNames;
  569. bool querySuccessful = false;
  570. for (auto v : views) {
  571. try {
  572. auto handler = KeyHandler::OpenKey(key, v);
  573. auto list = handler.GetValueNames();
  574. std::move(list.begin(), list.end(), std::back_inserter(valueNames));
  575. querySuccessful = true;
  576. } catch (registry_error const& e) {
  577. this->LastError = e.what();
  578. continue;
  579. }
  580. }
  581. if (!valueNames.empty()) {
  582. // value names must be unique and sorted
  583. std::sort(valueNames.begin(), valueNames.end());
  584. valueNames.erase(std::unique(valueNames.begin(), valueNames.end()),
  585. valueNames.end());
  586. }
  587. if (querySuccessful) {
  588. // At least one query was successful, so clean-up any error message
  589. this->LastError.clear();
  590. return valueNames;
  591. }
  592. #else
  593. (void)key;
  594. (void)view;
  595. #endif
  596. return cm::nullopt;
  597. }
  598. cm::optional<std::vector<std::string>> cmWindowsRegistry::GetSubKeys(
  599. cm::string_view key, View view)
  600. {
  601. #if defined(_WIN32) && !defined(__CYGWIN__)
  602. this->LastError.clear();
  603. // compute list of registry views
  604. auto views = this->ComputeViews(view);
  605. std::vector<std::string> subKeys;
  606. bool querySuccessful = false;
  607. for (auto v : views) {
  608. try {
  609. auto handler = KeyHandler::OpenKey(key, v);
  610. auto list = handler.GetSubKeys();
  611. std::move(list.begin(), list.end(), std::back_inserter(subKeys));
  612. querySuccessful = true;
  613. } catch (registry_error const& e) {
  614. this->LastError = e.what();
  615. continue;
  616. }
  617. }
  618. if (!subKeys.empty()) {
  619. // keys must be unique and sorted
  620. std::sort(subKeys.begin(), subKeys.end());
  621. subKeys.erase(std::unique(subKeys.begin(), subKeys.end()), subKeys.end());
  622. }
  623. if (querySuccessful) {
  624. // At least one query was successful, so clean-up any error message
  625. this->LastError.clear();
  626. return subKeys;
  627. }
  628. #else
  629. (void)key;
  630. (void)view;
  631. #endif
  632. return cm::nullopt;
  633. }
  634. cm::optional<std::vector<std::string>> cmWindowsRegistry::ExpandExpression(
  635. cm::string_view expression, View view, cm::string_view separator)
  636. {
  637. #if defined(_WIN32) && !defined(__CYGWIN__)
  638. static std::string NOTFOUND{ "/REGISTRY-NOTFOUND" };
  639. this->LastError.clear();
  640. // compute list of registry views
  641. auto views = this->ComputeViews(view);
  642. std::vector<std::string> result;
  643. for (auto v : views) {
  644. ExpressionParser parser(expression);
  645. while (parser.Find()) {
  646. try {
  647. auto handler =
  648. KeyHandler::OpenKey(parser.GetRootKey(), parser.GetSubKey(), v);
  649. auto data = handler.ReadValue(parser.GetValueName(),
  650. this->SupportedTypes, separator);
  651. parser.Replace(data);
  652. } catch (registry_error const& e) {
  653. this->LastError = e.what();
  654. parser.Replace(NOTFOUND);
  655. continue;
  656. }
  657. }
  658. result.emplace_back(parser.GetExpression());
  659. if (expression == parser.GetExpression()) {
  660. // there no substitutions, so can ignore other views
  661. break;
  662. }
  663. }
  664. return result;
  665. #else
  666. (void)view;
  667. (void)separator;
  668. ExpressionParser parser(expression);
  669. if (parser.Find()) {
  670. // expression holds unsupported registry access
  671. // so the expression cannot be used on this platform
  672. return cm::nullopt;
  673. }
  674. return std::vector<std::string>{ std::string{ expression } };
  675. #endif
  676. }