Автор:
МалаяПод редакцией
Клуба программистов «Весельчак У».
Эта статья посвящена некоторым функциям, позволяющим нам работать с реестром. Сразу оговорюсь — к статье прикреплен класс
CRegistryManip (см. раздел «
Файлы»), в котором собраны некоторые примеры по применению вышеозначенных функций.
Была поставлена задача: создать пользовательский «MessageBox», который, кроме стандартных элементов типа «текст» и «кнопки», имел бы еще и «CheckBox», перевод которого в состояние «установлено» должен был бы приводить к тому, что это сообщение больше не будет показано.
Соответственно, эта информация должна быть сохранена в реестре. Причем в виде пары значений: «StringId» (все тексты должны быть внесены в «string table» из-за поддержки многоязычности в проекте) + «Flag» (т.е. состояние этого «CheckBox»).
Благодаря этому и появился вышеупомянутый класс для работы с реестром.
Реестр имеет иерархическую структуру, аналогичную каталогу (вложенные папки, деревья). Корневые элементы представляют собой установленные Майкрософтом константы.
HKEY_CLASSES_ROOT
HKEY_CURRENT_CONFIG
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE
HKEY_USERS
Каждый из корневых элементов — корневых ключей — имеет подключи — папки (далее — ключи или подключи), те, в свою очередь, тоже могут иметь свои подключи и т.д. В подключе, находящемся в этой цепочке, могут содержаться параметры и соответствующие им значения. Например:
HKEY_LOCAL_MACHINE
|
SOFTWARE
|
Microsoft
|
IE4
|
Setup
|
Path = "%programfiles%\Internet Explorer"
В примере:
- HKEY_LOCAL_MACHINE — корневой ключ.
- Path — параметр;
- все остальные — подключи.
С помощью стандартных функций мы можем:
- считывать и запоминать имена ключей и параметров;
- считывать и запоминать значения параметров;
- создавать и удалять ключи;
- получать общую информацию о ключах и их параметрах.
Эти две команды - основа основ. И они самые простые в использовании. ;-)
CRegistryManip::OpenRegKey (
const HKEY hKey,
const TCHAR* pSubKey,
HKEY& hKeyPrograms,
REGSAM samDesired = KEY_READ
);
Где:
- hKey — один из корневых ключей в реестре:
- HKEY_CLASSES_ROOT,
- HKEY_CURRENT_CONFIG,
- HKEY_CURRENT_USER,
- HKEY_LOCAL_MACHINE,
- HKEY_USERS;
- pSubKey — подраздел в реестре, который нас интересует (например: «Microsoft\IE4\Setup»);
- hKeyPrograms — указатель на handle (описатель) открытого ключа;
- samDesired — уровень доступа (для чтения значений, для чтения имен параметров, записи, создания ключей и т.д.):
- KEY_ALL_ACCESS — максимальный доступ. Представляет собой комбинацию ключей: KEY_QUERY_VALUE, KEY_ENUMERATE_SUB_KEYS, KEY_NOTIFY, KEY_CREATE_SUB_KEY, KEY_CREATE_LINK и KEY_SET_VALUE;
- KEY_READ — для чтения информации из реестра. Представляет собой комбинацию ключей: KEY_QUERY_VALUE, KEY_ENUMERATE_SUB_KEYS и KEY_NOTIFY;
- KEY_WRITE — для записи информации в реестр. Представляет собой комбинацию ключей: KEY_SET_VALUE и KEY_CREATE_SUB_KEY;
- KEY_QUERY_VALUE — для опроса значений параметров;
- KEY_ENUMERATE_SUB_KEYS — для чтения имен подключей;
- KEY_NOTIFY — для изменения нотификаций;
- KEY_CREATE_SUB_KEY — для создания подключа;
- KEY_CREATE_LINK — для создания линка;
- KEY_SET_VALUE — для установки значений параметров.
Чтобы открыть ключ, используем стандартную функцию RegOpenKeyEx().
Эта функция получает: hKey — имя корневого ключа в реестре, pSubKey — имя подключа, который мы хотим открыть и samDesired — уровень доступа к информации. В случае удачного завершения функция возвращает hKeyPrograms — указатель на открытый ключ.
Возвращаемое значение функции ::RegOpenKeyEx() — тип ULONG. Значение ERROR_SUCCESS означает благополучное завершение выполнения функции, все остальное говорит о том, что нам где-то и в чем-то не повезло. ;-)
Вызов выглядит следующим образом:
CString cstrSubKey (_T("SOFTWARE\\Microsoft\\IE4\\Setup"));
HKEY hKeyPrograms = NULL;
ULONG nRes = OpenRegKey (
HKEY_LOCAL_MACHINE,
cstrSubKey,
hKeyPrograms
);
(Какой-либо ключ, который был перед этим открыт с помощью OpenRegKey().)
CRegistryManip::CloseRegKey(
HKEY hKey
);
Где hKey — тот самый указатель на handle (описатель) открытого ключа, который нам вернула функция OpenRegKey().
Чтобы закрыть ключ используем стандартную функцию RegCloseKey().
Эта функция получает hKey — указатель на открытый ключ. Она закрывает наш сеанс работы с реестром, открытый функцией RegOpenKeyEx(). Эти функции всегда должны вызываться в паре.
Вызов выглядит следующим образом:
CString cstrSubKey (_T("SOFTWARE\\Microsoft\\IE4\\Setup"));
HKEY hKeyPrograms = NULL;
BOOL bRes = OpenRegKey (
HKEY_LOCAL_MACHINE,
cstrSubKey,
hKeyPrograms
);
//.....
bRes = CloseRegKey (hKeyPrograms);
Мы можем проверить, существует ли в реестре нужный нам ключ. Например, «HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\IE4\Setup».
CRegistryManip::CheckIfKeyExists (
const HKEY hKey,
const TCHAR* pSubKey
);
Где:
- hKey — один из корневых ключей в реестре:
- HKEY_CLASSES_ROOT,
- HKEY_CURRENT_CONFIG,
- HKEY_CURRENT_USER,
- HKEY_LOCAL_MACHINE,
- HKEY_USERS;
- pSubKey — подраздел в реестре, который нас интересует (в нашем случае: «SOFTWARE\Microsoft\IE4\Setup»).
Для проверки нам достаточно вызвать функцию OpenRegKey(): если интересующий нас ключ существует, функция вернет TRUE, иначе — FALSE.
Вызов выглядит следующим образом:
CString cstrSubKey (_T("SOFTWARE\\Microsoft\\IE4\\Setup"));
BOOL bRes = OpenRegKey (
HKEY_LOCAL_MACHINE,
cstrSubKey
);
Например, «HKEY_LOCAL_MACHINE\SOFTWARE\Irina».
CRegistryManip::CreateNewKey (
const HKEY hKeyBase,
const TCHAR* pKeyPath,
const TCHAR* pNewKey
);
Где:
- hKeyBase — один из корневых ключей в реестре:
- HKEY_CLASSES_ROOT,
- HKEY_CURRENT_CONFIG,
- HKEY_CURRENT_USER,
- HKEY_LOCAL_MACHINE,
- HKEY_USERS;
- pKeyPath — подраздел в реестре, который нас интересует (в нашем случае: «SOFTWARE»). Этот ключ — родитель для нашего нового ключа;
- pNewKey — наш новый ключ (в нашем случае — «Irina»).
Чтобы создать ключ, используем стандартную функцию RegCreateKeyEx().
Эта функция получает
hKey (имя основного ключа в реестре),
pSubKeyNew = pKeyPath (имя подключа, который мы хотим открыть) +
pNewKey (имя нового подключа) и
samDesired (уровень доступа к информации).
В случае удачного завершения функция возвращает hKeyPrograms (указатель на созданный подключ).
В списке есть еще некоторые параметры:
- lpClass — имя класса (Я долго искала какую-либо информацию о том, каково практическое применение этого параметра, но ничего умного не нашла... Во всех приводимых примерах этот параметр равен NULL. Я последовала примеру. ;-) Если кто-то сможет меня поправить в этом вопросе, буду премного благодарна.);
- dwOptions — специальный флаг, который по умолчанию равен REG_OPTION_NON_VOLATILE. В обычной жизни этого хватает (Особо осложненные жизненные обстоятельства я не рассматривала, так как у меня на это просто не было времени. Но я думаю, что при необходимости всегда можно найти нужную информацию.);
- lpSecurityAttributes — устанавливает правила для наследования (использования) полученного указателя на созданный подключ процессами — детьми основного процесса. Опять же, в обычной жизни может быть без зазрения совести равен NULL;
- lpdwDisposition — флаг, который говорит о том, что либо этот ключ уже существует, либо еще нет и должен быть создан. Если установить его в NULL, то система сама решает, что ей делать исходя из наличия или отсутствия заданного ключа. То есть, если ключ уже существует, функция работает как RegOpenKeyEx(), если нет — она создает заданный ключ;
- samDesired — уровень доступа к информации в реестре. Для создания ключа должен быть установлен как минимум равным KEY_CREATE_SUB_KEY (но лучше всего — KEY_ALL_ACCESS).
Еще одно замечание: никогда не получится создать параметр непосредственно под основным ключом (HKEY_CLASSES_ROOT и т.д.). Необходимо сначала создать свой подключ и только затем создавать параметры.
Вызов выглядит следующим образом:
CString cstrSubKey (_T("SOFTWARE"));
CString cstrNewKey (_T("Irina"));
BOOL bRes = CreateNewKey (
HKEY_LOCAL_MACHINE,
cstrSubKey,
cstrNewKey
);
CRegistryManip::DeleteInfoFromRegistry (
const HKEY hKey,
const char* pSubKey,
const TCHAR* pKeyName
);
Где:
- hKey — один из корневых ключей в реестре: HKEY_CLASSES_ROOT и т.д.;
- pSubKey — подраздел в реестре, который нас интересует (например: «SOFTWARE\Irina»);
- pKeyName — имя параметра (Например, подключ «Irina» иммет параметр «StringID» со значением, равным 2000. В этом случае pKeyName равно «StringID».).
Это основная функция, которая, в свою очередь, вызывает
DeleteSubKeyFromRegistry() или
DeleteParamFromRegistry() — в зависимости от того, что мы хотим удалить (описание функций — см. пункты «
удаляем подключ» и «
удаляем параметр»).
Вызов выглядит следующим образом:
BOOL bRes = FALSE;
// удаление параметра
bRes = DeleteInfoFromRegistry (
HKEY_LOCAL_MACHINE,
_T("SOFTWARE\\Irina"),
_T("StringID")
);
// удаление подключа
bRes = DeleteInfoFromRegistry (
HKEY_LOCAL_MACHINE,
_T("SOFTWARE\\Irina"),
NULL
);
CRegistryManip::DeleteSubKeyFromRegistry (
const HKEY hKey,
const char* pSubKey
);
Чтобы удалить подключ, используем стандартную функцию RegDeleteKey().
Эта функция получает:
- hKey — указатель на открытый ключ;
- pSubKey — имя подключа, который мы хотим удалить.
CRegistryManip::DeleteParamFromRegistry (
const HKEY hKey,
const char* pSubKey,
const TCHAR* pKeyName
);
Если речь идет о подключе, то достаточно вызова стандартной функции RegDeleteKey().
Если же надо удалить параметр, то тогда надо сначала открыть реестр, затем удалить параметр при помощи стандартной функции RegDeleteValue(), в заключение закрыть реестр.
При открытии реестра уровень доступа должен быть как минимум KEY_SET_VALUE. Для удаления параметра используем стандартную функцию RegDeleteValue().
Эта функция получает:
- hKey — указатель на открытый ключ;
- lpValueName — имя параметра, который мы хотим удалить.
Например:
HKEY_LOCAL_MACHINE\\SOFTWARE\\Irina — подключ
|
StringId = 2000 (StringId — это имя параметра, 2000 — его значение)
CRegistryManip::SetOneValue (
HKEY hKey,
const TCHAR* pKeyName,
const TCHAR* pKeyVal,
DWORD dwType = REG_SZ
);
Где:
- hKey — указатель на handle (описатель) открытого ключа, который нам возвращает функция OpenRegKey();
- pKeyName — имя параметра;
- pKeyVal — значение параметра;
- dwType — тип параметра.
Чтобы установить параметр, используем стандартную функцию RegSetValueEx().
Эта функция получает:
- hKey — указатель на открытый ключ;
- lpReserved — зарезервированное значение, равно NULL;
- pKeyName — имя параметра, значение которого мы хотим установить;
- dwType — тип данных;
- lpData — адрес переменной (т.е. области памяти), в которую будет помещен результат (тип этой переменной зависит от типа данных);
- cbData — размер области этой памяти.
Если тип данных — REG_SZ, то для получения результата надо создать массив типа char (или TCHAR, если мы работаем с Unicode).
Если тип данных — REG_BINARY, то для получения результата надо создать переменную типа int.
Если тип дfанных — REG_DWORD, то для получения результата надо создать переменную типа DWORD.
Применение преобразования любого из этих типов данных в (BYTE*) необходимо для получения правильного результата.
Вызов выглядит следующим образом:
CString cstrSubKey (_T("SOFTWARE\\Irina"));
CString cstrParName (_T("StringID"));
CString cstrParVal (_T("2000"));
HKEY hKeyPrograms = NULL;
// Сначала вызываем
// OpenRegKey(HKEY_LOCAL_MACHINE, cstrSubKey, hKeyPrograms),
// т.к. сначала надо открыть ключ, в котором находится
// наш параметр, затем вызываем функцию по установке
// значения параметра. В данном случае мы не передаем тип,
// т.к. он по умолчанию уже установлен как REG_SZ.
ULONG nRes = SetOneValue (
hKeyPrograms,
cstrParName,
cstrParVal
);
// В конце закрываем реестр: CloseRegKey(hKeyPrograms).
CRegistryManip::getInfoFromRegistry (
const HKEY hKey,
const TCHAR* pSubKey,
const TCHAR* pRegKey,
TCHAR *pRes,
DWORD dwType = REG_SZ
);
Где:
- hKey — один из корневых ключей в реестре: HKEY_CLASSES_ROOT и т.д.;
- pSubKey — подраздел в реестре, который нас интересует (например, «Software\Irina»);
- pKeyName — имя параметра (например: «StringID»);
- pRes — значение параметра (возвращаемое этой функцией);
- dwType — тип параметра.
Сама функция осуществляет следующие действия: открывает реестр и вызывает долонительную функцию CRegistryManip::GetOneValueFromReg(), которая считывает информацию из реестра в зависимости от типа данных:
CRegistryManip::GetOneValueFromReg (
HKEY hKey,
const TCHAR* pRegKey,
TCHAR *pRes,
DWORD dwType = REG_SZ
);
Где:
- hKey — указатель на handle (описатель) открытого ключа, который нам вернула функция OpenRegKey();
- pRegKey — имя параметра (например, «StringID»);
- pRes — значение параметра, возвращаемое этой функцией;
- dwType — тип параметра.
Для получения значения используем стандартную функцию RegQueryValueEx().
Эта функция получает следующие параметры:
- hKey — указатель на открытый ключ;
- lpValueName — имя параметра, значение которого мы хотим получить;
- lpReserved — зарезервированное значение, равно NULL;
- lpType — адрес переменной (т.е. области памяти), в которую будет помещен тип результата;
- lpData — адрес переменной, в которую будет помещен результат. В зависимости от типа данных контейнером для получения значения выступает либо строка символов, либо переменная типа int;
- lpcbData — адрес переменной, в которой находится размер переменной, предназначенной для помещения в нее результата.
Применение преобразования любого из этих типов данных в (BYTE*) необходимо для получения правильного результата. Вызов выглядит следующим образом (я беру пример вызова этой функции из getInfoFromRegistry()):
HKEY hKeyPrograms; // получим при вызове OpenRegKey()
TCHAR dwBuffer[1024];
GetOneValueFromReg(hKeyPrograms, pRegKey, dwBuffer, dwType);
Например:
HKEY_LOCAL_MACHINE\\SOFTWARE\\Irina - папка (подключ)
|
StringId -> параметр
|
Flag -> параметр
|
Param_bin -> параметр
Итого в папке 3 параметра. Наша задача — получить это количество программным путем.
CRegistryManip::GetCountOfSubKeys (
HKEY hKey,
unsigned long& nSubKeysCount
);
Где:
- hKey — указатель на handle (описатель) открытого ключа, который нам возвращает функция OpenRegKey();
- nSubKeysCount — количество параметров, возвращаемое функцией значение.
Для получения этой информации используем стандартную функцию RegQueryInfoKey().
- hKey — указатель на открытый ключ в реестре, который нас интересует;
- lpClass — адрес переменной, где находится имя класса;
- lpcbClass — адрес переменной, в которой находится размер буфера для имени класса;
- lpReserved — зарезервированный параметр, равен NULL;
- lpcSubKeys — адрес переменной, в которой находится результат — количество подключей;
- lpcbMaxSubKeyLen — адрес переменной, в которой находится длина для имени подключа;
- lpcbMaxClassLen — адрес переменной, в которой находится длина буфера, предназначенного для хранения имени класса;
- lpcValues — адрес переменной, в которой находится еще один результат — количество параметров в данном ключе;
- lpcbMaxValueNameLen — адрес переменной, в которой находится размер буфера для имени параметра или подключа;
- lpcbMaxValueLen — адрес переменной, в которой находится размер буфера для значения параметра;
- lpcbSecurityDescriptor — адрес переменной, в которой может находиться security-информация;
- lpftLastWriteTime — адрес переменной типа FILETIME, в которой находится время последнего изменения этого ключа.
Комментарий: за исключением переменных hKey, lpcSubKeys, и lpcValues я
так и не поняла, где и как все остальное может быть использовано... Пардон ;-)
В нашем случае нас интересует значение переменной lpcValues (в нашем примере оно будет равно 3). Значение переменной lpcSubKeys в нашем примере будет равно 0, т.к. наш ключ не имеет вложенных подключей.
CRegistryManip::FindAllParamsWithValues (
HKEY hKeyBase,
const CString& csKeyPath,
CStringArray& acsKeyList,
CStringArray& acsValList
);
Где:
- hKeyBase — один из корневых ключей в реестре: HKEY_CLASSES_ROOT и т.д.;
- csKeyPath — подраздел в реестре, который нас интересует (например, «Software\Irina»);
- acsKeyList — список имен параметров;
- acsValList — список значений параметров.
Для получения этой информации нужно сделать несколько шагов:
- с помощью функции GetCountOfSubKeys() получаем количество параметров;
- для каждого параметра сначала считываем его имя с помощью функции RegEnumValue();
- затем при наличии имени получаем всю информацию о параметре с помощью функции RegQueryMultipleValues(). Среди прочего мы получаем тип параметра. Это необходимо для получения значения параметра;
- само значение мы получаем с помощью функции GetOneValueFromReg().
- hKey — указатель на открытый ключ, который нас интересует;
- dwIndex — индекс параметра;
- lpValueName — адрес буфера, в который будет помещено имя параметра;
- lpcbValueName — адрес переменной, в которой находится размер буфера для имени параметра;
- lpReserved — зарезервированный параметр, равен NULL;
- lpType — адрес переменной, в которой находится тип возвращаемого значения. В нашем случае он равен REG_NONE, т.к. мы его не знаем и значение параметра с помощью этой функции не считываем;
- lpData — адрес буфера, в который будет помещено значение параметра;
- lpcbData — адрес переменной, в которой находится размер буфера для значения параметра.
- hKey — указатель на открытый ключ, который нас интересует;
- val_list — указатель на структуру, в которой будут находиться результаты. Перед вызовом функции мы должны внести сюда имя параметра:
VALENT val_list[1];
val_list[0].ve_valuename = *lpValueName;
// получено перед этим с помощью RegEnumValue()
- num_vals — размер структуры val_list;
- lpValueBuf — адрес буфера, в который будет помещено значение параметра (если мы заранее не знаем тип значения, то возвращаемое значение может быть представлено в такой форме, в которой оно нечитабельно);
- ldwTotsize — адрес переменной, в которой находится размер буфера для значения параметра.
У меня в проекте была пара значений «StringId» (основной) — «Flag» (зависимый от основного), которые должны были изменяться синхронно, посему в этой функции есть два имени параметров и, соответственно, два значения. В обычной жизни это может быть и один параметр.
BOOL CRegistryManip::ChangeOneVal (
const HKEY hKey,
const TCHAR* pSubKey,
const TCHAR* pRegKeyMain,
const TCHAR* pRegKeyToChange,
int nValMain,
int nValPaar,
BOOL& bWasFound
);
Где:
- hKey — один из корневых ключей в реестре: HKEY_CLASSES_ROOT и т.д.;
- pSubKey — имя подключа;
- pRegKeyMain — имя основного параметра (при изменениях этот параметр остается нетронутым);
- pRegKeyToChange — имя вспомогательного параметра, который и надо изменить;
- nValMain — значение основного параметра;
- nValPaar — новое значение вспомогательного параметра;
- bWasFound — указывает на успешное завершение.
Для изменения значения параметра нужно сделать несколько шагов:
- сначала с помощью CheckIfKeyExists() проверяем, существует ли заданный подключ;
- если нет — сначала создаем этот подключ. Для этого:
- анализируем строку pSubKey, разбивая ее на подэлементы на основании разделителя "\\";
- и затем проверяем каждый из них на наличие в registry с помощью CheckIfKeyExists();
- если папка не существует, создаем ее с помощью CreateNewKey().
Пример: pSubKey = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Irina\\projects\\proj_1\\settings".
Подэлементы: HKEY_LOCAL_MACHINE, SOFTWARE, Irina, projects, proj_1, settings.
Проверяем по нарастающей:
- HKEY_LOCAL_MACHINE\\SOFTWARE,
- HKEY_LOCAL_MACHINE\\SOFTWARE\\Irina,
- HKEY_LOCAL_MACHINE\\SOFTWARE\\Irina\\projects и т.д.
- затем находим в заданном подключе все параметры и их значения с помощью FindAllParamsWithValues();
- полученную информацию анализируем:
- если уже есть параметр pRegKeyMain со значением nValMain, то мы просто меняем имя вспомогательного параметра — SetOneValue();
- если параметр pRegKeyMain со значением nValMain отсутствует, сначала создаем его, а затем создаем дополнительный параметр — InputNewValue().
На этом позволю себе закруглиться. Если кому-то это описание поможет, буду очень рада.
С уважением, Малая.