В данной статье вы узнаете основные понятия необходимые для работы с реестром и научитесь легко и быстро его изменять через Unishell.

Что такое реестр Windows?

Реестр - это база данных, которая хранит настройки приложений( как пользовательских, так и системных) для всех пользователей компьютера.

Зачем мне реестр может понадобиться?

Реестр может понадобиться для добавления в автозагрузку, добавление ассоциаций файлов, контекстного меню, изменение Environment и PATH, взаимодействие с настройками других приложений, backup реестра, сохранение своих настроек для всех/текущего пользователя. Для просмотра реестра можно открыть редактор реестра: WIN + R и ввести regedit.

Основные разделы реестра (кусты)

В реестре есть основные два куста (корневые разделы реестра) HKLM и HKU, остальные являются ссылками на эти два куста. Первый содержит данные для всего компьютера, второй - информацию для каждого пользователя. Без прав администратора можно изменять только данные текущего пользователя в HKU (и то не все).

Но работать с этими кустами напрямую не всегда удобно, поэтому часто используют ссылки на них (псевдонимы).

  • HKLM(HKEY_LOCAL_MACHINE) - данные для всего компьютера

  • HKU(HKEY_USERS) - данные для всех пользователей

  • HKCU(HKEY_CURRENT_USER) - данные для текущего пользователя(ссылка на HKU)

  • HKCR(HKEY_CLASSES_ROOT) - данные ассоциаций файлов и папок( изменения сохраняются в HKCU, при получении являются объединением HKLM и HKCU, при совпадении берутся из HKCU)

  • HKCC(HKEY_CURRENT_CONFIG) - данные текущей аппаратной конфигурации( ссылка на HKLM)

Типы данных и устройство реестра

Реестр можно представить в виде файловой системы, у которой есть несколько дисков - кустов(HKU, ...), папок - ключей(subkey), файлов - полей с данными(field). Причем у ключа может быть тоже значение(поле), но в большинстве случаев оно не используется. Пользователь идентифицируется по SID.

Типы полей реестра(рекомендуемые для использования):

Имя типа в Unishell

Имя типа в реестре

Назначение

STRING

REG_SZ

обычная строка

STRINGS

REG_MULTI_SZ

список строк

PATH

REG_EXPAND_SZ

строка, в которую при вызове Windows подставляются значения,например, "%USERPROFILE%", обычно нужна в путях которые вызывает сам Windows( ассоциация файлов)

INTEGER

REG_DWORD

целое число(4 байта), используется как для хранения чисел, так и флагов( т.к. типа bool нет в реестре)

LONG_INT

REG_QWORD

целое число(8 байт)

BINARY

REG_BINARY

бинарные данные

Также есть и другие типы данных, но скорей всего они вам не понадобятся, так как используются в основном системой.

Что же такое Unishell?

Это библиотека, предоставляющая унифицированный интерфейс, цель которого упростить работу на разных платформах. API стремится к обьектно-ориентированной модели и максимальному использованию свойств(property). Приоритет делается на читаемость кода.

Работа с реестром через Unishell

Установка библиотеки:

pip install unishell

Есть три типа для кого производятся изменения в Unishell:

from unishell.regedit import CurUser,User,Users
CurUser # Обьект для работы с текущим пользователем 
User("sid") # Обьект для работы с конкретным пользователем
Users # Обьект для работы со всеми пользователями

Все они могут заменять друг друга в большинстве случаев. Под капотом они автоматически определяют в какой куст реестра писать данные.

При работе с реестром желательно сделать его резервную копию через regedit.

Для начала подключимся к кусту текущего пользователя( в реальных задачах не рекомендуется использовать import *). Рекомендуется использовать 1 или 4 вариант, в целом они все идентичны. :

from unishell.regedit import *
import winreg
registry = Registry("HKCU")
registry2 = Registry(winreg.HKEY_CURRENT_USER) 

user_sid = CurUser.id
registry3 = Registry(user_sid) # Получение куста(раздела реестра) по SID пользователя
registry4 = Registry(CurUser) # или Users/User("sid")

Перемещение по ключам происходит через "/". ВАЖНО( реестр является регистронезависимым для доступа, формат сохраняется при создании ключа):

#Это одно и тоже
software = registry / "SOFTWARE" 
software = registry / "software"

# просмотр содержимого ключа(в Unishell ключ is Container)
registry.containers() # ['AppEvents', ..., 'SOFTWARE',...] 
registry.fields() # []

