目录
- 1 注册表(Registry)介绍
- 1.1 注册表简介
- 1.2 注册表位置
- 1.3 开启/禁用 注册表编辑器
- 1.4 注册表的结构
- 1.5 修改注册表实例
- 2 程序中对注册表的读写操作
- 2.1 打开和关闭注册表
- 2.2 创建和删除指定的注册表键
- 2.3 读取和设置指定注册表中某个键值
- 2.4 增加和删除注册表键中某个键值
- 2.5 迭代一个键下的所有子键
- 3 注册表读写的一个Demo
本文介绍了Windows注册表的基本知识,以及C++中打开\关闭\查询\修改注册表的常用接口。
注册表的基本知识(本文第1节)参考 https://blog.csdn.net/weixin_45300266/article/details/122359920并作修改。
1 注册表(Registry)介绍
1.1 注册表简介
注册表是windows系统中具有层次结构的核心数据库,储存的数据对windows 和Windows上运行的应用程序和服务至关重要。注册表时帮助windows控制硬件、软件、用户环境和windows界面的一套数据文件。
1.2 注册表位置
windows 注册表的位置:C:\Windows\System32\config 。
以上红框中标注的就是注册表文件了,但是不能直接打开,只能使用注册表编辑器对这些文件进行操作。切记切记,不要轻易尝试删除注册表文件。
1.3 开启/禁用 注册表编辑器
首先,打开注册表,方法为:WIN+R 输入—> “regedit”或“regedit.exe”、“regedt32”或“regedt32.exe” —>回车
如果以上方法均不能打开数据库,那么说明你没有管理员权限,或者注册表被锁定。
权限问题可联系管理员解决,若是注册表被锁定可以使用以下方法进行解锁
1、创建一个文本文件,复制以下文字文本内容(注意开头之后第二行一定要是空行并且不可少),选择另存为,文件类型选择所有文件,文件名称为XX.reg,保存到桌面后,双击打开该文件,点击确定便可。
REGEDIT4
[HKEY_USERS.DEFAULT\Software\Microsoft\Windows\CurrentVersion\Policies\system]
“DisableRegistryTools”=dword:00000000
2、锁定注册表编辑器的方法
创建一个文本文件,复制以下文字文本内容(注意开头之后第二行一定要是空行并且不可少),选择另存为,文件类型选择所有文件,文件名称为SS.reg,保存到桌面后,双击打开该文件,点击确定便可。
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\System]
“DisableRegistryTools”=dword:00000001
1.4 注册表的结构
注册表中,所有的数据都是通过一种树状结构以键和子键的方式组织起来的,就像磁盘文件系统的目录结构一样。
每个键包含一组特定的信息,每个键的键名都是和它所包含的信息相关联的。注册表的根键共有5个,且全为大写。
键值:
键值由三部分组成: 名称、类型、数据。
键值的类型:
键值类型由常用的6种组成
字符串值(REG_SZ)
二进制值(REG_BINARY)
32位值(4个字节)(REG_DWORD)
64位值(5个字节)(REG_QWORD)
多字符串值(REG_MULTI_SZ)
可扩充字符串值(REG_EXPAND_SZ)
1.5 修改注册表实例
假定我们需要设置一个程序为开机自启动,这可以通过修改注册表实现。
实现原理:windows 提供了专门的开机自启动注册表。每次开启时,它都会在这个注册表键下遍历键值,获取到键值中的程序路径,并创建进程启动程序。因此只需要将需要设置自启动的程序的路径添加到这个注册表中,便可以实现程序开启自启动功能。
常见的开机自启动注册表路径:
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run
注意:要修改 HKEY_LOCAL_MACHINE 主键的注册表需要管理员权限
现在我们尝试将计算器添加到开机自启动注册表,实现开机自启。
首先,打开注册表编辑器,并复制上述路径,回车后,跳到注册表自启动位置。
然后,新建 键值类型为 REG_SZ的键值,修改名称为 calcNew
最后,选中“calcNew”键值右键修改键值数据为:calc.exe的绝对路径,并点击确定。
启动系统,则可以看到计算器程序已经成功自启动。
2 程序中对注册表的读写操作
2.1 打开和关闭注册表
1.打开注册表键
LONG WINAPI RegOpenKeyEx(
__in HKEY hKey, //主键的名称
__in_opt LPCTSTR lpSubKey,//子键的名称
__reserved DWORD ulOptions, //保留,为0
__in REGSAM samDesired,//指定对键的访问权限
__out PHKEY phkResult //指定打开键的句柄
);
请注意,hKeyParent是一个打开的键(它充当即将打开的子健的父键),或者可以是以下值;
HKEY_CLASSES_ROOT
HKEY_CURRENT_CONFIG
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE
HKEY_USERS
lpSubKey是需要打开的子键的名称。
2.关闭注册表
LONG WINAPI RegCloseKey(
__in HKEY hKey
);
以上接口的使用可以查看本文第3部分。
2.2 创建和删除指定的注册表键
- 创建注册表键
LONG WINAPI RegCreateKeyEx(
HKEY hKey, //主键的名称
LPCTSTR lpSubKey, //子键的名称
DWORD Reserved, //保留,为0
LPTSTR lpClass, //指定此键的类(对象类型), 如果键已经存在,则忽略此参数
DWORD dwOptions, //指定键的特殊选项
REGSAM samDesired, //指定对键的访问权限
LPSECURITY_ATTRIBUTES lpSecurityAttributes, //可设为NULL
PHKEY phkResult, //句柄
LPDWORD lpdwDisposition //可设为NULL
- 删除某个注册表键
LONG WINAPI RegDeleteKeyEx(
__in HKEY hKey,
__in LPCTSTR lpSubKey,
__in REGSAM samDesired,
__reserved DWORD Reserved);
2.3 读取和设置指定注册表中某个键值
- 读取某个注册表键值
LONG RegQueryValueEx(
HKEY hKey, // 句柄
LPCTSTR lpValueName, // 要查询注册表键值的名字字符串
LPDWORD lpReserved, // 设为0
LPDWORD lpType, // 用于装载取回数据类型的一个变量
LPBYTE lpData, // 用于装载指定值的一个缓冲区
LPDWORD lpcbData // 用于装载lpData缓冲区长度的一个变量
- 在注册表某个键下设置指定键值
LONG RegSetValueEx(
HKEY hKey, // 句柄
LPCTSTR lpValueName, // 指向一个字符串的指针,该字符串包含要设置的值的名称
DWORD Reserved, // 保留,设为0
DWORD dwType, // 指定数据类型(字符串:REG_SZ, 数字:REG_DWORD)
CONST BYTE *lpData, // 指向一个缓冲区,该缓冲区包含了欲为指定值名称存储的数据
DWORD cbData // 指定由lpData参数所指向的数据的大小,单位是字节。
2.4 增加和删除注册表键中某个键值
- 增加键下某个键值
这个接口就是上面讲的RegSetValueEx,它既可以设置,也可以新建一个键值。 - 删除键下某个键值
LONG WINAPI RegDeleteValue(
__in HKEY hKey,
__in_opt LPCTSTR lpValueName
);
2.5 迭代一个键下的所有子键
迭代一个注册表键下所有子键,需要用到RegQueryInfoKey、RegEnumKeyEx和RegEnumValue。
RegEnumKey可以迭代一个打开键下的所有子键。
LONG WINAPI RegEnumKey(
__in HKEY hKey,
__in DWORD dwIndex,
__out LPTSTR lpName,
__in DWORD cchName
);
RegEnumValue可以迭代一个指定键下所有的键值。
LONG WINAPI RegEnumValue(
__in HKEY hKey,
__in DWORD dwIndex,
__out LPTSTR lpValueName,
__inout LPDWORD lpcchValueName,
__reserved LPDWORD lpReserved,
__out_opt LPDWORD lpType,
__out_opt LPBYTE lpData,
__inout_opt LPDWORD lpcbData
);
下面一个接口,可以取出一个键下所有的子键,以及这个键的所有键值名称。
//获取一个注册表键下面的各个子键名称,以及该键下面所有值名称,根据MSDN实例修改
//parSubKey存储子键名称,parKeyValueName存储键下所有键值名称
void QueryKey(HKEY hKey,std::vector<CString>* parSubKey,std::vector<CString>* parKeyValueName)
{
TCHAR pszSubKeyName[MAX_KEY_LENGTH]; // buffer for subkey name
DWORD cbName; // size of name string
TCHAR pszClass[MAX_PATH] = TEXT(""); // buffer for class name
DWORD cchClassName = MAX_PATH; // size of class string
DWORD cSubKeys = 0; // number of subkeys
DWORD cbMaxSubKey; // longest subkey size
DWORD cchMaxClass; // longest class string
DWORD cValues; // number of values for key
DWORD cchMaxValue; // longest value name
DWORD cbMaxValueData; // longest value data
DWORD cbSecurityDescriptor; // size of security descriptor
FILETIME ftLastWriteTime; // last write time
DWORD i, retCode;
TCHAR pszValue[MAX_VALUE_NAME];
DWORD cchValue = MAX_VALUE_NAME;
// Get the class name and the value count.
retCode = RegQueryInfoKey(
hKey, // key handle
pszClass, // buffer for class name
&cchClassName, // size of class string
NULL, // reserved
&cSubKeys, // number of subkeys
&cbMaxSubKey, // longest subkey size
&cchMaxClass, // longest class string
&cValues, // number of values for this key
&cchMaxValue, // longest value name
&cbMaxValueData, // longest value data
&cbSecurityDescriptor, // security descriptor
&ftLastWriteTime); // last write time
// Enumerate the subkeys, until RegEnumKeyEx fails.
if (parSubKey)
{
for (i = 0; i < cSubKeys; i++)
{
cbName = MAX_KEY_LENGTH;
retCode = RegEnumKeyEx(hKey, i, pszSubKeyName, &cbName,
NULL, NULL, NULL, &ftLastWriteTime);
if (retCode == ERROR_SUCCESS)
parSubKey->push_back(pszSubKeyName);
}
}
// Enumerate the key values.
if (parKeyValueName)
{
for (i = 0, retCode = ERROR_SUCCESS; i < cValues; i++)
{
cchValue = MAX_VALUE_NAME;
pszValue[0] = _T('\0');
retCode = RegEnumValue(hKey, i, pszValue, &cchValue,
NULL, NULL, NULL, NULL);
if (retCode == ERROR_SUCCESS)
parKeyValueName->push_back(pszValue);
}
}
}
3 注册表读写的一个Demo
这个实例是读取注册表中自启动项,然后增加和删除其中的键值,从而改变自启动的程序。
新建一个基于对话框的MFC工程,名称为SetStartUp,在资源编辑器中增加1个List control和3个按钮,按钮分别对应查询启动项、删除启动项和增加启动项。
在对话框类的头文件中增加
private:
void FillList(); //填充
CListCtrl m_wndLst;
static CString m_sPath; //启动项的键路径
std::vector<CString> m_arsStartItemPath; //保持键值的名称
std::vector<CString> m_arsStart; //保持键值的数值
实现文件中增加下列内容。
首先,静态CString变量m_sPath存储注册表自启动键的路径,它的根键在 HKEY_CURRENT_USER中。
#define MAX_KEY_LENGTH 255
#define MAX_VALUE_NAME 16383
CString CSetStartUpDlg::m_sPath = _T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run");
在OnInitDialog中增加初始化列表控件的代码。
BOOL CSetStartUpDlg::OnInitDialog()
{
//.......省略
// TODO: 在此添加额外的初始化代码
m_wndLst.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
CRect rect;
m_wndLst.GetClientRect(rect);
m_wndLst.InsertColumn(0, _T("程序名"), LVCFMT_LEFT, rect.Width() / 3.0, 0);
m_wndLst.InsertColumn(1, _T("安装路径"), LVCFMT_LEFT, rect.Width()*2.0 / 3.0, 1);
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
填充list control的函数
void CSetStartUpDlg::FillList()
{
CString sAppName;
m_wndLst.DeleteAllItems();
for (int i = 0; i < m_arsStartItemPath.size(); i++)
{
m_wndLst.InsertItem(i, m_arsStart[i]);
m_wndLst.SetItemText(i, 1, m_arsStartItemPath[i]);
}
}
OnBnClickedBtnShow为显示自启动项的按钮响应函数,注意这里面的对键值的迭代操作(采用函数RegEnumValue)。请特别注意RegOpenKeyEx函数第四个实参需要包括KEY_WOW64_64KEY,因为我Windows系统是64位,这样,无论程序是32位程序还是64位程序,都可以打开64位的注册表视图;没有这个掩码的话,32位的程序只能访问32位的注册表视图,64位的只能访问64位的注册表视图。在其后所有涉及打开注册表的接口中,都应该使用这个掩码位。
void CSetStartUpDlg::OnBnClickedBtnShow()
{
// TODO: 在此添加控件通知处理程序代码
m_arsStartItemPath.clear();
m_arsStart.clear();
HKEY hRegKey;
if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_CURRENT_USER, m_sPath, NULL, KEY_READ | KEY_WRITE | KEY_WOW64_64KEY, &hRegKey))
return;
#define _MAX_VALUE_BUF_ 2048
TCHAR pszValueName[MAX_VALUE_NAME];
DWORD cchValueName = MAX_VALUE_NAME;
BYTE pszValue[_MAX_VALUE_BUF_] = {0};
DWORD dwBufSize = _MAX_VALUE_BUF_;
int i = 0;
long lRetCode;
DWORD dwType;
while (ERROR_NO_MORE_ITEMS !=
(lRetCode = RegEnumValue(hRegKey, i++, pszValueName, &cchValueName, NULL, &dwType, pszValue, &dwBufSize)))
{
if (ERROR_SUCCESS == lRetCode)
{
if (dwType == REG_SZ)
{
pszValue[dwBufSize] = '\0';
m_arsStartItemPath.push_back((TCHAR*)pszValue);
m_arsStart.push_back(pszValueName);
}
}
dwBufSize = _MAX_VALUE_BUF_;
cchValueName = MAX_VALUE_NAME;
}
RegCloseKey(hRegKey);
FillList();
}
下面接口为按下“删除启动项”按钮的响应函数,注意这里面的删除键值操作。
void CSetStartUpDlg::OnBnClickedBtnDelItem()
{
// TODO: 在此添加控件通知处理程序代码
HKEY hRegKey;
if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_CURRENT_USER, m_sPath, NULL, KEY_READ | KEY_WRITE | KEY_WOW64_64KEY, &hRegKey))
return;
int iSelect = m_wndLst.GetNextItem(-1, LVIS_SELECTED);
if (-1 != iSelect)
{
CString sKeyName = m_wndLst.GetItemText(iSelect, 0);
m_wndLst.DeleteItem(iSelect);
RegDeleteValue(hRegKey, sKeyName);
}
RegCloseKey(hRegKey);
}
下面接口为单击“新增启动项”响应函数,提示用户选择一个*.exe文件,然后把它放入启动项键下。
void CSetStartUpDlg::OnBnClickedBtnAddStartup()
{
// szFilters is a text string that includes two file name filters:
TCHAR szFilters[] = _T("可执行文件 (*.exe)|*.exe|All Files (*.*)|*.*||");
CFileDialog fileDlg(TRUE, NULL, NULL, OFN_FILEMUSTEXIST | OFN_HIDEREADONLY, szFilters);
if (fileDlg.DoModal() == IDOK)
{
CString sPath = fileDlg.GetPathName();
//将用户选择的文件放到启动项中
HKEY hRegKey;
if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_CURRENT_USER, m_sPath, NULL, KEY_READ | KEY_WRITE | KEY_WOW64_64KEY, &hRegKey))
return;
CString sAppName = sPath.Mid(sPath.ReverseFind(_T('\\')) + 1);
if (ERROR_SUCCESS == RegSetValueEx(hRegKey, sAppName, NULL, REG_SZ,
(const BYTE*)sPath.GetString(), (sPath.GetLength() + 1) * sizeof(TCHAR)))
{
int iNew = m_wndLst.InsertItem(m_wndLst.GetItemCount(), sAppName);
m_wndLst.SetItemText(iNew, 1, sPath);
}
RegCloseKey(hRegKey);
}
}
通过增加以上内容,即可完成这个小工具的制作!