Для работы с реестром Windows можно воспользоваться программой regedit
. Но может потребоваться реализовать более специализированный инструмент. Одним из возможных подходов является создание Qt-приложения, основанного на QSettings
. Этим мы и займемся.
В первую очередь обеспечим возможность просмотра содержимого реестра:
Далее обеспечим возможность добавления новых значений в открытую ветку (с помощью кнопки Создать
):
Наше приложение действительно сможет влиять на систему (поэтому будьте осторожны при тестировании примера):
Также мы сможем удалять записи, которые нам не нужны (по нажатию кнопки Удалить
):
Предупреждение: приложение является демонстрационным, поэтому для упрощения реализации часть ошибок заведомо не обрабатывается и игнорируется. Например, кнопки Создать
и Удалить
действуют всегда, поэтому следите, чтобы состояние интерфейса отвечало логике действия (для создания - выделена ветка реестра, а для удаления - выбрано какое-нибудь значение), иначе в худшем случае это может привести к проблемам в работе системы.
Заголовочный файл mainwidget.h
:
#ifndef MAINWIDGET_H
#define MAINWIDGET_H
#include <QWidget>
#include <QSet>
class QTreeWidgetItem;
class QSettings;
namespace Ui {
class MainWidget;
}
class MainWidget : public QWidget {
Q_OBJECT
public:
explicit MainWidget( QWidget* parent = 0 );
~MainWidget();
private slots:
void onRegistryItemExpanded( QTreeWidgetItem* item );
void onRegistryItemClicked( QTreeWidgetItem* item, int column );
void onCreate();
void onRemove();
private:
void addRegistryGroup( QTreeWidgetItem* root, QSettings* settings );
QString findPathForItem( QTreeWidgetItem* item ) const;
private:
Ui::MainWidget *ui;
};
#endif // MAINWIDGET_H
Файл реализации mainwidget.cpp
:
#include "mainwidget.h"
#include "ui_mainwidget.h"
#include "registryitemcreationdialog.h"
#include <QSettings>
#include <QDebug>
#include <QTreeWidgetItem>
#include <algorithm>
static const auto REG_PATH_SEPARATOR = "\\";
MainWidget::MainWidget( QWidget* parent ) :
QWidget( parent ),
ui( new Ui::MainWidget ) {
ui->setupUi( this );
auto* root = new QTreeWidgetItem( QStringList() << "Computer" );
ui->treeWidget->addTopLevelItem( root );
const auto REGISTRY_GROUPS = {
"HKEY_CLASSES_ROOT",
"HKEY_CURRENT_USER",
"HKEY_LOCAL_MACHINE",
"HKEY_USERS"
};
for( const auto& g : REGISTRY_GROUPS ) {
QSettings settings( g, QSettings::NativeFormat );
auto* item = new QTreeWidgetItem( QStringList() << g );
root->addChild( item );
addRegistryGroup( item, &settings );
}
ui->treeWidget->expandItem( root );
connect(
ui->treeWidget,
SIGNAL( itemExpanded( QTreeWidgetItem* ) ),
SLOT( onRegistryItemExpanded( QTreeWidgetItem* ) )
);
connect(
ui->treeWidget,
SIGNAL( itemClicked( QTreeWidgetItem*, int ) ),
SLOT( onRegistryItemClicked( QTreeWidgetItem*, int ) )
);
connect( ui->bnCreate, SIGNAL( clicked( bool ) ), SLOT( onCreate() ) );
connect( ui->bnRemove, SIGNAL( clicked( bool ) ), SLOT( onRemove() ) );
}
MainWidget::~MainWidget() {
delete ui;
}
void MainWidget::onRegistryItemExpanded( QTreeWidgetItem* item ) {
QSettings settings( findPathForItem( item ), QSettings::NativeFormat );
addRegistryGroup( item, &settings );
}
void MainWidget::onRegistryItemClicked( QTreeWidgetItem* item, int column ) {
Q_UNUSED( column )
if( item && item->parent() ) {
QSettings settings( findPathForItem( item ), QSettings::NativeFormat );
ui->tableWidget->setRowCount( 0 );
ui->tableWidget->clearContents();
for( const auto& key : settings.childKeys() ) {
auto row = ui->tableWidget->rowCount();
ui->tableWidget->insertRow( row );
auto item = new QTableWidgetItem( key );
item->setFlags( item->flags() & ~Qt::ItemIsEditable );
ui->tableWidget->setItem( row, 0, item );
auto val = settings.value( key ).toString();
item = new QTableWidgetItem( val );
item->setFlags( item->flags() & ~Qt::ItemIsEditable );
ui->tableWidget->setItem( row, 1, item );
}
}
}
void MainWidget::onCreate() {
RegistryItemCreationDialog dlg( this );
if( dlg.exec() == QDialog::Accepted ) {
auto item = ui->treeWidget->currentItem();
QSettings settings( findPathForItem( item ), QSettings::NativeFormat );
settings.setValue( dlg.getName(), dlg.getValue() );
settings.sync();
onRegistryItemClicked( item, 0 );
}
}
void MainWidget::onRemove() {
auto item = ui->treeWidget->currentItem();
auto path = findPathForItem( item );
QSettings settings( findPathForItem( item ), QSettings::NativeFormat );
settings.remove( ui->tableWidget->item( ui->tableWidget->currentRow(), 0 )->text() );
settings.sync();
onRegistryItemClicked( item, 0 );
}
void MainWidget::addRegistryGroup( QTreeWidgetItem* root, QSettings* settings ) {
static int depth = 0;
static const int MAX_DEPTH = 2;
++depth;
if( 0 < root->childCount() ) {
for( int i = 0; i < root->childCount(); ++i ) {
auto child = root->child( i );
if( 0 < child->childCount() ) {
break;
}
settings->beginGroup( child->text( 0 ) );
addRegistryGroup( child, settings );
settings->endGroup();
}
} else if( depth <= MAX_DEPTH ) {
for( const auto& g : settings->childGroups() ) {
auto* item = new QTreeWidgetItem( QStringList() << g );
root->addChild( item );
settings->beginGroup( g );
addRegistryGroup( item, settings );
settings->endGroup();
}
}
--depth;
}
QString MainWidget::findPathForItem( QTreeWidgetItem* item ) const {
QStringList reversePath;
for( ; item != nullptr; item = item->parent() ) {
reversePath << item->text( 0 );
}
reversePath.pop_back();
std::reverse( reversePath.begin(), reversePath.end() );
return reversePath.join( REG_PATH_SEPARATOR );
}
Просмотр реестра мы основываем на классе QSettings
с его функциями-членами для доступа к группам (childGroups()
) и к ключам для заданной группы (childKeys()
). При этом QSettings
инициализируется следующим образом:
QSettings settings( groupName, QSettings::NativeFormat ); // где groupName, например, - "HKEY_CURRENT_USER"
Поскольку реестр содержит очень много записей, мы не можем загрузить и отображать его целиком. Поэтому приходится идти на небольшую хитрость - загружаем древовидную структуру реестра не больше, чем на один уровень ниже видимого:
void MainWidget::addRegistryGroup( QTreeWidgetItem* root, QSettings* settings ) {
static int depth = 0;
static const int MAX_DEPTH = 2;
++depth;
if( 0 < root->childCount() ) {
for( int i = 0; i < root->childCount(); ++i ) {
auto child = root->child( i );
if( 0 < child->childCount() ) {
break;
}
settings->beginGroup( child->text( 0 ) );
addRegistryGroup( child, settings );
settings->endGroup();
}
} else if( depth <= MAX_DEPTH ) {
for( const auto& g : settings->childGroups() ) {
auto* item = new QTreeWidgetItem( QStringList() << g );
root->addChild( item );
settings->beginGroup( g );
addRegistryGroup( item, settings );
settings->endGroup();
}
}
--depth;
}
При этом нам приходится обрабатывать сигнал разворачивания элемента дерева:
void MainWidget::onRegistryItemExpanded( QTreeWidgetItem* item ) {
QSettings settings( findPathForItem( item ), QSettings::NativeFormat );
addRegistryGroup( item, &settings );
}
Поиск пути в реестре для некоторого элемента дерева происходит с помощью вспомогательной функции:
QString MainWidget::findPathForItem( QTreeWidgetItem* item ) const {
QStringList reversePath;
for( ; item != nullptr; item = item->parent() ) {
reversePath << item->text( 0 );
}
reversePath.pop_back();
std::reverse( reversePath.begin(), reversePath.end() );
return reversePath.join( REG_PATH_SEPARATOR );
}
Обратите внимание, что вложенные элементы в пути реестра разделяются символами "\\"
(например, HKEY_CURRENT_USER\\Environment
).
Чтение значений происходит при щелчке на элементе в дереве:
void MainWidget::onRegistryItemClicked( QTreeWidgetItem* item, int column ) {
Q_UNUSED( column )
if( item && item->parent() ) {
QSettings settings( findPathForItem( item ), QSettings::NativeFormat );
ui->tableWidget->setRowCount( 0 );
ui->tableWidget->clearContents();
for( const auto& key : settings.childKeys() ) {
auto row = ui->tableWidget->rowCount();
ui->tableWidget->insertRow( row );
auto item = new QTableWidgetItem( key );
item->setFlags( item->flags() & ~Qt::ItemIsEditable );
ui->tableWidget->setItem( row, 0, item );
auto val = settings.value( key ).toString();
item = new QTableWidgetItem( val );
item->setFlags( item->flags() & ~Qt::ItemIsEditable );
ui->tableWidget->setItem( row, 1, item );
}
}
}
Обратите внимание, что для упрощения мы считываем все значения без учета их типа, как текст. Это может привести к появлению загадочных надписей в интерфейсе программы на неизвестных человечеству языках. Именно по этой причине я не добавил функцию редактирования значений в нашу программу.
Создание новой записи сводится к добавлению значения в текущую ветку реестра:
void MainWidget::onCreate() {
RegistryItemCreationDialog dlg( this );
if( dlg.exec() == QDialog::Accepted ) {
auto item = ui->treeWidget->currentItem();
QSettings settings( findPathForItem( item ), QSettings::NativeFormat );
settings.setValue( dlg.getName(), dlg.getValue() );
settings.sync();
onRegistryItemClicked( item, 0 );
}
}
Запрос данных у пользователя осуществляем с помощью модального диалогового окна, описание которого здесь мы опускаем.
Удаление значения из реестра сводится реализуется не сложнее, чем добавление:
void MainWidget::onRemove() {
auto item = ui->treeWidget->currentItem();
auto path = findPathForItem( item );
QSettings settings( findPathForItem( item ), QSettings::NativeFormat );
settings.remove( ui->tableWidget->item( ui->tableWidget->currentRow(), 0 )->text() );
settings.sync();
onRegistryItemClicked( item, 0 );
}
Скачать пример работы с реестром Windows с помощью QSettings