Windows注册表的读写操作


本文介绍了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 创建和删除指定的注册表键

  1. 创建注册表键

​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

  1. 删除某个注册表键

LONG WINAPI RegDeleteKeyEx(
__in HKEY hKey,
__in LPCTSTR lpSubKey,
__in REGSAM samDesired,
__reserved DWORD Reserved);

2.3 读取和设置指定注册表中某个键值

  1. 读取某个注册表键值

LONG RegQueryValueEx(
HKEY hKey, // 句柄
LPCTSTR lpValueName, // 要查询注册表键值的名字字符串
LPDWORD lpReserved, // 设为0
LPDWORD lpType, // 用于装载取回数据类型的一个变量
LPBYTE lpData, // 用于装载指定值的一个缓冲区
LPDWORD lpcbData // 用于装载lpData缓冲区长度的一个变量

  1. 在注册表某个键下设置指定键值

LONG RegSetValueEx(
HKEY hKey, // 句柄
LPCTSTR lpValueName, // 指向一个字符串的指针,该字符串包含要设置的值的名称
DWORD Reserved, // 保留,设为0
DWORD dwType, // 指定数据类型(字符串:REG_SZ, 数字:REG_DWORD)
CONST BYTE *lpData, // 指向一个缓冲区,该缓冲区包含了欲为指定值名称存储的数据
DWORD cbData // 指定由lpData参数所指向的数据的大小,单位是字节。

2.4 增加和删除注册表键中某个键值

  1. 增加键下某个键值
    这个接口就是上面讲的RegSetValueEx,它既可以设置,也可以新建一个键值。
  2. 删除键下某个键值

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);
	}
}

通过增加以上内容,即可完成这个小工具的制作!

  • 1
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
C 注册表读写类是一种用于管理和操作Windows操作系统注册表的类。注册表Windows操作系统的核心组件之一,用于存储系统配置和应用程序设置的信息。 C 注册表读写类提供了一组方法和属性,用于读取、写入、修改和删除注册表中的键值。通过使用该类,可以轻松地访问和管理注册表中的数据。该类提供了以下常用的方法: 1. OpenKey:打开一个指定路径的注册表键。 2. CreateKey:创建一个指定路径的注册表键。 3. GetValue:获取指定路径下的键值。 4. SetValue:设置指定路径下的键值。 5. DeleteKey:删除指定路径下的注册表键。 6. DeleteValue:删除指定路径下的键值。 使用C 注册表读写类,可以方便地对注册表进行读写操作,从而实现对应用程序设置和系统配置的管理。比如,可以使用该类来读取和修改系统的默认浏览器设置、时间和日期格式、桌面背景等。同时,也可以使用该类来读取和修改应用程序的配置信息,如保存上一次打开的文件路径、用户界面偏好设置等。 需要注意的是,对注册表读写操作需要管理员权限,因为注册表是系统关键信息的存储位置。在使用该类时,需要确保程序运行在具有管理员权限的账户下,否则可能会导致访问被拒绝的错误。 总之,C 注册表读写类是一个很实用的工具类,可以帮助开发人员轻松地读取和写入注册表中的数据,实现对系统和应用程序设置的管理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_Santiago

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值