Описание функций работы с реестром Windows

Реестр - это централизованное хранилище настроек как ОС Windows, так и приложений. Правила хорошего тона требуют, чтобы язык программирования позволял читать данные реестра и писать в реестр свои данные. В HomeLisp встроен небольшой набор функций манипулирования реестром. Все эти функции являются простыми надстройками над соответствующими функциями Windows-API.

Информация в реестре хранится в иерархическом виде - она включает разделы (ключи) и параметры. Проводя аналогию с файловой системой, можно сказать, что разделы - это папки, а параметры - это файлы. На самом верхнем уровне иерархии находятся корневые разделы: HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_CURRENT_CONFIG, а в системах Win-9x/Me еще и HKEY_DYN_DATA. За каждым из корневых разделов реестра закрепляется битовая константа в соответствии со следующей таблицей:

 
Имя корневого раздела Битовая константа-обозначение
HKEY_CLASSES_ROOT &H80000000
HKEY_CURRENT_USER &H80000001
HKEY_LOCAL_MACHINE &H80000002
HKEY_USERS &H80000003
HKEY_CURRENT_CONFIG &H80000005
HKEY_DYN_DATA &H80000006


Прежде, чем начать работать с каким-либо разделом реестра, его следует открыть. Для этого существует две функции REGOPENKEY и REGCREATEKEY. Первая из них открывает существующий раздел, вторая - позволяет создать несуществующий раздел и затем открыть его. У обеих функций раздел задается двумя параметрами - обозначением корневой ветви и "путем" внутри ветви. При успешном открытии функции возвращают т.н. манипулятор - целое число. Его следует сохранить, поскольку в большинстве операций ссылка на раздел осуществляется через его манипулятор.

При открытии раздела можно ограничить операции, выполняемые с манипулятором - функции открытия/создания требуют задания битовой константы, задающей права доступа:

 
Битовая константа Права доступа
KEY_ALL_ACCESS = &HF003F Полный доступ
KEY_CREATE_SUB_KEY = &H4 Разрешено создавать подразделы
KEY_ENUMERATE_SUB_KEYS = &H8 Разрешено перечисление подразделов
KEY_READ = &H20019
Общее разрешение операций чтения
KEY_QUERY_VALUE = &H1 Разрешено запрашивать данные подразделов
KEY_SET_VALUE = &H2 Разрешено устанавливать данные подразделов
KEY_WRITE = &H20006 Общее разрешение операций записи


Функция REGENUMKEY позволяет перечислить подразделы ("папки") открытого раздела. Функция REGENUMVALUE позволяет перечислить параметры ("файлы") открытого раздела. Перечисление означает получение имени подраздела (параметра) по его номеру. Объекты нумеруются с нуля. Нет возможности узнать сколько подразделов (параметров) содержит раздел. Поэтому единственным способом перечисления всех разделов (параметров) является итерационный цикл с получением значений объектов с номерами 0, 1, 2 и т.д., до тех пор, пока функция не вернет код ошибки.

При открытом разделе возможно получение значения параметра по имени. Для этого служит функция REGQUERYVALUE. Для того, чтобы установить значение параметра или создать новый параметр с нужным значением служит функция REGSETVALUE.

Для удаления ключа со всем его содержимым служит функция REGDELETEKEY. Раздел при этом открывать не требуется. Для удаления параметра открытого раздела можно использовать функцию REGDELETEVALUE. Раздел, из которого удаляется параметр, должен быть открыт.

Открытый раздел реестра после завершения работы с ним следует закрыть, для чего служит функция REGCLOSEKEY.

Системный реестр легко испортить, что может серьезно нарушить работоспособность Windows. Автор не возлагает на себя ответственность за возможную порчу реестра пользователями HomeLisp, и усиленно рекомендует сделать предварительную копию реестра - запустить regedit.exe и далее в меню: Реестр -> Экспорт файла реестра (нажать радиокнопку "весь реестр"); задать имя файла и директорию; нажать "OK".

