Эта статья впервые опубликована в ClarionOnline ( http://www.clariononline.com ) в томе 2 выпуск 6 (Январь 1999)
Использование реестра
С появлением Windows 95 и Windows NT, Microsoft представила новую концепцию хранения конфигурационных данных системы и приложений - реестр.
Регистр предназначался для замены всех этих маленьких .INI файлов, которые можно найти в самых разных местах вашего жесткого диска. Вместо этого, все эти настройки должны удобно храниться в одном файле - удобно, это так, но только до тех пор, пока он не сломается и вы не сможете загрузить свой компьютер.
Однако, это другая история - я думаю, раз вы здесь, то от меня требуется небольшое объяснение. В этой статье я собираюсь объяснить, как осуществить доступ к реестру из ваших Clarion программ.
Прежде чем я это сделаю, позвольте мне попытаться немного объяснить как организован реестр. На эту тему имеется множество книг с множеством подробностей, но небольшое введение, кажется, не помешает.
Реестр хранит данные в иерархически структурированном дереве. Каждый узел дерева называется ключ (key). Каждый ключ может содержать как подключи (subkeys) так и элемент данных, называемые значения (values).
Если вы сравните это с обычным жестким диском, то увидите, что ключ подобен каталогу, а значение - имени файла. Придерживаясь этой аналогии, содержимое значения соответствует файлу данных.
Значения в основном могут быть двух различных типов (строки или числа), с вариациями (Unicode, null-terminated, big-endian, little-endian и т.д.). Рекомендуется, если у вас имеется большой объем данных (>2Kб), то данные должны быть записаны в файл, а в реестре должна быть только ссылка на этот файл. Выполняемый двоичный код не должен храниться в реестре.
В реестре имеется несколько корневых ключей, все остальные ключи являются их подключами. Главные ключи это:
| HKEY_CLASSES_ROOT | Записи реестра под этим ключом определяют типы (или классы) документов и свойства, связанные с этими типами. Данные, хранимые под этим ключом используются приложениями Windows и другими приложениями, использующими object linking and embedding (OLE). |
| HKEY_CURRENT_USER | Записи реестра под этим ключом определяют настройки текущего пользователя. Эти настройки включают установки переменных среды, данные о программах, группах, цветах, принтерах, сетевых соединениях, и настройках приложений. |
| HKEY_LOCAL_MACHINE | Записи реестра под этим ключом определяют физическое состояние компьютера, включая данные о типе шины, системной памяти, и установленной аппаратуре и программном обеспечении. |
| HKEY_USERS | Записи реестра под этим ключом определяют конфигурацию пользователя по умолчанию для новых пользователей локального компьютера и пользовательскую конфигурацию для текущего пользователя |
Вы можете также найти другие корневые ключи, в зависимости от вашей ОС. Win95/98 и Win2000 имеют зависящие от ОС ключи, которые я не буду здесь описывать - я оставляю это вам как упражнение для выполнения. Большую часть информации мы собираемся только читать, и все, что вам когда ни будь нужно будет записать, находится в одном из этих 4-х базовых ключей, описанных здесь.
Написание кода . . .
Теперь, когда мы имеем смутное представление об организации реестра, давайте проверим некоторые данные. Как и с многими другими вещами, чтение данных выглядит легче, чем их запись, поэтому в этом примере я собираюсь написать небольшую программку на Clarion, которая показывает список всех установленных программ на данном компьютере. Обычно, нам нужно знать имя ключа, в котором содержатся эти данные, и, так как мы умные программисты, мы знаем, что это HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall
Этот список также доступен в при помощи аплета панели управления ‘Add-Remove Programs’, поэтому на первый взгляд это кажется бесцельным упражнением. Но обычно установлено больше программ, и панель управления не показывает их все: она отфильтровывает те, которые не имеют значения DisplayName.
Наша первая задача - прототипирование функций работы с реестром. При помощи надежного MSDN мы узнаем, то имеется 38 функций, относящихся к реестру - к счастью, нам нужны только 4 из них:
RegOpenKeyEx(Long,Long,Long,Long,Long),Long,Pascal,Raw,Name('RegOpenKeyExA')
RegCloseKey(Long),Long,Pascal,Raw
RegEnumKey(Long,Long,Long,Long),Long,Pascal,Raw,Name('RegEnumKeyA')
RegEnumValue(Long,Long,Ulong,Ulong,Ulong,Ulong,Ulong,Ulong),Long,Pascal,Raw,Name('RegEnumValueA')
Как вы видите из прототипов, я решил использовать ANSI эквиваленты, но если вы действительно хотите наибольшего быстродействия, то вы должны использовать вместо них версии Unicode.
Следует знать, что хотя процедуры и прототипы одинаковы для Win95/98 и Win2000, они могут вести себя по-разному, особенно при удалении ключей. Функция REGDELETEKEY() (которую мы не будем использовать в этой статье) автоматически удаляет все подключи под Win95/98 - и не делает этого под Win2000.
Первая вещь, которую нам нужно сделать - это открыть базовый ключ, который мы уже знаем. Делаем это при помощи функции REGOPENKEYEX():
Loc:KeyName = 'Software\Microsoft\Windows\CurrentVersion\UnInstall' If RegOpenKeyEx(HKEY_LOCAL_MACHINE,Address(Loc:KeyName),0,Key_Query_Value,Address(Loc:KeyHandle)) = ERROR_SUCCESS ! OK End
При работе с реестром, базовый ключ передается как один параметр, а остаток ключа как другой параметр (это облегчает изменение базового ключа без манипуляций со строками). Все что мы здесь сделали - это открытие ключа с правами доступа, достаточными для перечисления (enumerate) всех подключей и значений. Мы передали адрес строки, содержащей название открываемого ключа, и адрес переменной, предназначенной для хранения хендла открытого ключа. Нам будет нужен этот хендл для прохода по всем подключам ключа.
Теперь базовый ключ открыт, и мы должны найти все его подключи (каждая установленная программа имеет свой собственный подключ). Когда у нас есть имя этого ключа, мы должны открыть подкласс и перечислить (enumerate) все значения в этом ключе. Вероятно, вы догадались, что для сложных операций вы будете быстро обрабатывать ключи, подключи, под-подключи и так далее.
Для перечисления всех подключей мы используем функцию RegEnumKey.
RegEnumKey(Loc:KeyHandle,Loc:SubKeyLoop,Address(Loc:SubKeyName),100)
Она принимает хендл базового ключа, который мы только что открыли, номер ключа который мы хотим, строку для имени ключа, и число определяющее длину строки. Для поиска всех подключей ключа, просто поместите вызов этой функции в цикл и увеличивайте номер ключа до тех пор, пока возвращаемое значение не станет равно ERROR_NO_MORE_ITEMS (константа, равная 259).
Если вызов функции закончился успешно, она возвращает 0 и строка содержит имя подключа. Мы должны объединить эту строку с оригинальной для поиска нового полностью-определенного имени подключа, которое мы затем передадим другому вызову функции REGOPENKEYEX().
Если мы успешно открыли подключ, нам нужно перечислить все значения в этом ключе. Для этого будем использовать функцию REGENUMVALUE():
Loc:ValueNameStrSize = 50
Loc:ValueDataStrSize = 500
Loc:ValueDataType = Reg_SZ
x# = RegEnumValue( Loc:SubKeyHandle,Loc:ValueLoop,Address(Loc:ValueNameStr),|
Address(Loc:ValueNameStrSize),NULL,Address(Loc:ValueDataType),|
Address(Loc:ValueDataStr),Address(Loc:ValueDataStrSize))
Это подобно использованию REGENUMKEY() - она принимает хендл на только что открытый подкласс, и использует числовой счетчик для прохода через все значения подключа. Также как и REGENUMKEY() она возвращает ERROR_NO_MORE_ITEMS если значений больше нет. Она также принимает как параметр адрес строки, куда будет помещено имя значения, длину строки, адрес переменной, которая содержит тип данных, адрес строки содержащей само значение, и длину этой строки.
Итак, что из этого получилось? Чтото подобно этому:
Loc:KeyName = 'Software\Microsoft\Windows\CurrentVersion\UnInstall'>
If RegOpenKeyEx(HKEY_LOCAL_MACHINE,|
Address(Loc:KeyName),0,Key_Query_Value,Address(Loc:KeyHandle)) <> ERROR_SUCCESS
Then
Message('Unable to open registry key','User Notification')
Else>
Loc:SubKeyLoop = 1
Loop
Case RegEnumKey(Loc:KeyHandle,Loc:SubKeyLoop,Address(Loc:SubKeyName),100)
Of Error_Success
Loc:SubKeyLoop += 1
Loc:Keyname = 'Software\Microsoft\Windows\CurrentVersion\UnInstall\' &
Clip(Loc:SubKeyName)
Case RegOpenKeyEx(HKEY_LOCAL_MACHINE,Address(Loc:KeyName),0,Key_Query_Value,Address(Loc:SubKeyHandle))
Of Error_Success>
Rq:Value=-1;RQ:DataString = Clip(Loc:SubKeyName);Add(RegistryQ)
Loc:ValueLoop = 0>
Loop
Loc:ValueNameStrSize = 50
Loc:ValueDataStrSize = 500
Loc:ValueDataType = Reg_SZ
x# = RegEnumValue( Loc:SubKeyHandle,Loc:ValueLoop,Address(Loc:ValueNameStr),Address(Loc:ValueNameStrSize),NULL,|
Address(Loc:ValueDataType),Address(Loc:ValueDataStr),Address(Loc:ValueDataStrSize))
Case x#
Of Error_Success
Loc:ValueLoop += 1
If Clip(Loc:ValueNameStr) = '' Then
Loc:ValueNameStr = '{{Default}'
End
If Clip(Loc:ValueDataStr) = '' Then
Loc:ValueDataStr = '<<NONE>'
End
Rq:Value=2;RQ:DataString = Clip(Loc:ValueNameStr);Add(RegistryQ)
Rq:Value=3;RQ:DataString = Clip(Loc:ValueDataStr);Add(RegistryQ)
Of Error_No_More_Items
Break
Else
Message('Unable to enumerate value for key "' & Clip(Loc:SubKeyName) & '"','User Notification')
Break>
End
End
If RegCloseKey(Loc:SubKeyHandle) <>ERROR_SUCCESS
Message('Unable to close registry key','User Notification')
End
Else
Message('Unable to open sub-key ' & Clip(Loc:SubKeyName),'User Notification')
End
Of Error_No_More_Items
Break
Else
Message('Unable to enumerate key','User Notification')
Break
End
End
If RegCloseKey(Loc:KeyHandle) <> ERROR_SUCCESS
Message('Unable to close registry key','User Notification')
End
End
Этот код должен читать реестр и строить queue из всех програм, установленных на машине, и все всех их соответствующих значений. Если вы сравните этот список, который формирует этот код, со списком, который выдает панель управления, то сможете увидеть, что реестр содержит много больше информации.
Как я упоминал в начале, чтение реестра гораздо легче чем запись в него, главным образом из-за того, что нужно создавать ключи если они не существуют перед тем как открыть их. Это на самом деле не так сложно сделать, но это замедляет кодирование и забирает ценное время разработки. Итак, в дополнение к этому ежемесячному download-у, имеется также дополнительный бонус . . .
Download
Download этого месяца включает некоторый код, который я написал для доступа к реестру. Это исходный текст для DLL, названный REGLIB, который содержит аналоги функций GetIni и PutIni для реестра, названные GetRegistry и PutRegistry.
Они обе принимают такое же количество параметров, как их эквиваленты для INI файлов, но делают свою работу немного по-другому. GetRegistry принимает 4 параметра - две строки, опциональную строку, и long, и возвращает строку, содержащую требуемые значения:
String1 - имя ключа String2 - имя искомого значения String3 - значение для возврата по умолчанию, если искомое значение не найдено Long1 - базовый ключ
PutRegistry также принимает 4 параметра - строку, две опциональные строки, и long:
String1 - имя ключа
String2 - имя искомого значения
String3 - данные для помещения в значение
Long1 - базовый ключ
Если 3-й параметр (данные) опущен или пустая строка, то значение удаляется из ключа.
Если 2-й параметр (значение) опущен или пустая строка, то удаляется весь ключ (с пониманием, что все подключи должны быть сначала удалены, если мы работаем под Windows NT).
Надеюсь, что эти функции будут использоваться для экономии времени разработки, и облегчат разработчикам Clarion использование реестра для хранения настроек приложений.
Выводы
Использование реестра не так страшно, как кажется сначала. С небольшой основой, это так же легко как использование INI файлов, и только благодаря этой сложности вы будете счастливы знать, что 99% ваших пользователей не смогут изменить значения, которые не предназначены для них!
В следующем выпуске …
Использование Simple MAPI для email-enable ваших Clarion приложений.
Возврат к домашней странице. Или пишите мне paula@attglobal.net
(c) 1999 Paul Attryde