Программирование для системного реестра на С++
©
Ни одно профессиональное Windows-приложение не обходится без обращения к центальной базе данных всей системы - системному реестру Windows (registry). Между тем в интернете и сопутствующих изданиях достаточно мало рассказывается о Win32-функциях, которые позволяют взаимодействовать с системным реестром Windows и программисту нужно обращаться к Platform SDK, которая ко всему прочему на английском языке, что для некоторых является камнем преткновения. Данная статья содержит описание основных функций и положений программирования реестра на C++.
Раздел системного реестра, является стандартным объектом исполнительной системы, который экспортирует подсистема Win32, и к которому можно получить доступ через описатель (handle). Такая схема получения доступа используется для всех объектов ядра, экспортируемых через подсистему Win32. Общие сведения о функционировании объектов ядра содержатся в книге Дж. Рихтера "Windows для профессионалов".
Как и для любого объекта ядра, для обращения к разделу реестра нужно получить его описатель и указать действия, которые вы будете выполнять с ним. Описатель раздела системного реестра можно получить создавая или открывая раздел, для этого предназначены следующие Win32-функции.
RegOpenKey
RegOpenKeyEx
RegCreateKeyEx
Как видно из названия функции RegOpenKey и RegOpenKeyEx открывают раздел реестра (получают описатель), а RegCreateKeyEx - создает раздел реестра и тоже получает описатель. Прототипы функции и их использование объяснены ниже.
LONG RegOpenKey (HKEY hKey, LPCTSTR lpSubKey, PHKEY phkResult)
Функция открывает раздел реестра.
hKey Описатель открываемого раздела, который может быть получен функциями RegCreateKeyEx и RegOpenKeyEx. Microsoft упростила жизнь разработчику, определив стандартные описатели:
HKEY_CLASSES_ROOT HKEY_CURRENT_CONFIG HKEY_CURRENT_USER HKEY_LOCAL_MACHINE HKEY_USERS Для Windows Me/98/95 также: HKEY_DYN_DATA |
lpSubKey Указатель на строку, завершающуюся нулевым байтом, которая содержит имя открываемого раздела.
Этот раздел должен быть подразделом, идентифицируемого описателем раздела. Если этот параметр NULL, то функция вернет описатель самого раздела, т. е. раздела, идентифицируемого описателем. phkResult Указатель на переменную, получающую описатель открытого раздела.
Если открытие произошло успешно, функция вернет ERROR_SUCCESS, в противном случае вернет ненулевой код ошибки, определенный в Winerror.h
LONG RegOpenKeyEx(HKEY hKey, LPCTSTR lpSubKey, DWORD ulOptions,
REGSAM samDesired, PHKEY phkResult)
Функция открывает раздел реестра.
hKey Описатель открываемого раздела, который может быть получен функциями RegCreateKeyEx и RegOpenKey. Действуют стандартные описатели, перечисленные выше. lpSubKey Указатель на строку, завершающуюся нулевым байтом, которая содержит имя открываемого раздела. Этот раздел должен быть подразделом, идентифицируемого описателем раздела. Если этот параметр NULL, то функция вернет описатель самого раздела, т. е. раздела, идентифицируемого описателем. ulOptions Зарезервировано - 0. samDesired Определяет права доступа (действия, которые будет проделывать с разделом программист). Как уже упоминалось, раздел реестра является системным объектом, а следовательно он имеет дескриптор защиты, именно в нем перечисляются права пользователей на объект. Определены следующие стандартные макросы:
KEY_ALL_ACCESS | Разрешаются любые действия над разделом |
KEY_ENUMERATE_SUB_KEYS | Разрешается перечисление подразделов данного раздела |
KEY_READ | Разрешается чтение раздела |
KEY_SET_VALUE | Разрешается создавать, удалять параметр или устанавливать его значение |
KEY_QUERY_VALUE | Разрешается запрос параметра раздела |
LONG RegCreateKeyEx(HKEY hKey, LPCTSTR lpSubKey, DWORD Reserved,
LPTSTR lpClass, DWORD dwOptions, REGSAM samDesired,
LPSECURITY_ATTRIBUTES lpSecurityAttributes, PHKEY phkResult,
LPDWORD lpdwDisposition);
Функция создает раздел реестра. Если раздел уже существует, функция открывает его. Имена разделов не чувствительны к регистру.
hKey Описатель открываемого раздела. lpSubKey Указатель на строку, завершающуюся нулевым байтом, которая содержит имя создаваемого или открываемого раздела. Reserved Зарезервировано - 0. lpClass Указатель на строку, завершающуюся нулевым байтом, которая содержит класс этого раздела. Может быть проигнорирован и равен NULL. Используется для подключения к удаленному реестру. dwOptions Параметр может принимать следующие значения.
REG_OPTION_BACKUP_RESTORE | Если флаг установлен, функция игнорирует параметр samDesired и пытается открыть раздел с правами, для резервного копирования и восстановления раздела. Если поток вызывает функцию, обладая привилегией SE_BACKUP_NAME, то раздел открывается с правами доступа ACCESS_SYSTEM_SECURITY и KEY_READ. Если вызывающий поток обладает привилегией SE_RESTORE_NAME, то раздел открывается с правами доступа ACCESS_SYSTEM_SECURITY и KEY_WRITE. Если поток обладает обоими привилегиями, то раздел открывается с комбинированными правами доступа. |
REG_OPTION_VOLATILE | Раздел, созданный с помощью этого флага - изменчив. При этом информация сохраняется в памяти и не сохраняется, когда соответствующий улей выгружается. Для HKEY_LOCAL_MACHINE, это происходит, когда компьютер выключается или перезагружается. Для разделов реестра, загруженных функцией RegLoadKey, это происходит, когда выполнена функция RegUnLoadKey. Функция RegSaveKey не сохраняет изменчивые разделы реестра. Этот флаг игнорируется, если раздел уже существует. |
REG_OPTION_NON_VOLATILE | Раздел, созданный с помощью этого флага - не изменяем. При этом информация записывается в файл реестра. Функция RegSaveKey сохраняет не изменчивые разделы. |
В эту структуру, входит дескриптор защиты раздела. Если параметр равен NULL, раздел получает дескриптор защиты по умолчанию. Список управления доступом (ACL) в заданном по умолчанию дескрипторе защиты для раздела, наследуются от родительского раздела. phkResult Указатель на переменную, получающую описатель открытого или созданного раздела. lpdwDisposition Указатель на переменную, в которую записывается одно из следующих значений.
REG_CREATED_NEW_KEY | Раздел не существует и будет создан |
REG_OPENED_EXISTING_KEY | Раздел существует |
Если параметр равен NULL, то никакая информация не возвращается.
Если открытие произошло успешно, функция вернет ERROR_SUCCESS, в противном случае вернет ненулевой код ошибки, определенный в Winerror.h
После получения описателя раздела реестра с ним проделывают нужные действия, например считывают или записывают значения параметров. После проделанных операций описатель раздела реестра должен быть корректно закрыт. Для этого существует функция RegCloseKey, прототип которой показан ниже.
LONG RegCloseKey(HKEY hKey);
hKey Описатель открытого раздела, который подлежит закрытию.
Если описатель успешно освобожден, функция возвращает ERROR_SUCCESS, в противном случае вернет ненулевой код ошибки, определенный в Winerror.h
Используя вышесказанные функции можно написать следующий код, создающий раздел и закрывающий его.
HKEY h;
if(RegCreateKeyEx(HKEY_CURRENT_USER, "12", 0, NULL, REG_OPTION_VOLATILE, KEY_WRITE, NULL, &h, NULL)!=ERROR_SUCCESS) { printf("Could not create the registry key."); abort(); } //делаем определенные действия с разделом RegCloseKey(h);
В этом примере создается раздел для записи с названием 12, которого уже не будет при следующей загрузке профиля пользователя. Затем корректно освобождаем описатель созданного раздела.
После того, как раздел создан или открыт в нем могут быть созданы подразделы, считана информация о нем, созданы параметры и т. д. Для каждой из перечисленных операций в Win32 API предусмотрена соответствующая функция.
Рассмотрим функцию, которая позволяет получать информацию о разделе реестра.
LONG RegQueryInfoKey(HKEY hKey, LPTSTR lpClass, LPDWORD lpcClass, LPDWORD lpReserved, LPDWORD lpcSubKeys, LPDWORD lpcMaxSubKeyLen, LPDWORD lpcMaxClassLen, LPDWORD lpcValues, LPDWORD lpcMaxValueNameLen, LPDWORD lpcMaxValueLen, LPDWORD lpcbSecurityDescriptor, PFILETIME lpftLastWriteTime);
hKey Описатель открытого раздела. Раздел должен быть открыт с правами KEY_QUERY_VALUE. lpClass Указатель на строку, завершающуюся нулевым байтом, которая содержит класс этого раздела. Может быть проигнорирован и равен NULL. Используется для подключения к удаленному реестру. lpсClass Указатель на переменную, которая содержит размер буфера, на которую указывает lpClass. Размер должен включать и завершающий нулевой символ. После того, как функция возвратит значение, в этой переменной будет сохранена информация о размере буфера-приемника - lpClass. Возвращаемое значение не включает нулевой символ. Если буфер является недостаточным для сохранения данных, то функция возвращает значение ERROR_MORE_DATA и переменная содержит размер строки в байтах, нулевой символ не учитывается. Если параметр lpClass содержит правильный адрес, но lpсClass является неверным, например NULL, то функция возвращает ERROR_INVALID_PARAMETER. В Windows Me/98/95 если параметр lpClass содержит правильный адрес, но lpсClass является неверным, например NULL, то функция возвращает ERROR_SUCCESS, вместо ERROR_INVALID_PARAMETER. lpReserved Зарезервировано - должен быть NULL. lpcSubKeys Указатель на переменную, получающую кол-во подразделов данного раздела. Может быть - NULL. lpcMaxSubKeyLen Указатель на переменную, которая получает размер самого длинного имени подраздела данного раздела, нулевой символ не включается. Может быть NULL. В Windows Me/98/95 размер включает и нулевой символ. lpcMaxClassLen Указатель на переменную, которая получает размер самой длинной строки, которая определяет класс подраздела. Возвращаемый размер не включает нулевой байт. lpcValues Указатель на переменную, которая получает число параметров данного раздела.
Может быть - NULL. lpcMaxValueNameLen Указатель на переменную, получающую размер самого длинного названия параметра данного раздела. Размер не включает нулевой байт. Может быть - NULL. lpcMaxValueLen Указатель на переменную, получающую размер самого длинного значения среди тех, которые имеют параметры данного раздела. Может быть - NULL. lpcbSecurityDescriptor Указатель на переменную, которая получает размер дескриптора защиты раздела, в байтах. Может быть - NULL. lpftLastWriteTime Указатель на структуру FILETIME, которая получает время последней модификации раздела. Может быть - NULL.
Функция устанавливает члены структуры FILETIME в соответствии с временем последней модификации самого раздела, либо параметра, который был изменен позднее.
Для Windows Me/98/95 функция устанавливает члены структуры FILETIME в 0, т. к. система не поддерживает механизмов отслеживания модификации разделов реестра. Если функция выполнена успешно, возвращается ERROR_SUCCESS, в противном случае возвращается ненулевой код ошибки, определенный в Winerror.h
LONG RegQueryValueEx(HKEY hKey, LPCTSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData)
Функция возвращает информацию о параметре раздела и значение этого параметра.
hKey Описатель открытого раздела. Раздел должен быть открыт с правами KEY_QUERY_VALUE. lpValueName Указатель на С-строку, содержащую название параметра, о котором получается информация. Если параметр - NULL или пустая строка, то возвращается информация о параметре по умолчанию. lpReserved Зарезервирован - NULL. lpType Указатель на переменную, которая получает тип данных, сохраненных в параметре. Если равен NULL, то соответственно, информация не возвращается. lpData Указатель на массив, получающий данные параметра. Если параметр - NULL, то данные не возвращаются. Если данные - это строка, то функция проверяет наличие нулевого символа. lpcbData Указатель на переменную, которая определяет размер буфера, принимающего данные из параметра, в байтах.
После того, как функция вернет значение, эта переменная будет содержать размер данных, скопированных в буфер. Если данные носят текстовый характер (REG_xxx_SZ), то также включается и нулевой символ (нулевые символы для REG_MULTI_SZ). Если размер буфера, недостаточен для сохранения данных, то функция вернет ERROR_MORE_DATA и сохранит требуемый размер буфера в переменную, на которую указывает этот параметр. Если lpData - NULL, а параметр lpcbData не нулевой, функция возвращает ERROR_SUCCESS и сохраняет размер данных в переменной, на которую указывает lpcbData.
Если функция выполнена успешно, возвращается ERROR_SUCCESS, в противном случае возвращается ненулевой код ошибки, определенный в Winerror.h
Как видим, функция RegQueryValueEx предоставляет возможность программисту вначале проверить, каков размер данных в параметре, а затем уже его считать. Это позволяет выделять память динамически, по ходу выполнения программы. Например, можно написать следующий код, считывающий данные параметра, если они текстовые и выводит их на экран.
HKEY h; PBYTE pbBuff; DWORD cBuff=0; DWORD Type=0; //Откроем раздел if(RegOpenKeyEx(HKEY_CURRENT_USER,TEXT("asd"),0, KEY_QUERY_VALUE,&h)==ERROR_SUCCESS) { //Определим объем считываемых данных if(RegQueryValueEx(h,TEXT("set"),NULL,NULL,NULL, &cBuff)==ERROR_SUCCESS) { if(cBuff>1) { if((pbBuff=new BYTE [cBuff])==NULL) abort(); //Считываем информацию из параметра RegQueryValueEx(h,TEXT("set"),NULL,&Type,pbBuff,&cBuff); register int i; PBYTE tmpBuff; if((tmpBuff=new BYTE [cBuff])==NULL) abort(); switch(Type) { case(REG_SZ): cout<<"Type of REG_SZ, data: "<<pbBuff; break; case(REG_MULTI_SZ): cout<<"Type of REG_MULTI_SZ, data:\n\t"; for(i=0;i<cBuff-1;i++) pbBuff[i] ? cout<<pbBuff[i] : cout<<'\n'<<'\t'; break; case(REG_EXPAND_SZ): cout<<"Type of REG_EXPAND_SZ, data: "<<pbBuff<<endl; if(ExpandEnvironmentStrings((PCHAR)pbBuff, (PCHAR)tmpBuff,cBuff)!=0) cout<<tmpBuff; break; }
} else cout<<"Value is empty"<<endl; } else cerr<<"Error in query"<<endl; } else cerr<<"Error in open"<<endl;
Следующие две функции: RegEnumKeyEx и RegEnumValue используются для перечисления всех подразделов и параметров, указанного описателем раздела.
LONG RegEnumKeyEx(HKEY hKey, DWORD dwIndex, LPTSTR lpName, LPDWORD lpcName, LPDWORD lpReserved, LPTSTR lpClass, LPDWORD lpcClass, PFILETIME lpftLastWriteTime)
Функция перечисляет подразделы раздела, определяемого описателем.
hKey Описатель открытого раздела. Раздел должен быть открыт с правами KEY_ENUMERATE_SUB_KEYS. dwIndex Индекс подраздела. При первом вызове этот параметр должен быть равен нулю, а затем увеличиваться, для получения всех подразделов раздела. lpName Указатель на буфер, принимающий название подраздела. lpcName Указатель на переменную, которая определяет размер буфера, определенного параметром lpName, в TCHAR. Когда функция возвратит значение, переменная, на которую указывает указатель будет содержать кол-во символов, сохраненных в буфере, не включая нулевой символ. lpReserved Зарезервировано - NULL. lpClass Указатель на буфер, который получает класс строки с нулевым символом. Может быть - NULL. lpcClass Указатель на переменную, которая определяет размер буфера, определенного параметром lpClass, в TCHAR. Размер должен включать нулевой символ. После выполнения этот параметр содержит кол-во символов, сохраненных в буфере. Кол-во не включает нулевой байт. lpftLastWriteTime Указатель на переменную, которая получает время последней модификации раздела. Может быть - NULL.
Если функция выполнена успешно, то возвращается - ERROR_SUCCESS. В противном случае возвращается системный код ошибки, определенный в WinError.h. Приложение, вызывающее эту функцию должно увеличивать параметр dwIndex и вызывать функцию, пока она не возвратит значение - ERROR_NO_MORE_ITEMS.
LONG RegEnumValue(HKEY hKey, DWORD dwIndex, LPTSTR lpValueName, LPDWORD lpcValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData)
Функция перечисляет параметры раздела, определяемого описателем.
hKey Описатель открытого раздела. Раздел должен быть открыт с правами KEY_QUERY_VALUE. dwIndex Индекс параметра. При первом вызове этот параметр должен быть равен нулю, а затем увеличиваться, для получения всех параметров раздела. Поскольку значения не упорядочены, то функция может возвращать их в любом порядке. lpValueName Указатель на буфер, который получает название параметра, должен оканчиваться нулевым символом. lpcValueName Указатель на переменную, которая определяет размер буфера, на который указывает lpValueName, в TCHAR. Параметр должен включать завершающий нуль-символ. После возврата значения функцией, этот параметр будет содержать кол-во символов, записанных в буфер. Возвращаемое кол-во не включает нуль-символ. lpReserved Зарезервировано - NULL. lpType Указатель на переменную, которая содержит тип данных, сохраненных в параметре. Может быть - NULL. lpData Указатель на буфер, который получает данные параметра. Может быть - NULL. Если этот параметр - NULL, а lpcbData не NULL, функция сохраняет размер данных, в байтах, в переменной, на которую указывает lpcbData. lpcbData Указатель на переменную, определяющую размер буфера, на который указывает lpData, в байтах. Когда функция возвратит значение, переменная будет содержать кол-во байт, сохраненных в буфере. Может быть NULL, только если lpData - NULL. Если данные имеют тип REG_XXX_SZ, этот размер включает все завершающие нули.
Если буфер, указанный в lpData недостаточен для сохранения в нем данных, то функция возвращает ERROR_MORE_DATA и сохраняет требуемый размер буфера в переменной, на которую указывает lpcbData. Если функция выполнена успешно, то возвращается - ERROR_SUCCESS. В противном случае возвращает системный код ошибки, определенный в WinError.h.
LONG RegSetValueEx(HKEY hKey, LPCTSTR lpValueName, DWORD Reserved, DWORD dwType, const BYTE* lpData, DWORD cbData)
Функция устанавливает значение параметра.
hKey Описатель открытого раздела.
Раздел должен быть открыт с правами KEY_SET_VALUE. lpValueName Указатель на строку, которая содержит название параметра, значение которого нужно установить. Если параметра с таким именем не существует, функция создаст его. Если параметр равен NULL, или пустой строке, то устанавливается значение параметра по умолчанию. В Windows Me/98/95 каждый раздел имеет параметр по умолчанию, который первоначально не содержит данных. В Windows 95 по умолчанию тип этого параметра - REG_SZ. Reserved Зарезервировано - NULL. dwType Тип данных параметра. lpData Указатель на буфер, содержащий данные, которые должны быть сохранены в соответствующем параметре. Для строк, таких как REG_SZ, строка должна заканчиваться нулевым символом. Для типа REG_MULTI_SZ строка должна заканчиваться двумя нулевыми символами. cbData Размер буфера lpData, в байтах. Если данные имеют тип REG_SZ, REG_MULTI_SZ или REG_EXPAND_SZ, то параметр должен учитывать нулевой или нулевые символы.
Если функция выполнена успешно, возвращается ERROR_SUCCESS, в противном случае возвращается ненулевой код ошибки, определенный в Winerror.h