Впрочем, настоящие программисты ничего не боятся...

Имена всех функций, обслуживающих реестр, для удобства начинаются с префикса REG. Функции работы с реестром принадлежат к классу SUBR - их аргументы вычисляются. Функции доступны при работе в среде разработки, в составе EXE-файла, а также из библиотеки HomeLispLib.Exe. При работе WEB-компоненты функции недоступны.

Имя функции К-во аргументов Тип аргументов Выполняемое действие
REGCLOSEKEY   1 FIXED Закрывает манипулятор раздела
REGCREATEKEY   4 1-BITS,
2-STRING,
3-FIXED,
4-BITS
Открывает существующий ключ(раздел) или создает новый.
REGDELETEKEY   2 1-BITS,
2-STRING
Удаляет раздел вместе со всеми подразделами и параметрами.
REGDELETEVALUE   2 1-FIXED,
2-STRING
Удаляет параметр из предварительно открытого раздела
REGENUMKEY   2 FIXED Дает значение ключа из предварительно открытого раздела
REGENUMVALUE   2 FIXED Дает значение параметра из предварительно открытого раздела
REGOPENKEY   3 1-BITS,
2-STRING,
3-BITS
Открывает существующий раздел
REGQUERYVALUE   2 1-FIXED,
2-STRING
Получение значения параметра
REGSETVALUE   2 1-FIXED,
2-STRING,
3-FIXED,
4-ANY
Установление значения параметра
REGCLOSEKEY  

Функция REGCLOSEKEY закрывает открытый ранее раздел реестра. Функция требует единственный параметр типа FIXED - манипулятор открытого раздела. При успешном завершении функция возвращает T, в противном случае возвращается список из двух целых: -1 и целочисленный код ошибки. Вот примеры закрытия разделов:


(setq h (car (regCreateKey HKEY_CLASSES_ROOT "HomeLisp" 0 KEY_ALL_ACCESS)))

==> -1030057424

(regCloseKey h)

==> T

(regCloseKey -999)

==> (-1 1114)

Здесь создается и открывается раздел HKEY_CLASSES_ROOT\HomeLisp, и сразу же закрывается. Попытка закрыть несуществующий манипулятор раздела вызывает ошибку с кодом 1114.

REGCREATEKEY  

Функция REGCREATEKEY позволяет создать несуществующий раздел реестра и открыть его. Если раздел уже существует, то функция открывает его.

Первым параметром функции должна быть битовая константа, идентифицирующая одну из корневых ветвей реестра. Вторым параметром должна быть строка - имя раздела. Имя раздела может содержать разделить уровней - обратный слэш. В этом случае функция создаст необходимую цепочку вложенных разделов. Третьим параметром функция требует целое число (FIXED), задающее статус создаваемого ключа. Если задан нуль, то ключ будет сохранен в реестре, если задана единица - то ключ сохранится только до перезагрузки Windows. Последний четвертый параметр задает права доступа к заданному манипулятору. Права доступа - это одна из битовых констант.

Функция всегда возвращает список из двух целых. Признаком успешного завершения является отличие головы списка от -1. В этом случае голова списка является манипулятором открытого раздела. Второй элемент списка может примать значения 1 или 2. Единица означает, что требуемый раздел создан в процессе вызова. Двойка означает, что раздел существовал ранее.


(regCreateKey &H12 "HomeLisp" 0 KEY_ALL_ACCESS)

==> (-1 1010)

(regCreateKey HKEY_CLASSES_ROOT "HomeLisp" 0 KEY_ALL_ACCESS)

==> (-1029447712 2)

(regCloseKey -1029447712)

==> T

(regCreateKey HKEY_CLASSES_ROOT "HomeLisp" 0 KEY_ALL_ACCESS)

==> (-1029562304 1)

