Итак, начнем:
1. Нажмите кнопку "Пуск"
2. Выберите меню "Выполнить"
3. В появившемся диалоговом окне, в текстовое поле впишем: regedit
4. Нажмем кнопку "ОК"
5. В открывшемся редакторе реестра, выберем меню Реестр\Экспорт файла реестра
6. Впишем любое, понравившееся нам имя файла, и нажмем кнопку "Сохранить"
Теперь я за Вас спокоен, и мы можем перейти к нашей статье. Почему же такое "строгое" начало? Все дело в том, что Windows хранит в реестре все свои настройки и настройки программ, которые в нем работают. Начиная от внешнего графического вида и кончая соответствием расширений файлов их программам. Поэтому любое неграмотное вмешательство в реестр, может привести к тому, что Ваш Windows …
Необходимость в месте хранения информации возникла, наверно, с момента создания самой первой операционной системы (ОС). И по мере усложнения ОС – усложнялось и хранение самой информации о ней.
До появления Win 95 эта информация хранилась в многочисленных ini-файлах (директория
Windows/System). Однако, по мере наращивания мощностей операционной системы – это становилось все более неудобным. И Win 95, уже как новая платформа, практически полностью перешел на хранение информации в реестре. Практически полностью – так мы говорим потому, что "осколки старого" все еще живы даже в Win Me (например,
Win.ini, System.ini).
Visual Basic, как и любая другая среда программирования, должна уметь обращаться к информации системы. Что он и делает, используя ряд встроенных функций (см. Часть 1).
Но и тут все далеко неоднозначно. Если в VB3 и VB4 с помощью этих функций мы могли обратиться к любому ini-файлу, то, начиная с VB5, данные функции работают только с реестром и только с небольшой веточкой в нем. По-видимому, разработчики VB, предвидя нарастание увлечения программированием на VB и
VBA, решили обезопасить реестр от вмешательства дилетантов. В большинстве работ начинающих программистов – этого вполне достаточно. А вот работа с ini-файлами теперь возможна только через API-функции (?). Как работать с ними см. Часть 2.
Ну и, наконец, мы считаем себя уже "маститыми" программистами и работать хотим со всем реестром. Бейсик позволяет делать и это, но опять же работая через API-функции. Как это делается, мы будем изучать в Части 3.
Часть 1. Встроенные функции работы с реестром в Visual Basic.
Таких функций всего четыре:
Имя функции |
Описание функции |
GetSetting |
Получить значение параметра |
GetAllSettings |
Получить массив значений в указанной секции |
SaveSetting |
Сохранить значение параметра |
DeleteSetting |
Удалить параметр (секцию) |
Все они работают с одной конкретной веткой реестра, а именно:
HKEY_CURRENT_USER/SOFTWARE/VB and VBA Program Settings/. Обращение к данной ветке, происходит автоматически внутри функции, и поэтому прописывать столь длинный путь нет необходимости. Давайте чуть-чуть подробнее остановимся на каждой из этих функций.
GetSetting и SaveSetting – с ними Вы будете встречаться чаще всего.
GetSetting(appname,
section, key[, default])
SaveSetting
appname, section, key, setting
appname – строковый параметр, определяет имя приложения и/или проекта, где будут храниться данные. В 99,9 % случаев для каждой программы достаточно одного значения
appname. Чтобы не забивать себе голову ненужными вещами, а спустя некоторое время не заниматься длительными поисками в реестре (где же это у меня хранится?), удобней всего использовать объект App (например,
App.EXEName).
section – так же строковый параметр, определяет имя подпапки, где хранятся все значения. Таких папок может быть от одной до … Т.е. здесь Вы формируете наличие и расположение папок, так как Вам удобно.
key – строковый параметр, имя, под которым будет храниться Ваше значение. Как рекомендация, давайте ему осмысленные значения.
setting – непосредственно само сохраняемое значение.
[, default] – судя по квадратным скобкам, Вы уже догадались, что это необязательный параметр. Однако желательно все же его заполнять. По крайней мере – это избавит Вас от громоздкой проверки при запуске приложения самый первый раз, когда еще ничего в реестре Вашей программой не прописано.
Для закрепления материала маленький примерчик, демонстрирующий эти две функции.
Private Sub
Form_Load()
Me.Left = GetSetting(App.EXEName, "Position", "Left", 0)
Me.Top = GetSetting(App.EXEName, "Position", "Top", 0)
Me.Caption = GetSetting(App.EXEName, "Other", "Caption", "Проба")
End Sub
Private Sub
Form_Unload(Cancel As Integer)
SaveSetting App.EXEName, "Position", "Left", Me.Left
SaveSetting App.EXEName, "Position", "Top", Me.Top
SaveSetting App.EXEName, "Other", "Caption", Me.Caption
End Sub
Здесь, при загрузке формы, в самый первый раз, в связи с отсутствием записей в реестре, используются значения по умолчанию. Если Вы после запуска переместили форму, то на выходе новые координаты будут записаны в реестр. При следующем запуске происходит считывание параметров (игнорируя значение по умолчанию) и расположение формы в нужном месте. "Кувыркания" с заголовком здесь приведены просто как пример создания нескольких папок для одного приложения. Теперь, если Вы откроете редактор реестра и пройдете по веточке
HKEY_CURRENT_USER/SOFTWARE/VB and VBA Program Settings/, то увидите там папку Project1 (если проект не переименовывался!), а в ней две подпапки
"Position" и "Other", с сохраненными в них значениями.
Функция DeleteSetting имеет чуть более простую структуру:
DeleteSetting appname, section[, key]
Параметры appname, section, key – имеют те же описания, что и в предыдущих функциях.
И снова небольшой пример. Добавьте код в
Form_Unload, чтобы он выглядел так:
Private Sub
Form_Unload(Cancel As Integer)
SaveSetting App.EXEName, "Position", "Left", Me.Left
SaveSetting App.EXEName, "Position", "Top", Me.Top
SaveSetting App.EXEName, "Other", "Caption", Me.Caption
On Error Resume Next
DeleteSetting App.EXEName, "Position", "Left"
End Sub
И при следующей загрузке формы, не найдя нужного параметра, она будет позиционирована по левому краю. Обратите внимание на включение обработчика ошибок. Он необходим для того, чтобы в случае не нахождения данного параметра, не происходило аварийное завершение программы.
Данная строка удаляет только один параметр
("Left"). Если же нам необходимо удалить весь подраздел, то строка должна выглядеть следующим образом:
On Error Resume Next
DeleteSetting
App.EXEName, "Position"
Ну и, наконец, последняя встроенная VB-функция, для работы с реестром.
GetAllSettings(appname, section)
Как нетрудно догадаться
appname, section – описывают все те же самые параметры, что и предыдущие функции. Данная функция служит для получения массива параметров.
Разместим на форме кнопку, которая будет считывать массив параметров подпапки
"Position" и выдавать ее в заголовок. Не забудьте удалить последние две строки в
Form_Unload, посвященные, удалению параметров.
Добавим код для кнопки:
Private Sub Command1_Click()
Dim arrSettings As Variant, intLoop As Integer
Me.Caption = vbNullString
arrSettings = GetAllSettings(App.EXEName, "Position")
For intLoop = LBound(arrSettings, 1) To UBound(arrSettings, 1)
Me.Caption = Me.Caption & " " & arrSettings(intLoop, 0) & "=" &
arrSettings(intLoop, 1)
Next
End Sub
Чуть поподробнее: arrSettings объявляем как
Variant, т.к. в дальнейшем она будет нести функции двумерного массива. intLoop – переменная-счетчик для цикла
For-Next.
С помощью функции
GetAllSettings, считываем в переменную arrSettings двумерный массив значений. Затем циклом For-Next проходим от первого значения до последнего (в нашем случае их только два). И внутри цикла присваиваем заголовку программы считываемые значения. Обратите внимание, что второй компонент в массиве может принимать только значения 0 или 1. Если 0, то считывается заголовок параметра
(key). Если 1, то считывается его значение (setting).
На этом со встроенными функциями бейсика, посвященных реестру можно закончить.
Работа с ini-файлами.
Как говорилось выше, ini-файлы – это наследие прошлого. Хорошо это или плохо? Однозначного ответа нет. Существует много сторонников ini-файлов, и их основным аргументом является доступность и легкость в редактировании. Что такое, по сути, ini-файл? Это текстовый файл, разбитый внутри на секции. Заголовок каждой секции заключен в квадратные скобки. Каждая секция состоит из строк, представляющих собой название параметра (ключ) и через равно (=) – его значение.
Все элементарно просто и понятно для восприятия. Однако это же самое является и отрицательным моментом. Учитывая, что нынешний Windows занимает порядка 150-300 мегабайт, имеет массу встроенных программ и каждой из них необходим свой собственный ini-файл. А, кроме того, мы инсталлируем у себя, как минимум, Офис. А как максимум еще 3-4 десятка программ. Представляете в какую "выгребную яму" превратится директория, хранящая ini-файлы? Кроме того, из-за своей простоты, ini-файлы не могут предоставить иерархическую структуру вложенности, то, что так легко удается реестру.
И в заключение: ini-файл – это отдельный файл, т.е. принципиально он может храниться в любом месте, а не только Windows\System. Насколько усложнится поиск необходимого файла, из-за кажущейся "простоты редактирования". Однако, несмотря на все минусы ini-файлы продолжают жить. Не так вольготно, как в Windows 3x, но все же. По-прежнему сохраняются 2 основных файла, несущих информацию о самом Windows (win.ini, system.ini). Поэтому давайте разберемся, как все-таки с ними надо работать. Встроенных VB-функций для работы с ini-файлами нет, однако существуют API-функции. Излагаемый ниже материал полностью приведен в Листинге. Его можно просто скопировать и оформить как отдельный модуль или класс, который подключается по мере надобности.
- Private Declare Function GetPrivateProfileInt Lib "kernel32" Alias
"GetPrivateProfileIntA" (ByVal strSection As String, ByVal strKeyName As
String, ByVal lngDefault As Long, ByVal strFileName As String) As Long
- Private Declare Function GetPrivateProfileString Lib "kernel32" Alias
"GetPrivateProfileStringA" (ByVal strSection As String, ByVal strKeyName As
String, ByVal strDefault As String, ByVal strReturned As
String, ByVal lngSize As Long, ByVal strFileName As String) As Long
- Private Declare Function WritePrivateProfileString Lib "kernel32" Alias
"WritePrivateProfileStringA" (ByVal strSection As String, ByVal strKeyNam As
String, ByVal strValue As String, ByVal strFileName As String) As Long
Приведенные здесь три основные функции служат для работы с ini-файлами 2 на запись (для строкового и целочисленного параметров) и одна на чтение. Кроме того, существует еще три API-функции (по тому же принципу), но работающие ТОЛЬКО с одним файлом WIN.INI
- Private Declare Function GetProfileInt Lib "kernel32" Alias
"GetProfileIntA" (ByVal strSection As String, ByVal strKeyName As
String, ByVal lngDefault As Long) As Integer
- Private Declare Function GetProfileString Lib "kernel32"
Alias "GetProfileStringA" (ByVal strSection As String, ByVal strKeyName As
String, ByVal strDefault As String, ByVal strReturned As
String, ByVal intSize As Long) As Long
- Private Declare Function WriteProfileString Lib "kernel32" Alias
"WriteProfileStringA" (ByVal strSection As String, ByVal strKeyName As
String, ByVal strValue As String) As Integer
Нетрудно заметить, что последние три функции, практически полностью повторяют первые, за исключением одного параметра – путь файла. Однако, благодаря этому, скорость их работы значительно выше. Внутренняя структура представленных API-функций достаточно проста, и, в принципе их можно использовать "напрямую" в своих приложениях. Однако я рекомендую все же сделать функции
- "обертки", так как API-функции не поддерживают обработку ошибок, и лучше это сделать самому, не дожидаясь, когда Ваша программа "скоропостижно" закончит работу.
Public Function GetValueInteger(strSection As
String, strKey As String, strFile As String) As Integer
Dim intValue As
Integer
On Error GoTo PROC_ERR
intValue = GetPrivateProfileInt(strSection, strKey, 0, strFile)
GetValueInteger = intValue
PROC_EXIT:
Exit Function
PROC_ERR:
MsgBox "Ошибка: <" & Err.Number & "> - " &
Err.Description, vbExclamation + vbOKOnly, "GetValueInteger"
Resume PROC_EXIT
End Function
Данная функция возвращает целочисленное значение из ini-файла. Где имя секции – strSection, имя ключа – strKey и strFile – полный путь к ini-файлу.
Public Function GetValueString(strSection As
String, strKey As String, strFile As String) As String
Dim strBuffer As String * 256
Dim intSize As Integer
On Error GoTo PROC_ERR
intSize = GetPrivateProfileString(strSection, strKey, "",
strBuffer, 256, strFile)
GetValueString =
Left$(strBuffer, intSize)
PROC_EXIT:
Exit Function
PROC_ERR:
MsgBox "Ошибка: <" & Err.Number & "> - " &
Err.Description, vbExclamation + vbOKOnly, "GetValueString"
Resume PROC_EXIT
End Function
Данная функция возвращает строковое значение из ini-файла. Где имя секции – strSection, имя ключа – strKey и strFile – полный путь к ini-файлу.
Обратите внимание, что вначале резервируется пустая строка, а из результата функции выкидываются пустоты.
Public Function SetValue(strSection As String, strKey As
String, strValue As String, strFile As String) As Integer
Dim intStatus As Integer
On Error GoTo PROC_ERR
intStatus =
WritePrivateProfileString(strSection, strKey, strValue, strFile)
SetValue = (intStatus <> 0)
PROC_EXIT:
Exit Function
PROC_ERR:
MsgBox "Ошибка: <" & Err.Number & "> - " &
Err.Description, vbExclamation + vbOKOnly, "SetValue"
Resume PROC_EXIT
End Function
Данная функция записывает в файл значение strValue и возвращает целочисленное значение: True, если запись произведена и False, в случае ошибки записи. Имя секции – strSection, имя ключа – strKey и strFile – полный путь к ini-файлу. Как уже говорилось выше, работа с файлом WIN.INI строится аналогичным образом. Поэтому повторяться здесь не буду. До этого момента все было легко и просто. Однако встречаются ситуации, когда нам необходимо считать все значения в конкретной секции. И данные функции нам не помогут. Оказывается, существует еще одна API-функция, которая нам в этом поможет:
- Private Declare Function GetPrivateProfileSection Lib "KERNEL32" Alias
"GetPrivateProfileSectionA" (ByVal lpAppName As String, ByVal lpReturnedString As
String, ByVal nSize As Long, ByVal lpFileName As String) As Long
Для нее так же напишем функцию-"обертку".
Public Function GetSection(strSection As String, strFile As String)
Dim strBuffer As String * 512
Dim intSize As Integer
Dim strTemp As String
Dim intTemp As Integer
Dim Index As Integer
Dim arrSection() As String
Dim key As String, value As String, str As String
On Error GoTo PROC_ERR
intSize = GetPrivateProfileSection(strSection, strBuffer, 512, strFile)
strTemp = Left$(strBuffer, intSize)
Do Until Len(strTemp) = 0
intTemp = InStr(1, strTemp, Chr(0))
ReDim Preserve arrSection(1, Index) As String
str = Mid(strTemp, 1, intTemp)
key = Mid(str, 1, InStr(1, str, "=") - 1)
value = Mid(str, InStr(1, str, "=") + 1)
arrSection(0, Index) = key
arrSection(1, Index) = value
Index = Index + 1
strTemp = Mid(strTemp, intTemp + 1, Len(strTemp))
Loop
GetSection = arrSection
PROC_EXIT:
Exit Function
PROC_ERR:
MsgBox "Ошибка: <" & Err.Number & "> - " &
Err.Description, vbExclamation + vbOKOnly, "GetValueString"
Resume PROC_EXIT
End Function
Данная функция возвращает двумерный массив значений конкретной секции. Вначале, с помощью API-функции в буферизованную строку (strBuffer) считывается значение всей секции и определяется количество знаков (intSize). Далее, обрезаем строку от пустот и передаем это значение переменной
strTemp. Затем в цикле Do-Loop считываем значение каждой строки (они разделены символом Chr(0)) и, предварительно разделив их на имя ключа и его значение, сбрасываем в двумерный массив. Первый параметр в массиве – будет означать, что мы считываем: имя ключа (0) или его значение (1).
Давайте сделаем маленький пример, чтобы стало понятнее, как работает наша функция. В качестве примера возьмем всем известную игру "Минер". Расположите на форме кнопку и список. При нажатии на кнопку в список последовательно передаются значения из секции [Сапер].
Private Sub Command1_Click()
Dim i As Integer, arrList As Variant
List1.Clear
arrList = GetSection("Сапер",
"C:\Windows\winmine.ini")
For i = 0 To UBound(arrList, 2)
List1.AddItem arrList(1, i)
Next
End Sub
Обратите внимание, что если мы запишем arrList(0, i), то будем передавать уже не значения, а имена ключей. Думаю на этом раздел, посвященный ini-файлам, можно закончить.
Часть 3. Работа с реестром.
Теперь мы подходим к самому главному: работе с реестром. Реестр в Windows служит хранилищем всех настроек. Любое неадекватное вмешательство может привести к краху всей системы. Поэтому, если Вы не выполнили указаний, данных в самом начале статьи – сейчас самое время их сделать. Остановимся чуть подробнее на структуре реестра. 2 основных его преимущества: сохранение всей информации в едином месте и иерархическая структура. Начнем с "самого верха". Здесь имеется 6 основных разделов (ключей):
HKEY_CLASSES_ROOT
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE
HKEY_USERS
HKEY_CURRENT_CONFIG
HKEY_DYN_DATA
Каждый из них отвечает за "свой" раздел работы. HKEY_CLASSES_ROOT – содержит данные, связывающие типы файлов (по расширениям) с приложениями, работающими с ними.
HKEY_CURRENT_USER – содержит данные, которые описывают профиль пользователя, т.е. внешний вид и поведение рабочего стола, данные различных программ, сетевые соединения. Именно здесь и сохраняются все основные настройки приложений, используемых в Windows, в том числе и VB.
HKEY_LOCAL_MACHINE – здесь хранятся настройки компьютера, общие для всех зарегистрированных пользователей.
HKEY_USERS – этот раздел хранит информацию о профилях каждого из зарегистрированных пользователей.
HKEY_CURRENT_CONFIG – включает в себя информацию о шрифтах, зарегистрированных на данном компьютере, а так же данные о принтерах.
HKEY_DYN_DATA – самый часто обновляемый раздел реестра, т.к. включает в себя часто изменяющиеся динамические данные.
Visual Basic не имеет встроенных функций для работы с любой частью реестра. Тот небольшой участок реестра, к которому допуск из VB напрямую разрешен, был подробно описан в первой части данной статьи. Однако существует "палочка-выручалочка" – это API-функции. Задекларировав их вначале, из программы можно обратиться в любой момент к любому разделу реестра. Перечень функций для работы с реестром (все они начинаются с Reg), а так же константы приведены в листинге. Итак, основные функции и что они выполняют:
API-функция |
Описание |
RegCloseKey |
Освобождает манипулятор раздела |
RegCreateKeyEx |
Создает раздел, а если раздел существует, то просто открывает его. Это очень удобно, когда неизвестно: существует данный раздел реестра или нет |
RegDeleteKey |
Удаляет раздел со всеми входящими в него подразделами |
RegDeleteValue |
Удаляет заданный параметр из конкретного раздела |
RegEnumKeyEx |
Возвращает имеющийся подраздел из конкретного раздела. Удобно использовать в цикле для получения всех подразделов конкретного раздела |
RegEnumValue |
Аналогична предыдущей функции, с той лишь разницей, что
возвращает параметр из конкретного раздела |
RegOpenKeyEx |
Открывает заданный раздел |
RegQueryValueEx |
Получает информацию о типе и данные для параметра с заданным именем. |
RegSetValueEx |
Сохраняет данные в параметре реестра. |
Проверять все производимые нами изменения можно через редактор реестра. Обычно он располагается в директории Windows (файл regedit.exe). Не забудьте, что после проведенных изменений в реестре, чтобы эти изменения увидеть, редактор необходимо обновить (клавиша F5). Для наших примеров мы будем использовать стандартную ветку реестра:
HKEY_CURRENT_USER\SOFTWARE\VB AND VBA PROGRAM SETTINGS
А когда Вы поймете суть работы функций, то заменить пути сохранения в реестре необходимых Вам данных не составит труда.
Создание раздела.
Создадим новый подраздел TestRegistry в вышеуказанной ветке. Расположим на форме кнопку Command1. В Form_Load инициализируем наши переменные, отвечающие за путь в реестре.
Private Create As Long
Private SubKey As String
Private hKey As Long
Private Sub Form_Load()
SubKey = "SOFTWARE\VB AND VBA PROGRAM SETTINGS"
hKey = HKEY_CURRENT_USER
End Sub
Примечание: для значения переменной hKey берется константа, объявленная в модуле.
В обработке события кнопки попросим пользователя ввести Имя Подраздела. Затем проследим, чтобы перед названием стоял знак "\". И, наконец, проверим функцию, создающую новый подраздел.
Private Sub Command1_Click()
On Error GoTo ErrorRoutineErr:
Dim Reply As String
Reply = InputBox("Введите имя для создания подраздела:" & vbCrLf & "в разделе " & SubKey)
If Reply = "" Then
Exit Sub
End If
If Mid(Reply, 1, 1) <> "\" Then
Reply = "\" & Reply
End If
If Reply = "" Then
MsgBox "Не могу создать подраздел."
Exit Sub
End If
If CreateRegKey(Reply) Then
MsgBox "Создан новый подраздел"
Else
MsgBox "Не могу создать подраздел."
End If
Exit Sub
ErrorRoutineErr:
MsgBox "Ошибка: <" & Err.Number & "> - " & Err.Description, vbExclamation + vbOKOnly, "Созданиие подраздела"
End Sub
И самое главное: внутренняя функция, создающая новый подраздел и возвращающая True, если все завершилось удачно и False, если раздел не был создан. В конце освобождаем манипулятор раздела.
Function CreateRegKey(NewSubKey As String) As Boolean
On Error GoTo ErrorRoutineErr:
Dim phkResult As Long
Dim SA As SECURITY_ATTRIBUTES
CreateRegKey = (RegCreateKeyEx(hKey, SubKey & NewSubKey, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, SA, phkResult, Create) = ERROR_SUCCESS)
RegCloseKey phkResult
Exit Function
ErrorRoutineErr:
MsgBox "Ошибка: <" & Err.Number & "> - " & Err.Description, vbExclamation + vbOKOnly, "Созданиие подраздела"
CreateRegKey = False
End Function
Обратите внимание, что если данный раздел уже существует, то ошибки не выдается, а просто данный раздел открывается.
Создание параметра.
После того, как раздел или подраздел создан, необходимо в нем создать параметр и присвоить ему значение. Допустим, в предыдущем шаге мы создали подраздел "TestRegistry". Теперь внесем имя параметра "Test1" и его значение "New Value". Для этого нам понадобится все та же форма и вторая кнопка на ней Command2. Инициализация переменных у нас уже описана выше, в предыдущем шаге. Добавим обработку нажатия на вторую кнопку. Запросим у пользователя сначала имя параметра, а затем его значение. И если все ОК – то сделаем запись.
Private Sub Command2_Click()
Dim Reply1 As String, Reply2 As String
SubKey = SubKey & "\TestRegistry"
Reply1 = InputBox("Введите имя параметра:" &
vbCrLf & "в разделе: " & SubKey)
If Reply1 = "" Then
Exit Sub
End If
Reply2 = InputBox("Введите значение параметра:" & vbCrLf & "в разделе: " & SubKey & vbCrLf & "для параметра: " & Reply1)
If Reply2 = "" Then
Exit Sub
End If
If Not SetRegValue(hKey, SubKey, Reply1, Reply2) Then
MsgBox "Не могу создать параметр."
End If
End Sub
Теперь, опишем саму функцию, выполняющую все это. Вначале откроем (а если нет – то создадим) необходимый раздел (подраздел). Затем запишем в него имя и значение параметра. И, в конце, закрываем текущий раздел, освобождая манипулятор.
Function SetRegValue(hKey As Long, sSubKey As String, ByVal sSetValue As String, ByVal sValue As String) As Boolean
On Error GoTo ErrorRoutineErr:
Dim phkResult As Long
Dim lResult As Long
Dim SA As SECURITY_ATTRIBUTES
RegCreateKeyEx hKey, sSubKey, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, SA, phkResult, Create
lResult = RegSetValueEx(phkResult, sSetValue, 0, REG_SZ, sValue, CLng(Len(sValue) + 1))
RegCloseKey phkResult
SetRegValue = (lResult = ERROR_SUCCESS)
Exit Function
ErrorRoutineErr::
MsgBox "Ошибка: <" & Err.Number & "> - " & Err.Description, vbExclamation + vbOKOnly, "Созданиие параметра"
SetRegValue = False
End Function
Удалить параметр.
Итак, у нас создан подраздел "TestRegistry", а в нем параметр "Test1". Располагаем на форме третью кнопку – Command3. Как и во всех предыдущих наших манипуляциях, вначале мы должны открыть раздел, а по окончании работы закрыть его, освободив манипулятор. Удаление параметра производится с помощью API-функции RegDeleteValue.
Private Sub Command3_Click()
Dim phkResult As Long
Dim SA As SECURITY_ATTRIBUTES
SubKey = "SOFTWARE\VB AND VBA PROGRAM SETTINGS\TestRegistry"
RegCreateKeyEx hKey, SubKey, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, SA, phkResult,
Create
If RegDeleteValue(phkResult, "Test1") = ERROR_SUCCESS Then
MsgBox "Параметр удален"
Else
MsgBox "Не могу удалить параметр"
End If
RegCloseKey phkResult
End Sub
Удалить раздел.
В принципе, все различия между удалением параметра и удалением раздела (подраздела), заключаются только в использовании другой API-функции: RegDeleteKey. Все остальное – идентично. Размещаем на форме четвертую кнопку и удаляем раздел "TestRegistry".
Private Sub Command4_Click()
Dim phkResult As Long
Dim SA As SECURITY_ATTRIBUTES
SubKey = "SOFTWARE\VB AND VBA PROGRAM SETTINGS"
RegCreateKeyEx hKey, SubKey, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, SA, phkResult, Create
If RegDeleteKey(phkResult, "TestRegistry") = ERROR_SUCCESS Then
MsgBox "Подраздел удален"
Else
RegCloseKey phkResult
MsgBox "Не могу удалить подраздел"
End If
End Sub
Получение значения раздела.
В связи с тем, что мы удалили в предыдущей работе наш подраздел и его содержимое, давайте сначала восстановим удаленные параметры: Запустим на выполнение сначала первую кнопку, а затем вторую. Проверим через Regedit. У нас должно получиться: подраздел "TestRegistry", а в нем параметр "Test1" со значением "New Value". Добавим очередную кнопку Command5.
Private Sub Command5_Click()
SubKey = "SOFTWARE\VB AND VBA PROGRAM SETTINGS\TestRegistry"
MsgBox GetValue(hKey, SubKey, "Test1", "Ничего нет")
End Sub
Теперь займемся написанием самой функции. Как всегда, вначале открываем раздел, а в конце не забудем освободить манипулятор раздела. С помощью API-функции RegQueryValueEx получаем значение параметра. Полученный результат возвращаем функции, а в случае его не нахождения – присваиваем значение sDefault.
Обратите внимание, что сама функция возвращает тип Variant, т.к. значение параметра может быть не только строковым числом.
Function GetValue(hKey As Long, sSubKey As String, sKey As String, sDefault As String) As Variant
On Error GoTo ErrorRoutineErr
Dim phkResult As Long
Dim lResult As Long
Dim sBuffer As String
Dim lBuffSize As Long
'Создаем буфер
sBuffer = Space(255)
lBuffSize = Len(sBuffer)
RegOpenKeyEx hKey, sSubKey, 0, 1, phkResult
lResult = RegQueryValueEx(phkResult, sKey, 0, 0, sBuffer, lBuffSize)
RegCloseKey phkResult
If lResult = ERROR_SUCCESS Then
GetValue = Left(sBuffer, lBuffSize - 1)
Else
GetValue = sDefault
End If
Exit Function
ErrorRoutineErr::
MsgBox "Ошибка: <" & Err.Number & "> - " & Err.Description,
vbExclamation + vbOKOnly, "Получение параметра"
GetValue = ""
End Function
Получение всех подразделов конкретного раздела и получение всех значений раздела.
Подробно изучив выше перечисленные функции и процедуры, Вы, при необходимости, можете их использовать для получения названий подразделов или значений всех параметров подраздела. Собирать эти названия можно в массив или, например, напрямую – в ListBox. Перебор параметров осуществляется с помощью цикла Do … Loop (или While … Wend кому как привычнее), до тех пор пока искомое значение равно ERROR_SUCCESS.
Разобравшись со всеми функциями, Вы можете использовать представленный Листинг, как отдельный модуль, или преобразовать его в отдельный класс. Обратите внимание: в представленном листинге функции отображены в несколько ином виде – более глобальном.
Часть 4. Работа с реестром через подключаемые библиотеки.
Изначально эта часть не была запланирована для данной статьи. Подтолкнул меня к написанию один из "Советов…" Андрея Колесова и Ольги Павловой, которые вот уже на протяжении ряда лет публикующих в "Компьютер-Пресс" свои интересные находки по VB и VBA.
Так вот, оказывается, что при инсталляции VB происходит установка на Ваш компьютер одной маленькой библиотеки (всего 32 Кб) - REGTOOL5.DLL. Для любознательных указываю полный путь: …\Program Files\Microsoft Visual Studio\Common\Tools\APE\REGTOOL5.DLL. Она содержит всего 4 функции для работы с реестром, однако использование этих функций значительно упрощает доступ к реестру.
Итак, для начала работы подключим данную библиотеку к нашему проекту (Проект Standard EXE; форма + 5 кнопок на ней). Для подключения выберем меню: Project/References и в открывшемся диалоговом окне отметим опцию Registry Access Functions. Не забудем нажать ОК :-).
Теперь, когда библиотека подключена к проекту, небходимо объявить переменную. Делаем это в разделе деклараций
Option Explicit
Dim Reg As Registry
При загрузке формы – инициализируем ее
Private Sub Form_Load()
Set Reg = New Registry
End Sub
А при выгрузке – не забудем ее уничтожить.
Private Sub Form_Unload(Cancel As Integer)
Set Reg = Nothing
End Sub
Теперь займемся функциями. Как я говорил выше их всего 4: UpdateKey,
DeleteKey, GetKeyValue, GetNextValue. А так же имеется 5 констант:
Const HKEY_CLASSES_ROOT = -2147483648 (&H80000000)
Const HKEY_CURRENT_USER = -2147483647 (&H80000001)
Const HKEY_LOCAL_MACHINE = -2147483646 (&H80000002)
Const HKEY_PERFORMANCE_DATA = -2147483644 (&H80000004)
Const HKEY_USERS = -2147483645 (&H80000003)
Результаты работы наших функций можно проверять через Редактор Реестра. Только не забудьте обновлять содержимое его окна после выполненных манипуляций (клавиша F5).
Для нашего примера выберем все ту же (самую безопасную в смысле экспериментов) ветку реестра: HKEY_CURRENT_USER\SOFTWARE\VB and VBA Program Settings.
NB! Данная библиотека может работать с любой веткой реестра.
Присвоим нашей ПЕРВОЙ кнопке возможность создания нового параметра и значения для него.
Private Sub Command1_Click()
Reg.UpdateKey HKEY_CURRENT_USER, "SOFTWARE\VB and VBA Program Settings\TestRegistryDLL", "FirstValue", "Test"
Reg.UpdateKey HKEY_CURRENT_USER, "SOFTWARE\VB and VBA Program Settings\TestRegistryDLL", "SecondValue", "Test2"
MsgBox "Создана папка TestRegistryDLL и 2 параметра в ней"
End Sub
Заметили интересную особенность? При отсутствии необходимой папки, она автоматически создается этой функцией. Кроме того, данная функция позволяет изменять значение уже существующего параметра. Что мы и покажем на примере ВТОРОЙ кнопки.
Private Sub Command2_Click()
Reg.UpdateKey HKEY_CURRENT_USER, "SOFTWARE\VB and VBA Program Settings\TestRegistryDLL", "SecondValue", "New Test"
MsgBox "Изменено значение 2-го параметра в папке TestRegistryDLL"
End Sub
Т.е. обращение данной функции идет то же самое, однако, в случае нахождения указанного параметра она изменяет у него значение.
Ну что ж, двинулись дальше. Создавать и изменять параметры мы научились – теперь самое время научиться их удалять. К ТРЕТЬЕЙ кнопке привяжем функцию удаления. Если Вы внимательно изучали данные функции (например через Object Browser), то, наверное, заметили, что все функции возвращают тип Boolean. Воспользуемся этой замечательной возможностью для проверки эффективности работы нашей функции.
Private Sub Command3_Click()
If Reg.DeleteKey(HKEY_CURRENT_USER, "SOFTWARE\VB and VBA Program Settings\TestRegistryDLL") Then
MsgBox "Удалена папка TestRegistryDLL"
Else
MsgBox "Ничего не получилось :("
End If
End Sub
NB! Данная функция удаляет целиком папку и в ней не предусмотрено удаление отдельно взятого параметра. Как мне кажется, это самый главный недостаток данной библиотеки, при всех остальных громадных плюсах.
Ну и, наконец, оставшиеся две функции служат для поиска (получения) значений указанных параметров. Запишем код для ЧЕТВЕРТОЙ кнопки. Основа кода взята из "Советов …" Андрея Колесова
Private Sub Command4_Click()
Dim KeyFound As Boolean, strValue As String
strValue = ""
KeyFound = Reg.GetKeyValue(HKEY_CURRENT_USER, "SOFTWARE\VB and VBA Program Settings\TestRegistryDLL", "SecondValue", strValue)
If KeyFound Then
MsgBox strValue
Else
MsgBox "Такого параметра нет"
End If
End Sub
Другая функция чуть-чуть посложнее (хотя принцип тот же, что и у предыдущей), она считывает последовательно все параметры и их значения.
Private Sub Command5_Click()
Dim KeyFound As Boolean, strName As String, strValue As String
strValue = ""
Do
KeyFound = Reg.GetNextValue(HKEY_CURRENT_USER,
"SOFTWARE\VB and VBA Program Settings\TestRegistryDLL", strName, strValue)
If KeyFound = False Then Exit Do
MsgBox strName & " = " & strValue
Loop While KeyFound
MsgBox "Больше параметров нет!"
End Sub
На этом примере можно закончить работу с подключаемой библиотекой Registry Access Functions. В заключение хочется отметить необчно маленький для Microsoft размер и простоту в работе.
В заключение: Не только эта библиотека имеет доступ к реестру. Если Вы подключили к своему проекту библиотеку Word'а (начиная с версии 8) например для проверки орфографии, то параллельно, для получения значений реестра можно воспользоваться ее объектом System, а конкретнее свойствами PrivateProfileString и ProfileString. Что самое смешное, таких возможностей не имеет ни Excel, ни Access, ни PowerPoint, ни FrontPage. Ох уж этот Microsoft :).