注册表是Windows操作系统中一个重要的数据库,其中保存着操作系统和各种软件的重要信息。注册表是如此的重要,所以也是病毒和反病毒软件的兵家必争之地。
注册表是Windows系统管理和维护的配置较为复杂的信息数据库,它以树状形式存储信息。可以通过“开始”->“运行”,在运行窗口中输入“regedit”,来打开Windows提供的“注册表编辑器”窗口。如下图所示:
注册表由若干部分组成,分别是根键、子键、键值项。
根键:类似于磁盘驱动器的名称,在树状结构中类似于树的根节点。如上图中1标识框出的部分,根键的父节点是“计算机”。
子键:类似于文件夹,一个根键下可以包含多个子键,子键下也可以包含多个子键。如上图标识2、3所框出的部分。
键值项:不包括子键的子键就是键值项。如上图标识4所框出的部分。
无论是根键、子键还是键值项,都是注册表中的结构,具体的数据就存储在各个结构的相应位置。注册表的数据由3部分组成:名称、类型和值。如上图右边框出的部分。
好了,基本的注册表知识介绍完了,下面介绍操作注册表的常用API。
打开注册表:
LONG RegOpenKeyEx( HKEY hKey,//指定一个父键句柄 LPCTSTR lpSubKey,//指向一个字符串,用来表示要打开的子键名称 DWORD ulOptions,//系统保留,必须指定为0 REGSAM samDesired,//打开注册表的存取权限,为了方便对注册表的操作,通常使用KEY_ALL_ACCESS PHKEY phkResult//指向一个双字变量,用来接收打开的子键句柄 );
如果函数执行成功,则返回ERROR_SUCCESS,并且在phResult中保存打开后返回的子键句柄。所谓打开注册表,实质就是打开注册表的某个子键,然后进行操作。
关闭注册表:
LONG RegCloseKey( HKEY hKey//要关闭的注册表句柄 );
创建一个子键:
LONG RegCreateKeyEx( HKEY hKey,//指定父键句柄 LPCTSTR lpSubKey,//指向一个字符串,用来表示要创建的子键名称 DWORD Reserved,//系统保留,必须指定为0 LPTSTR lpClass,//子键类型名,一般设置为NULL DWORD dwOptions,//创建子键时的选项,通常使用REG_OPTION_NON_VOLATILE宏,表示子键被创建到注册表中而不是内存中 REGSAM samDesired,//打开注册表的存取权限,为了方便对注册表的操作,通常使用KEY_ALL_ACCESS LPSECURITY_ATTRIBUTES lpSecurityAttributes,//指向一个SECURITY_ATTRIBUTES结构体,用来指定注册表句柄的安全属性,一般设置为NULL PHKEY phkResult,//指向一个双字变量,用来保存打开的子键句柄 LPDWORD lpdwDisposition//一般设置为NULL );
如果函数执行成功,则返回ERROR_SUCCESS,并在phkResult中保存返回创建子键的句柄。当需要创建的子键已经存在的时候,该函数起到与RegOpenKeyEx函数同样的效果。
删除子键:
LONG RegDeleteKey( HKEY hKey,//父键句柄 LPCTSTR lpSubKey//要删除的子键名称字符串 );
需要注意的是,该函数只能删除键值项,即删除最下层的子键。
读取键名称的数据或查询键名称的属性:
LONG RegQueryValueEx( HKEY hKey,//用来指定要读取的键值项所处的子键句柄 LPCTSTR lpValueName,//用来指定要读取的键值项的名称 LPDWORD lpReserved,//保留参数,必须为NULL LPDWORD lpType,//接收返回的键值类型,如果不需要返回键值项类型,可以给NULL值 LPBYTE lpData,//指向一个缓冲区,用来接收返回的键值数据 LPDWORD lpcbData//调用函数时,该参数指定缓冲区的长度;函数返回时,该参数保存缓冲区实际接收到的长度。 );
写入键值项的函数:
LONG RegSetValueEx( HKEY hKey,//用来指定要写入的键值项所处的子键句柄 LPCTSTR lpValueName,//指定定义键值项名称的字符串 DWORD Reserved,//保留参数,必须为0 DWORD dwType,//指出要写入的键值数据的类型 const BYTE* lpData,//指向要写入键值数据的缓冲区 DWORD cbData//要写入键值数据的缓冲区长度 );
删除键值项函数:
LONG RegDeleteValue( HKEY hKey,//用来指定要删除的句柄 LPCTSTR lpValueName//要删除键值项的名称 );
枚举子键的函数:
LONG RegEnumKeyEx( HKEY hKey,//指定被枚举的键句柄 DWORD dwIndex,//指定需要返回信息的子键索引编号 LPTSTR lpName,//用户接收返回子键名称的缓冲区 LPDWORD lpcName,//调用函数前,该参数保留lpName指向缓冲区的长度;函数调用后,该参数保存缓冲区实际接收到的数据的长度 LPDWORD lpReserved,//保留参数,必须为NULL LPTSTR lpClass,//一般为NULL LPDWORD lpcClass,//一般为NULL PFILETIME lpftLastWriteTime//指向一个FILETIME结构体,用于接收最后一次被写入的时间。 );
枚举键值的函数:
LONG RegEnumValue( HKEY hKey,//指定被枚举的键句柄 DWORD dwIndex,//指定需要返回信息的键值索引号 LPTSTR lpValueName,//用户接收返回键值名称的缓冲区 LPDWORD lpcValueName,// 调用函数前,该参数保留lpName指向缓冲区的长度;函数调用后,该参数保存缓冲区实际接收到的数据的长度 LPDWORD lpReserved, //保留参数,必须为NULL LPDWORD lpType,//指向一个用于返回键值数据类型的双字变量 LPBYTE lpData,//用户接收返回值数据的缓冲区 LPDWORD lpcbData//在调用函数前,保存lpData指向缓冲区的长度;函数调用后,该参数保存缓冲区实际接收到的数据长度 );
至此,注册表操作的常用API就介绍完了,接下来当然要开始实践一番了。
我们知道系统中有些程序是开机启动的,下面我们就注册表中一个开机启动处来进行下面的程序开发。其中一处开机启动的注册表位置是:"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run\"。
首先布局我们的主界面如下:
点击“添加启动”按钮出现的附属界面如下:
在附属界面的对应类中关联两个成员变量,分别用于保存两个文本框中输入的值。点击“确定”按钮把值更新到对应的成员变量中,可以用UpdateData函数来实现。
在主界面cpp中定义一个宏,来标识我们要操作的注册表位置:
#define REG_RUN"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run\\"
初始化CListCtrl控件://设置扩展样式 m_RunList.SetExtendedStyle( m_RunList.GetExtendedStyle() | LVS_EX_GRIDLINES | LVS_EX_FULLROWSELECT); //在ListCtrl中插入新列 m_RunList.InsertColumn(0, TEXT("NO.")); m_RunList.InsertColumn(1, TEXT("键值名称")); m_RunList.InsertColumn(2, TEXT("键 值")); //LVSCW_AUTOSIZE_USEHEADER: /*列的宽度自动匹配为标题文本 如果这个值用着最后一列,列宽被设置为ListCtrl剩余的宽度*/ m_RunList.SetColumnWidth(0, LVSCW_AUTOSIZE_USEHEADER); m_RunList.SetColumnWidth(1, LVSCW_AUTOSIZE_USEHEADER); m_RunList.SetColumnWidth(2, LVSCW_AUTOSIZE_USEHEADER);
枚举对应位置的启动项:
//清空ListCtrl中的所有项 m_RunList.DeleteAllItems(); DWORD dwType = 0; DWORD dwBufferSize = MAXBYTE; DWORD dwKeySize = MAXBYTE; char szValueName[MAXBYTE] = { 0 }; char szValueKey[MAXBYTE] = { 0 }; HKEY hKey = NULL; //打开注册表 LONG lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT(REG_RUN), 0, KEY_ALL_ACCESS, &hKey); if (lRet != ERROR_SUCCESS) return; int i = 0; CString strTmp; while (TRUE) { //枚举键项 lRet = RegEnumValue(hKey, i, (LPWSTR)szValueName, &dwBufferSize, NULL, &dwType, (unsigned char *)szValueKey, &dwKeySize); //没有则退出循环 if (lRet == ERROR_NO_MORE_ITEMS) break; //把得到的键项显示到列表控件中 strTmp.Format(_T("%d"), i + 1); m_RunList.InsertItem(i, strTmp); m_RunList.SetItemText(i, 1, (LPCTSTR)szValueName); m_RunList.SetItemText(i, 2, (LPCTSTR)szValueKey); ZeroMemory(szValueKey, MAXBYTE); ZeroMemory(szValueName, MAXBYTE); dwBufferSize = MAXBYTE; dwKeySize = MAXBYTE; ++i; } RegCloseKey(hKey);
添加启动项代码:
CRegAdd RegAdd;//CRegAdd是我定义的附属Dialog类 RegAdd.DoModal(); //AfxMessageBox(RegAdd.m_szKeyName); //AfxMessageBox(RegAdd.m_szKeyValue); //判断输入是否完整 if (strlen((const char *)RegAdd.m_szKeyName.GetBuffer(0)) > 0 && strlen((const char *)RegAdd.m_szKeyValue.GetBuffer(0)) > 0) { HKEY hKey = NULL; LONG lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT(REG_RUN), 0, KEY_ALL_ACCESS, &hKey); if (lRet != ERROR_SUCCESS) { AfxMessageBox(_T("添加注册表项发生错误!")); return; } RegSetValueEx(hKey, RegAdd.m_szKeyName, 0, REG_SZ, (const BYTE *)(LPCTSTR)RegAdd.m_szKeyValue, RegAdd.m_szKeyValue.GetLength() * 2); RegCloseKey(hKey); ShowRunList(); } else { AfxMessageBox(_T("请输入完整内容!")); }
删除启动项代码:
POSITION pos = m_RunList.GetFirstSelectedItemPosition(); int nSelected = -1; while (pos) { nSelected = m_RunList.GetNextSelectedItem(pos);//获得选中的最后一项 } if (-1 == nSelected) { AfxMessageBox(_T("请选择要删除的启动项")); return; } char szKeyName[MAXBYTE] = { 0 }; m_RunList.GetItemText(nSelected, 1, (LPTSTR)szKeyName, MAXBYTE); HKEY hKey = NULL; LONG lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT(REG_RUN), 0, KEY_ALL_ACCESS, &hKey); RegDeleteValue(hKey, (LPCWSTR)szKeyName); RegCloseKey(hKey); ShowRunList();//定义的枚举启动项函数
主要的函数操作就是上面这些了,运行项目就可以尝试给自己的系统添加和删除启动项了(*^-^*)