(regCreateKey &H666 "HomeLisp" 0 KEY_ALL_ACCESS)

==> (-1 1010)

(regCloseKey -1029562304)

==> T

Здесь создается раздел HKEY_CLASSES_ROOT\HomeLisp. При первом вызове второй элемент списка равен 1, что говорит о том, что раздел до вызова не существовал. При втором вызове второй элемент списка равен 2, что свидетельствует о существовании раздела до вызова. Попытка создать раздел в несуществующем корне (&H666) завершается ошибкой с кодом 1010.

REGDELETEKEY  

Функция REGDELETEKEY позволяет удалить раздел(ключ) реестра со всеми его подразделами. Функция примает два параметра: обозначение корневого раздела (BITS) и полного "пути" к разделу (STRING). Разделителем уровней является обратный слэш. Важно отметить, что открывать раздел не нужно.

Функция при успешном завершении возвращает T, а в случае ошибки - список из двух элементов. Первый равен -1, второй представляет собой код завершения.

Вот пример удаления раздела реестра.


(regCreateKey HKEY_CLASSES_ROOT "HomeLisp" 0 KEY_ALL_ACCESS)

==> (-1029599008 1)

(regCloseKey -1029599008)

==> T

(regDeleteKey HKEY_CLASSES_ROOT "HomeLisp")

==> T

(regDeleteKey HKEY_CLASSES_ROOT "HomeLisp")

==> (-1 2)

Здесь создается раздел HKEY_CLASSES_ROOT\HomeLisp, после чего его манипулятор закрывается. Затем вызовом RegDeleteKey раздел удаляется. Заглянув в реестр с помощью regedit.exe можно убедиться в том, что раздел удален. Последующая попытка удалить несуществующий раздел вызывает ошибку.

REGDELETEVALUE  

Функция RegDeleteValue позволяет удалить параметр из предварительно открытого раздела. Функция принимает два параметра: манипулятор раздела (FIXED) и имя параметра (STRING). При успехе функция возвращает T, а при ошибке - список из двух элементов: первый равен -1, второй равен коду завершения.

Вот пример вызова функции RegDeleteValue:


(setq h (car (regCreateKey HKEY_CLASSES_ROOT "HomeLisp" 0 KEY_ALL_ACCESS)))

==> -1029535696

(regSetValue h "bob" REG_SZ "test")

==> T

(regDeleteValue h "bob")

==> T

(regDeleteValue h "bob")

==> (-1 2)

Здесь создается ключ "HomeLisp", под этим ключом создается параметр "bob" и ему присваивается текстовое значение. После этого параметр удаляется. Удаление завершение успешно. А попытка удалить несуществующий параметр вызывает ошибку с кодом завершения 2.

REGENUMKEY  

Функция REGENUMKEY позволяет перечислить подразделы предварительно открытого раздела. Функция принимает два параметра: манипулятор открытого раздела (FIXED) и номер запрашиваемого подраздела (FIXED). При успешном завершении функция возвращает список из трех элементов: нуля (признак успешного завершения), имени подраздела и имени класса подраздела. При неудачном завершении функция возвращает список из двух элементов. Первый из них - минус единица (признак ошибки), второй - код завершения.

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

Вот пример перечисления подразделов:


(setq h (car (regCreateKey
                    HKEY_LOCAL_MACHINE 
                    "Software\Microsoft\Windows\CurrentVersion" 
                    0
                    KEY_ALL_ACCESS)))

==> -1029527856

(regEnumKey h 0)

==> (0 "Setup" "")

(regEnumKey h 1)

==> (0 "RunOnce" "")

(regEnumKey h 1000)

==> (-1 259)

Можно убедиться, что раздел HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion содержит не менее двух подразделов, но не более тысячи. А точное перечисление подразделов с выводом имени каждого подраздела и общего количества приводится ниже:


