Несмотря на то что многие программисты переходят на файлы конфигурации в формате JSON или XML, реестр Windows по-прежнему широко используется. В нём хранят пути установки, ассоциации файлов, параметры автозапуска, пользовательские настройки. Для Delphi работа с реестром давно упрощена: в стандартной поставке есть модуль Registry с классом TRegistry, который скрывает низкоуровневые вызовы WinAPI. В статье разберём, как им пользоваться уверенно и без неприятных сюрпризов.
Структура реестра в двух словах
Реестр — это иерархическая база данных, состоящая из разделов (ключей) и значений. Верхний уровень образуют корневые ключи, из которых чаще всего нужны два.
- HKEY_CURRENT_USER — настройки текущего пользователя. Писать сюда можно без прав администратора, и именно сюда следует сохранять параметры вашей программы.
- HKEY_LOCAL_MACHINE — настройки всей машины. Запись обычно требует повышенных прав, а на современных системах без них просто завершится ошибкой.
- HKEY_CLASSES_ROOT — ассоциации типов файлов и COM-регистрация.
Значения имеют типы: строка, расширяемая строка с переменными окружения, целое число, двоичные данные, многострочный текст. TRegistry поддерживает все основные типы через отдельные методы.
Первое знакомство с TRegistry
Базовый сценарий выглядит так: создали объект, указали корневой ключ, открыли нужный раздел, прочитали или записали значение, закрыли и освободили объект. Забывать про освобождение не стоит — используйте try..finally.
uses Registry, Windows;
procedure SaveSettings;
var
Reg: TRegistry;
begin
Reg := TRegistry.Create;
try
Reg.RootKey := HKEY_CURRENT_USER;
if Reg.OpenKey('\Software\MyCompany\MyApp', True) then
begin
Reg.WriteString('UserName', 'Ivan');
Reg.WriteInteger('WindowWidth', 800);
Reg.WriteBool('ShowHints', True);
Reg.CloseKey;
end;
finally
Reg.Free;
end;
end;
Второй параметр метода OpenKey — флаг создания. Если он равен True, отсутствующий раздел будет создан автоматически вместе со всеми промежуточными. Если False, при отсутствии ключа метод просто вернёт False.
Чтение значений безопасно
Самая частая ошибка — читать значение, не проверив, что оно существует. В этом случае ReadString или ReadInteger выбросят исключение. Поэтому перед чтением используйте ValueExists.
function LoadWidth: Integer;
var
Reg: TRegistry;
begin
Result := 640; // значение по умолчанию
Reg := TRegistry.Create(KEY_READ);
try
Reg.RootKey := HKEY_CURRENT_USER;
if Reg.OpenKeyReadOnly('\Software\MyCompany\MyApp') then
try
if Reg.ValueExists('WindowWidth') then
Result := Reg.ReadInteger('WindowWidth');
finally
Reg.CloseKey;
end;
finally
Reg.Free;
end;
end;
Обратите внимание на два приёма. Во-первых, конструктор получает маску доступа KEY_READ: мы сообщаем системе, что собираемся только читать, и не требуем лишних прав. Во-вторых, OpenKeyReadOnly не пытается создавать разделы и не падает на защищённых ветках.
Типы данных и соответствующие методы
| Тип значения | Чтение | Запись |
|---|---|---|
| Строка | ReadString | WriteString |
| Расширяемая строка | ReadString | WriteExpandString |
| Целое число | ReadInteger | WriteInteger |
| Логическое | ReadBool | WriteBool |
| Дробное | ReadFloat | WriteFloat |
| Дата и время | ReadDateTime | WriteDateTime |
| Двоичные данные | ReadBinaryData | WriteBinaryData |
Если тип значения заранее неизвестен, используйте GetDataType, который вернёт rdString, rdInteger, rdBinary и так далее. Это пригодится при написании универсального просмотрщика реестра.
Перечисление разделов и значений
Чтобы пройти по содержимому раздела, применяют GetKeyNames и GetValueNames. Оба метода заполняют список строк.
procedure ListSubKeys(Output: TStrings);
var
Reg: TRegistry;
begin
Reg := TRegistry.Create(KEY_READ);
try
Reg.RootKey := HKEY_CURRENT_USER;
if Reg.OpenKeyReadOnly('\Software\MyCompany') then
begin
Reg.GetKeyNames(Output);
Reg.CloseKey;
end;
finally
Reg.Free;
end;
end;
Для получения подробностей о ключе (количество подразделов, размер самого длинного значения) есть структура TRegKeyInfo и метод GetKeyInfo. Это полезно, когда нужно заранее выделить буфер.
Удаление и переименование
Для удаления значения служит DeleteValue, для удаления раздела — DeleteKey. Помните: DeleteKey удалит раздел, если у него нет подразделов, на некоторых версиях системы. Для рекурсивной очистки лучше написать собственную процедуру, обходящую подразделы, либо использовать функцию SHDeleteKey. Прежде чем удалять что-либо за пределами своей ветки, трижды подумайте: ошибка в пути может сломать чужую программу или систему.
Переименовать раздел можно методом MoveKey, который копирует содержимое в новое место, а затем при необходимости удаляет старое. Для значений есть RenameValue.
Автозапуск программы
Одна из популярных задач — добавить программу в автозагрузку. Делается это записью в раздел Run.
procedure SetAutoRun(Enable: Boolean);
const
RunKey = '\Software\Microsoft\Windows\CurrentVersion\Run';
var
Reg: TRegistry;
begin
Reg := TRegistry.Create;
try
Reg.RootKey := HKEY_CURRENT_USER;
if Reg.OpenKey(RunKey, True) then
try
if Enable then
Reg.WriteString('MyApp', '"' + ParamStr(0) + '"')
else if Reg.ValueExists('MyApp') then
Reg.DeleteValue('MyApp');
finally
Reg.CloseKey;
end;
finally
Reg.Free;
end;
end;
Путь к исполняемому файлу берите в кавычки: так запуск не сломается, если в пути есть пробелы. Запись в HKEY_CURRENT_USER не требует прав администратора, поэтому для пользовательских программ этот вариант предпочтителен.
32 и 64 бита: подводный камень
На 64-разрядной системе 32-разрядные приложения по умолчанию попадают в отдельное представление реестра. Данные в ветке Software\Wow6432Node — это тот самый «зеркальный» вариант, и ваша программа может искать значение там, где его нет. Чтобы явно указать нужное представление, добавьте флаги к маске доступа.
Reg := TRegistry.Create(KEY_READ or KEY_WOW64_64KEY);
Флаг KEY_WOW64_64KEY просит доступ к 64-разрядному представлению, KEY_WOW64_32KEY — к 32-разрядному. Если ваша программа читает данные чужого установщика, этот нюанс может стоить вам часа отладки.
Хранение настроек: лучшие практики
- Храните настройки в HKEY_CURRENT_USER\Software\ВашаКомпания\ВашаПрограмма. Не засоряйте корень.
- Всегда задавайте значения по умолчанию при чтении.
- Не храните в реестре большие данные: для этого есть файлы.
- Пароли не пишите в открытом виде. Если без хранения не обойтись, используйте шифрование средствами системы.
- Оборачивайте работу в try..except и сообщайте пользователю понятную ошибку, а не показывайте системный диалог.
- Для переносимых версий программы предусмотрите альтернативу: хранение в INI-файле рядом с программой. Можно использовать TRegistryIniFile, чтобы код чтения и записи не менялся.
Типичные ошибки
Список проблем, с которыми сталкивается почти каждый, кто начинает работать с реестром: не закрыт ключ после работы, забыто освобождение объекта, запись в HKEY_LOCAL_MACHINE без прав, неверно написан путь (лишний или отсутствующий обратный слеш в начале), попытка прочитать значение другого типа. Если ReadInteger выдаёт ошибку, проверьте через GetDataType, что в реестре действительно число, а не строка с цифрами.
Ещё одна тонкость — права. Если ваш инсталлятор выполнялся от администратора, а программа запускается от обычного пользователя, ключи в HKEY_LOCAL_MACHINE будут доступны только на чтение. Планируйте это заранее и разделяйте общие и пользовательские параметры.
Заключение
TRegistry — простой и надёжный инструмент: десяток методов покрывает почти все повседневные задачи. Помните про правильные маски доступа, проверяйте существование значений, не забывайте закрывать ключи и учитывайте различия между 32- и 64-разрядными представлениями. Следуя этим правилам, вы получите аккуратный код, который одинаково хорошо работает на разных версиях Windows и не мешает другим программам.