«Хороший сайт делается для людей, а поисковики приходят следом»

— из принципов редакции SeoLiga.ru
Главная » Статьи » Delphi и C++ » Работа с реестром Windows из Delphi

Работа с реестром Windows из Delphi

Несмотря на то что многие программисты переходят на файлы конфигурации в формате 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 не пытается создавать разделы и не падает на защищённых ветках.

Типы данных и соответствующие методы

Тип значенияЧтениеЗапись
СтрокаReadStringWriteString
Расширяемая строкаReadStringWriteExpandString
Целое числоReadIntegerWriteInteger
ЛогическоеReadBoolWriteBool
ДробноеReadFloatWriteFloat
Дата и времяReadDateTimeWriteDateTime
Двоичные данныеReadBinaryDataWriteBinaryData

Если тип значения заранее неизвестен, используйте 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 и не мешает другим программам.

ПО
Павел Орлов
Автор SeoLiga.ru. Пишет о практике: что работает на реальных проектах, а что лучше обойти стороной. Хотите предложить свою статью? Загляните в раздел «Авторам».