Visual Basic, .NET, ASP, VBScript
 

   
 

Михаил Эскин немало сделал для развития русскоязычных VB сайтов. Многие знают его по статьям про ActiveX на VB сайтах, другие читают статьи Михаила уже на его собственном сайте. Михаил родился в Городском роддоме №1 города Астрахани, в “черную пятницу”, ну, так скажем, почти сорок лет назад. По-прежнему живет на Юге, правда теперь уже Германии, в прекрасном городе Мюнхене.

 
     
   
 

Итак, начнем:

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 :).

 
     

   
   
     
  VBNet рекомендует