(defun EnumKeys  (Branch Path)

       (prog (i sk h s)
             
             (setq s (regOpenKey Branch Path KEY_ALL_ACCESS))
              
             (cond ((eq (car s) -1) (return s)))

             (setq h (cadr s))
                  
             (setq i 0)

       @1    (setq sk (regEnumKey h i))

             (cond ((eq (car sk) -1) (go @2)))

             (print i) (prints " ") (printline (cadr sk))

             (setq i (+ i 1))

             (go @1)

       @2    (regCloseKey h) (return i)

       )
)

 ==> EnumKeys

(EnumKeys HKEY_LOCAL_MACHINE "Software\Microsoft\Windows")

0 "CurrentVersion"
1 "ITStorage"
2 "Help"
3 "HTML Help"

==> 4

(EnumKeys HKEY_LOCAL_MACHINE "Software\Microsoft\Window")

==> (-1 2)

(EnumKeys &H777 "Software\Microsoft\Windows")

==> (-1 1114)

Следует обратить внимание на то, что при обращении к несуществующему разделу ("Software\Microsoft\Window") функция возвращает код завершения 2. А при обращению к несуществующей ветви (&H777) код завершения равен 1114.

REGENUMVALUE  

Функция REGENUMVALUE позволяет перечислить параметры предварительно открытого ключа. Функция требует два параметра: манипулятор раздела (FIXED) и номер параметра (FIXED). Параметры нумеруются с нуля.

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

Тип параметра задается константой типа FIXED из нижеследующей таблицы:

 
Символическая константа Значение Описание типа
0 REG_NONE (?)
1 REG_SZ Строка символов, закрытая нулем.
2 REG_EXPAND_SZ Расширенный строковый параметр (?)
3 REG_BINARY Последовательность байтов (в т.ч. и нулей).
4 REG_DWORD Двойное слова с Интеловским порядком байтов: младшие байты слева.
5 REG_DWORD_BIG_ENDIAN Двойное слова с неинтеловским порядком байтов: младшие байты справа.
6 REG_LINK Ссылка (?)
7 REG_MULTI_SZ Последовательность строк, закрытых нулями. Признак конца агрегата - два нуля, стоящих подряд.
8 REG_RESOURCE_LIST (?)


В HomeLisp не существует априорного способа узнать количество параметров данного раздела. Единственный способ сделать это - выполнить итерационный цикл, запрашивая параметры раздела с последовательными номерами до тех пор, пока функция не вернет ненулевой код завершения.

Вот пример перечисления параметров раздела реестра:


(defun EnumValues  (Branch Path)

       (prog (i sk h s)
             
             (setq s (regOpenKey Branch Path KEY_ALL_ACCESS))
              
             (cond ((eq (car s) -1) (return s)))

             (setq h (cadr s))
                  
             (setq i 0)

       @1    (setq sk (regEnumValue h i))

             (cond ((eq (car sk) -1) (go @2)))

             (print i) (prints " ") (printline (cdr sk))

             (setq i (+ i 1))

             (go @1)

       @2    (regCloseKey h) (return i)

       )
)

 ==> EnumValues

(EnumValues HKEY_LOCAL_MACHINE "Software\Microsoft\Windows\CurrentVersion\Run")

