Изучение работы Active Setup и получение его компонентов
Знаю, что Active Setup используется (1, 2) для выполнения команд и он хранится в реестре.
Помогите разобраться в теме подробнее и научиться вытаскивать компоненты Active Setup
Для работы с реестром использую winreg и функции, из прошлых наработок, для получения значений из реестра:
import os
import winreg
from dataclasses import dataclass
from typing import Dict, List, Optional, Tuple, Union, Any
from winreg import QueryInfoKey, EnumKey, EnumValue, OpenKey, HKEYType
@dataclass
class Entry:
name: str
value: Any
type: int
def expand_registry_key(key: str) -> str:
return {
'HKCU': 'HKEY_CURRENT_USER',
'HKLM': 'HKEY_LOCAL_MACHINE',
}.get(key, key)
def get_key(path: str) -> Optional[HKEYType]:
# Example:
# path = r"HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders"
# registry_key_name = "HKEY_LOCAL_MACHINE"
# relative_path = r"Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders"
registry_key_name, relative_path = path.split('\\', maxsplit=1)
registry_key_name = expand_registry_key(registry_key_name)
registry_key = getattr(winreg, registry_key_name)
try:
return OpenKey(registry_key, relative_path)
except:
return
def get_subkeys(path: str) -> List[Tuple[str, HKEYType]]:
items = []
key = get_key(path)
if not key:
return items
number_of_subkeys, _, _ = QueryInfoKey(key)
for i in range(number_of_subkeys):
sub_key_name = EnumKey(key, i)
sub_key = OpenKey(key, sub_key_name)
items.append((sub_key_name, sub_key))
return items
def get_entries_as_dict(path: Union[str, HKEYType], raw_value=False, expand_vars=True) -> Dict[str, Union[Entry, Any]]:
return {
entry.name: entry.value if raw_value else entry
for entry in get_entries(path, expand_vars)
}
def get_entries(path: Union[str, HKEYType], expand_vars=True) -> List[Entry]:
items = []
if isinstance(path, HKEYType):
key = path
else:
key = get_key(path)
if not key:
return items
_, number_of_values, _ = QueryInfoKey(key)
for i in range(number_of_values):
name, value, type_value = EnumValue(key, i)
if expand_vars and isinstance(value, str):
value = os.path.expandvars(value)
items.append(
Entry(name, value, type_value)
)
return items
Например, так можно получить все ключи по указанному пути, после по первому ключу получить параметры:
subkeys = get_subkeys(r'HKLM\Software\Microsoft\Active Setup\Installed Components')
_, sub_key = subkeys[0]
print(get_entries_as_dict(sub_key, raw_value=True))
# {'': 'Microsoft Windows Media Player', 'ComponentID': 'WMPACCESS', 'DontAsk': 2, 'Enabled': 1, 'IsInstalled': 0, 'Locale': '*', 'LocalizedName': '@C:\\Windows\\system32\\wmploc.dll,-128', 'StubPath': 'C:\\Windows\\system32\\unregmp2.exe /ShowWMP', 'Version': '12,0,10011,16384'}
Эта же запись в реестре (regedit):
Ответы (1 шт):
Active Setup это команды, что запускаются один раз при LOGON. Операционная система смотрит в компоненты ветки HKEY_LOCAL_MACHINE (HKLM) и если есть отличия от компонентов в ветки HKEY_CURRENT_USER (HKCU), то происходит обновление компонентов в HKCU и выполнение команд.
Пути реестра:
HKEY_LOCAL_MACHINE\Software\Microsoft\Active Setup\Installed ComponentsHKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Active Setup\Installed ComponentsHKEY_CURRENT_USER\Software\Microsoft\Active Setup\Installed ComponentsHKEY_CURRENT_USER\Software\Wow6432Node\Microsoft\Active Setup\Installed Components
Описание полей:
| Поле | Комментарий |
|---|---|
| GUID | Каждый компонент имеет уникальную строку в названии ключа. В ключе не обязательно должен быть GUID, просто он позволяет упростить условие уникальности |
| Default Value | Пустое имя параметра. Опциональное имя компоненты |
| IsInstalled | По-умолчанию, 1. Возможные значения: 0 - команда компоненты не будет выполнена, 1 - команда будет выполнена. |
| Locale | Произвольная строка, нет проверки ее формата, поэтому может быть что угодно написано, а не например значения "ru", "en". Если это поле в HKLM отличается от компоненты в HKCU (или ее нет), то команда запускается |
| StubPath | Команда, что будет выполнена. Может быть путем к файлу или командной строкой |
| Version | Формат: 4 числа, разделенных запятыми, пример: 1,2,3,4. Определяет будет ли запуск компоненты: если версия в HKCU отсутствует или меньше версии в HKLM |
Есть и другие поля, но по ним не нашел описания и есть подозрение, что они не задокументированы. Например, встречался параметр "Localized Name" только для "Microsoft Edge" и "LocalizedName" для остальных компонентов. Поэтому, поля, что не входят в список полей Component будут в поле other_fields (см. в реализации).
Реализация:
Действия:
- Функции из вопрос перенес в
common.py - Добавил класс
Componentи описал поля в соответствии с таблицей выше - Сделал функцию
get_active_setup_components, что перебирает путиActive Setupи собирает список изComponent
from dataclasses import dataclass, field
from typing import Dict, Any, List
from common import get_subkeys, get_entries_as_dict
PATHS = [
r'HKLM\Software\Microsoft\Active Setup\Installed Components',
r'HKLM\Software\Wow6432Node\Microsoft\Active Setup\Installed Components',
r'HKCU\Software\Microsoft\Active Setup\Installed Components',
r'HKCU\Software\Wow6432Node\Microsoft\Active Setup\Installed Components',
]
@dataclass
class Component:
guid: str
default: str = ''
is_installed: bool = True
locale: str = ''
stub_path: str = ''
version: str = ''
other_fields: Dict[str, Any] = field(default_factory=dict)
@classmethod
def create(cls, guid: str, fields: Dict[str, Any]) -> 'Component':
return cls(
guid=guid,
default=fields.pop('', ''),
is_installed=fields.pop('IsInstalled', True),
locale=fields.pop('Locale', ''),
stub_path=fields.pop('StubPath', ''),
version=fields.pop('Version', ''),
other_fields=fields,
)
def get_active_setup_components(exists_stub_path=True) -> Dict[str, List[Component]]:
path_by_items = dict()
for path in PATHS:
if path not in path_by_items:
path_by_items[path] = []
for sub_key_name, sub_key in get_subkeys(path):
entries = get_entries_as_dict(sub_key, raw_value=True)
component = Component.create(sub_key_name, entries)
# Если задана проверка наличия stub_path и он пустой
if exists_stub_path and not component.stub_path.strip():
continue
path_by_items[path].append(component)
return path_by_items
Пример использования функции:
path_by_components = get_active_setup_components()
for path, components in path_by_components.items():
print(f'{path} ({len(components)}):')
for component in components:
print(f' {component}')
print()
