понедельник, 13 июля 2009 г.

работаем с Windows Registry

Для восстановления веток реестра в Windows существует функция RegRestoreKey. До Windows Vista ей можно было свободно пользоваться для восстановления, например, настроек по умолчанию программы. В Windows Vista это стало требовать поднятия уровня процесса(elevation) с появлением назойливого диалога UAC. Конечно, понятно, что большинство читающих эту статью живут с отключенным UAC, но подавляющее большинство обычных пользователей(по данным Microsoft) UAC не выключают. У RegRestoreKey есть ещё один недостаток — она работает с файлами в бинарном формате, который отличается для каждой новой версии ОС.

Однако, существует в Windows и программа reg.exe, которая имеет встроенный парсер REG-фалов. Да, именно тех, которые описывают ключи в обычном тектовом формате. Команды import и export этой утилиты не требуют каких либо повышенных прав. Конечно, здесь речь идет о разделах HKCU. Почему эти функции не выделили в отдельный API остается загадкой.

Ниже приведен пример использования этой утилиты в программе:
// Получаем указатель на REG файл прилинковынный в ресурсы
HRSRC hRsrc = ::FindResource( NULL, MAKEINTRESOURCE(IDR_DEFSETTINGS), L"REG" ); // IDR_DEFSETTINGS - идентификатор ресурса с файлом. Может быть другим.
HGLOBAL hGlobal = ::LoadResource( NULL, hRsrc );
LPVOID lpReg = ::LockResource( hGlobal );

// Сохраняем во временный файл
wchar_t path_buf[MAX_PATH];
SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, &path_buf[0] );
const wstring temp_filename = path_buf+L"\\temp_reg.reg";
ofstream os( temp_filename.c_str(), ios_base::binary );
os.write( static_cast<char*>(lpReg), ::SizeofResource( NULL, hRsrc ) );
os.close();

// Готовим параметры reg.exe.
wstring params( L"import \"");
params += temp_filename;
params += L"\""; // Путь к файлу должен быть в кавычках на случай пробелов

// Запускаем reg.exe
SHELLEXECUTEINFO se = {0};
se.cbSize = sizeof SHELLEXECUTEINFO;
se.fMask = SEE_MASK_NOCLOSEPROCESS|SEE_MASK_UNICODE;
se.hwnd = GetSafeHwnd();
se.lpFile = L"reg.exe"; // находится в system32, поэтому путь указывать необязательно
se.lpParameters = params.c_str();
se.nShow = SW_HIDE; // Смотреть на консольку пользователю ни к чему
if ( ShellExecuteEx( &se ) ) {
WaitForSingleObject( se.hProcess, INFINITE ); // хотя тут и написано INFINITE, но в реальной жизни лучше указать таймаут
CloseHandle( se.hProcess );
} else {
// Ошибка запуска. Как-то обрабатываем эту ситуацию.
}

// Удаляем временный файл
_wremove( temp_filename.c_str() );

Комментировать в ВКонтакте