0 ("FLMOFFICE4DMOUSE" 1 "C:\Program Files\Browser MOUSE\mouse32a.exe")
1 ("internat.exe" 1 "internat.exe")
2 ("ScanRegistry" 1 "C:\WINDOWS\scanregw.exe /autorun")
3 ("TaskMonitor" 1 "C:\WINDOWS\taskmon.exe")
4 ("SystemTray" 1 "SysTray.Exe")
5 ("mdac_runonce" 1 "C:\WINDOWS\SYSTEM\runonce.exe")
6 ("Machine Debug Manager" 1 "C:\WINDOWS\SYSTEM\MDM.EXE")
7 ("MyDirScan" 1 "C:\!AVSCAN\DIRSCAN.EXE -T")
8 ("PWSTray" 1 "PwsTray.exe")
9 ("Outpost Firewall" 1 "C:\PROGRAM FILES\AGNITUM\OUTPOST FIREWALL 1.0\outpost.exe
   /waitservice")

==> 10

Здесь видно, что все параметры раздела автозапуска имеют тип REG_SZ.

REGOPENKEY  

Функция REGOPENKEY открывает существующий раздел реестра. Функция требует три параметра: ветвь реестра (BITS), путь к нужному разделу (STRING) и права доступа (BITS).

При успешном завершении функция возвращает список из двух элементов: нуля (признак успеха) и манипулятора открытого раздела.

При ошибке функция также возвращает список из двух элементов: -1 (признак ошибки) и расширенного кода ошибки.

Вот три примера вызова REGOPENKEY:


(regOpenKey HKEY_LOCAL_MACHINE "Software\Microsoft\Windows\CurrentVersion\Run" KEY_ALL_ACCESS)

==> (0 -1029700800)

(regOpenKey HKEY_LOCAL_MACHINE "Software\Microsoft\Windows\CurrentVersion\Ru" KEY_ALL_ACCESS)

==> (-1 2)

(regOpenKey &H888 "Software\Microsoft\Windows\CurrentVersion\Run" KEY_ALL_ACCESS)

==> (-1 1010)

Первый пример демонстрирует удачное открытие. Два последующих - неудачные, но по разным причинам. Во втором случае задан несуществующий раздел "Software\Microsoft\Windows\CurrentVersion\Ru", что порождает код завершения 2. В третьем случае задан код несуществующей ветви &H888, что дает код завершения 1010.

REGQUERYVALUE  

Функция REGQUERYVALUE позволяет получить значение параметра заранее открытого раздела по имени параметра. Функция требует два аргумента: манипулятор открытого раздела (FIXED) и имя запрашиваемого параметра (STRING).

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

Типы значений хранящихся в реестре приведены выше при описании функции REGENUMVALUE.


(setq h (cadr (regOpenKey HKEY_CLASSES_ROOT "homelisp" KEY_ALL_ACCESS)))

 ==> -1029765920

(regQueryValue h "Bob")

 ==> (0 1 "1954")

(regQueryValue h "buska")

 ==> (0 3 11 22 33 44 55 66)

(regQueryValue h "plushka")

==> (0 7 "bbb" "ccc" "ddd" "eee")

(regQueryValue h "zzz")

==> (-1 2)

В последнем случае запрос несуществующего параметра вызывает завершение с кодом 2.

REGSETVALUE  

Функция REGSETVALUE позволяет создать параметр у предварительно открытого раздела и привоить ему значение. Функция требует четыре обязательных аргумента. Первый, как обычно, задает манипулятор раздела (FIXED). Второй (типа STRING) задает имя параметра. Третий (типа FIXED) задает тип параметра. Четвертый параметр задает устанавливаемое значение. В зависимости от третьего параметра, четвертый пераметр может быть атомом или списком.

При успешном завершении функция возвращает T.

Вот примеры вызова функции REGSETVALUE:


(regSetValue h "leo" REG_BINARY '(0 1 2 3 4))

==> T

(regQueryValue h "leo")

==> (0 3 0 1 2 3 4)

(regSetValue h "pushok" REG_SZ "Пушок")

==> T

(regQueryValue h "pushok")

==> (0 1 "Пушок")

(regSetValue h "a1" -9 "q")

REGSETVALUE: Третий аргумент имеет недопустимое значение
==> ERRSTATE

В последнем случае задан неверный тип параметра (-9).

Следует отметить важное обстоятельство: функция допускает задание на месте имени создаваемого параметра пустой строки. В этом случае заданное значение становится значением по умолчанию для ключа, манипулятор которого указан при вызове функции REGSETVALUE.