windows_registry_setting.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. """
  2. Copyright (c) Contributors to the Open 3D Engine Project.
  3. For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. SPDX-License-Identifier: Apache-2.0 OR MIT
  5. Class for querying and setting a windows registry setting.
  6. """
  7. import pytest
  8. import logging
  9. from typing import List, Optional, Tuple, Any
  10. from winreg import (
  11. CreateKey,
  12. OpenKey,
  13. QueryValueEx,
  14. DeleteValue,
  15. SetValueEx,
  16. KEY_ALL_ACCESS,
  17. KEY_WRITE,
  18. REG_SZ,
  19. REG_MULTI_SZ,
  20. REG_DWORD,
  21. HKEY_CURRENT_USER,
  22. )
  23. from .platform_setting import PlatformSetting
  24. logger = logging.getLogger(__name__)
  25. class WindowsRegistrySetting(PlatformSetting):
  26. def __init__(self, workspace: pytest.fixture, subkey: str, key: str, hive: Optional[str] = None) -> None:
  27. super().__init__(workspace, subkey, key)
  28. self._hive = None
  29. try:
  30. if hive is not None:
  31. self._hive = self._str_to_hive(hive)
  32. except ValueError:
  33. logger.warning(f"Windows Registry Hive {hive} not recognized, using default: HKEY_CURRENT_USER")
  34. finally:
  35. if self._hive is None:
  36. self._hive = HKEY_CURRENT_USER
  37. def get_value(self, get_type: Optional[bool] = False) -> Any:
  38. """Retrieves the fast scan value in Windows registry (and optionally the type). If entry DNE, returns None."""
  39. if self.entry_exists():
  40. registryKey = OpenKey(self._hive, self._key)
  41. value = QueryValueEx(registryKey, self._subkey)
  42. registryKey.Close()
  43. # Convert windows data type to universal data type flag: PlatformSettings.DATA_TYPE
  44. # And handles unicode conversion for strings
  45. value = self._convert_value(value)
  46. return value if get_type else value[0]
  47. else:
  48. logger.warning(f"Could not retrieve Registry entry; key: {self._key}, subkey: {self._subkey}.")
  49. return None
  50. def set_value(self, value: Any) -> bool:
  51. """Sets the Windows registry value."""
  52. value, win_type = self._format_data(value)
  53. registryKey = None
  54. result = False
  55. try:
  56. CreateKey(self._hive, self._subkey)
  57. registryKey = OpenKey(self._hive, self._key, 0, KEY_WRITE)
  58. SetValueEx(registryKey, self._subkey, 0, win_type, value)
  59. result = True
  60. except WindowsError as e:
  61. logger.warning(f"Windows error caught while setting fast scan registry: {e}")
  62. finally:
  63. if registryKey is not None:
  64. # Close key if it's been opened successfully
  65. registryKey.Close()
  66. return result
  67. def delete_entry(self) -> bool:
  68. """Deletes the Windows registry entry for fast scan enabled"""
  69. try:
  70. if self.entry_exists():
  71. registryKey = OpenKey(self._hive, self._key, 0, KEY_ALL_ACCESS)
  72. DeleteValue(registryKey, self._subkey)
  73. registryKey.Close()
  74. return True
  75. except WindowsError:
  76. logger.error(f"Could not delete registry entry; key: {self._key}, subkey: {self._subkey}")
  77. finally:
  78. return False
  79. def entry_exists(self) -> bool:
  80. """Checks for existence of the setting in Windows registry."""
  81. try:
  82. # Attempt to open and query key. If fails then the entry DNE
  83. registryKey = OpenKey(self._hive, self._key)
  84. QueryValueEx(registryKey, self._subkey)
  85. registryKey.Close()
  86. return True
  87. except WindowsError:
  88. return False
  89. @staticmethod
  90. def _format_data(value: bool or int or str or List[str]) -> Tuple[int or str or List[str], int]:
  91. """Formats the type of the value provided. Returns the formatted value and the windows registry type (int)."""
  92. if type(value) == str:
  93. return value, REG_SZ
  94. elif type(value) == bool:
  95. value = "true" if value else "false"
  96. return value, REG_SZ
  97. elif type(value) == int or type(value) == float:
  98. if type(value) == float:
  99. logger.warning(f"Windows registry does not support floats. Truncating {value} to integer")
  100. value = int(value)
  101. return value, REG_DWORD
  102. elif type(value) == list:
  103. for single_value in value:
  104. if type(single_value) != str:
  105. # fmt:off
  106. raise ValueError(
  107. f"Windows Registry lists only support strings, got a {type(single_value)} in the list")
  108. # fmt:on
  109. return value, REG_MULTI_SZ
  110. else:
  111. raise ValueError(f"Windows registry expected types: int, str and [str], found {type(value)}")
  112. @staticmethod
  113. def _convert_value(value_tuple: Tuple[Any, int]) -> Tuple[Any, PlatformSetting.DATA_TYPE]:
  114. """Converts the Windows registry data and type (tuple) to a (standardized) data and PlatformSetting.DATA_TYPE"""
  115. value, windows_type = value_tuple
  116. if windows_type == REG_SZ:
  117. # Convert from unicode to string
  118. return value, PlatformSetting.DATA_TYPE.STR
  119. elif windows_type == REG_MULTI_SZ:
  120. # Convert from unicode to string
  121. return [string for string in value], PlatformSetting.DATA_TYPE.STR_LIST
  122. elif windows_type == REG_DWORD:
  123. return value, PlatformSetting.DATA_TYPE.INT
  124. else:
  125. raise ValueError(f"Type flag not recognized: {windows_type}")
  126. @staticmethod
  127. def _str_to_hive(hive_str: str) -> int:
  128. """Converts a string to a Windows Registry Hive enum (int)"""
  129. from winreg import HKEY_CLASSES_ROOT, HKEY_CURRENT_CONFIG, HKEY_LOCAL_MACHINE, HKEY_USERS
  130. lower = hive_str.lower()
  131. if lower == "hkey_current_user" or lower == "current_user":
  132. return HKEY_CURRENT_USER
  133. elif lower == "hkey_classes_root" or lower == "classes_root":
  134. return HKEY_CLASSES_ROOT
  135. elif lower == "hkey_current_config" or lower == "current_config":
  136. return HKEY_CURRENT_CONFIG
  137. elif lower == "hkey_local_machine" or lower == "local_machine":
  138. return HKEY_LOCAL_MACHINE
  139. elif lower == "hkey_users" or lower == "users":
  140. return HKEY_USERS
  141. else:
  142. raise ValueError(f"Hive: {hive_str} not recognized")