Создадим настройки своего приложения для текущего пользователя( приложения обычно пишут свои настройки в HKCU / Software/ <MyApp>, что не требует прав админа):

myapp = registry / "SOFTWARE" / "MyApp"

myapp["version"] = "1.2.3"                                 #STRING
myapp["dependies"] = ["winreg","unishell"]                 #STRINGS
myapp["num_load"] = 0                                      #INTEGER
myapp["int64"] = 2**32 + 5                                 #LONG_INT
myapp["all_users"] = False                                 #INTEGER
myapp["dir_for_install"] = "%USERPROFILE%\\AppData\\MyApp" #PATH
myapp["bin"] = bytes([1,1,1])                              #BINARY

Если есть необходимость использовать определенный тип( работает быстрее):

from unishell.regedit.reg_types import STRING
field = Field(myapp,"dir_for_install")
field.set("%USERPROFILE%\\AppData\\MyApp",STRING) 

Дополнительные полезные методы Container и Field:

myapp.create() #создает ключ
myapp.exists() # True
myapp.rename("NewName")
myapp["all_users"] # 0
del myapp["all_users"]
myapp.delete() # рекурсивно удаляет ключ и его данные

field.get(default = None, return_type = False)
field.exists() # True
field.rename("NewName")
field.delete()

Методы выгрузки / загрузки данных из реестра

Загрузить/ получить данные можно в двух форматах с типами или без. Рекомендуется использовать с типами для уменьшения ошибок. ВАЖНО: Unishell бинарные данные принимает/возвращает в типе bytes, который не является типом json и требует преобразования.Загрузка данных в реестр для нашего приложения из json:

# с типами
config = {
  'version': ('1.2.3', 'STRING'), 
  'dependies': (['winreg', 'unishell'], 'STRINGS'), 
  'num_load': (0, 'INTEGER'), 
  'int64': (4294967301, 'LONG_INT'),
  'all_users': (0, 'INTEGER'), 
  'dir_for_install': ('%USERPROFILE%\\AppData\\MyApp', 'PATH'), 
  'bin': (b'\x01\x01\x01', 'BINARY')}

myapp.from_json(config,use_types = True)

Backup куста реестра(требует прав Администратора для загрузки):

from unishell import File
import json
import base64

class BytesEncoder(json.JSONEncoder):   
    def default(self, obj):
        if isinstance(obj, bytes):
            return {
                '__type__': 'bytes',
                'value': base64.b64encode(obj).decode('utf-8')
            }
        return super().default(obj)

def bytes_decoder(dct):
    if '__type__' in dct and dct['__type__'] == 'bytes':
        return base64.b64decode(dct['value'].encode('utf-8'))
    return dct

SOFTWARE_backup = json.dumps(software.json(use_types=True),cls=BytesEncoder)
File("SOFTWARE_backup.json")< SOFTWARE_backup

SOFTWARE_backup = File("HKCU_backup.json").content
SOFTWARE_json = json.loads(SOFTWARE_backup, object_hook=bytes_decoder)
software.from_json(SOFTWARE_json,use_types=True) #Требуются права админа

Пробовал выгрузить куст текущего пользователя с помощью regedit и Unishell, вследствие чего получил следующую таблицу(Windows 10):

Размер .reg

Размер json(use_types = True)

Размер json( use_types = False)

100 Мб

24 Мб

23Мб

Как ни странно получилось, что json весит значительно меньше чем reg, возможно структура reg не очень экономична. Вследствие этой таблицы рекомендую использовать json(use_types = True) для некорневых разделов, для кустов это не полностью работает( права администратора не все разрешают изменять).

Заключение

В ходе данной статьи были рассмотрены базовые принципы работы с реестром с помощью Unishell. Некоторые подробности (типы данных и др.) были опущены для того, чтобы не раздувать статью. Дополнения по устройству Реестра Windows и работы Unishell планируются в следующих статьях. Важно смотреть версию Unishell(API ее может меняться).

P. S: являюсь разработчиком Unishell, прошу прокомментировать ее API, основную часть документации планирую выкладывать на Хабре. Развиваю идею кроссплатформенной cmd. На данный момент в приоритете разработка под Windows. Позже будут аналоги под Unix с аналогичным интерфейсом. Дополнения по устройству Windows и работы Unishell планируются в следующих статьях.