WindowsAPI查缺补漏-INI配置文件与注册表操作

碎碎念

INI文件即初始化文件,如Windows目录中Win.ini保存桌面设置和与应用程序运行有关信息,System.ini保存于硬件配置有关的信息,还有Control.ini等,但他们被映射到注册表HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\Current Version\IniFileMapping中。单个INI文件大小不得超过64KB,注释以“;”开头且必须独占一行。键名不能存在“;”但键值可存在,键名不能为多行文本。

NT系统注册表由Windows\system32\Config目录中多个文件构成,文件运行时这些文件被系统独占,无任何权限(甚至是读),只能通过Windows提供的接口进行操作。常见键值数据类型有:

键值数据类型 含义
REG_SZ
REG_DWORD
REG_QWORD
REG_BINARY
REG_MULTI_SZ 字符串序列,每个字符串以1个\0结尾,最后一个以2个\0结尾
REG_EXPAND_SZ 包含对环境变量的为扩展引用的字符串
REG_DWORD_LITTLE_ENDIAN
REG_DWORD_BIG_ENDIAN
REG_QWORD_LITTLE_ENDIAN
REG_LINK 包含符号链接的目标路径的字符串
REG_NONE 没定义的

注册表中有5个根键,其中HKEY_LOCAL_MACHINE和HEKY_USERS为两个大根键,其他根键都由他俩映射出来,如HEKY_CLASSES_ROOT是HEKY_LOCAL_MACHINE\SOFTWARE\Classes子键的映射。每个根键介绍有:

根键 含义
HKEY_LOCAL_MACHINE 系统和软件的设置,针对所有Windows系统用户
HKEY_USERS 默认用户、当前登录用户与软件等信息。如未来将被创建的新用户根键默认用户.DEFAULT子键配置信息生成自己的配置文件,包括环境、屏幕、声音等
HKEY_CLASSES_ROOT 系统中所有数据文件信息,主要为不同文件名后缀文件和与之关联的应用程序。
HKEY_CURRENT_USER 当前用户信息
HKEY_CURRENT_CONFIG 硬件配置文件,很少使用

键值数据不要大于2KB,否则影响效率。

INI配置文件

WritePrivateProfileString

在指定INI文件指定小节创建、更新或删除键值对,或删除整个小节:

1
2
3
4
5
6
BOOL WINAPI WritePrivateProfileString(
_In_ LPCTSTR lpAppName, //小节名字符串 不区分大小写
_In_ LPCTSTR lpKeyName, //键名字符串
_In_ LPCSTR lpString, //键值字符串
_In_ LPCTSTR lpFileName //INI文件名称字符串
);

当lpAppName或lpKeyName不存在则自动创建该小节/键名。lpKeyName为NULL则删除lpAppName指定的小节。lpString为NULL则删除lpKeyName指定键。lpFileName文件不存在自动创建,但指定目录必须存在,不含完整路径时从Windows目录中搜索。

GetPrivateProfileString

获取指定INI文件指定小节键名对应的键值字符串,还可以枚举指定INI文件中所有小节名称,还可以枚举指定INI文件指定小节名称中所有键名。

1
2
3
4
5
6
7
8
DWORD WINAPI GetPrivateProfileString(
_In_ LPCTSTR lpAppName, //小节名称字符串 不区分大小写
_In_ LPCTSTR lpKeyName, //键名字符串
_In_ LPCTSTR lpDefault, //默认字符串 可NULL
_Out_ LPTSTR lpReturnedString, //接收获取到的字符串缓冲区指针
_In_ DWORD nSize, //lpReturnedString指向缓冲区大小 单位字符
_In_ LPCTSTR lpFileName //INI文件名称字符串
);

当lpAppName为NULL时将INI中所有小节名复制到lpReturnedString缓冲区中,每个小节名后以0结尾,最后一个小节名后有2个0。当lpKeyName为NULL时将指定节中所有键名复制到lpReturnedString缓冲区中,每个键名后以0结尾,最后一个键名后有2个0。当INI中没有lpKeyName时将lpDefault复制到lpReturnedString缓冲区。lpFileName如不包含完整路径则在Windows目录中搜索。

返回复制到缓冲区中字符数,不含终止空字符。求键值时缓冲区大小不足则返回nSize-1。枚举时缓冲区大小不足返回nSize-2。

GetPrivateProfileInt

同上,UINT型键值:

1
2
3
4
5
6
UINT WINAPI GetPrivateProfileInt(
_In_ LPCTSTR lpAppName,
_In_ LPCTSTR lpKeyName,
_In_ INT nDefault,
_In_ LPCTSTR lpFileName
);

GetPrivateProfileSectionNames

枚举指定INI中所有小节名称,用法同GetPrivateProfileString

1
2
3
4
5
DWORD WINAPI GetPrivateProfileSectionNames(
_Out_ LPTSTR lpszReturnBuffer,
_In_ DWORD nSize,
_In_ LPCTSTR lpFileName
);

GetPrivateProfileSection

枚举指定INI文件指定小节中所有键值对,用法同GetPrivateProfileString

1
2
3
4
5
6
DWORD WINAPI GetPrivateProfileSection(
_In_ LPCTSTR lpAppName,
_Out_ LPTSTR lpReturnedString,
_In_ DWORD nSize,
_In_ LPCTSTR lpFileName
);

WritePrivateProfileSection

向指定INI指定小节批量写入键值对:

1
2
3
4
5
BOOL WINAPI WritePrivateProfileSection(
_In_ LPCTSTR lpAppName,
_In_ LPCTSTR lpString,
_In_ LPCTSTR lpFileName
);

原键值对将被全部覆盖删除,lpString中每个键值以0结尾,最后一个键值对以2个0结尾。lpString理所当然的最大只能64KB。

注册表操作

RegOpenKeyEx

打开一个子键并获取一个子键句柄:

1
2
3
4
5
6
7
LONG WINAPI RegOpenKeyEx(
_In_ HKEY hKey, //父键句柄
_In_opt_ LPCTSTR lpSubKey, //子键名称字符串
_In_ DWORD ulOptions, //通常0
_In_ REGSAM samDesired, //子键打开方式 即访问权限
_Out_ PHKEY phkResult //返回打开的子键句柄
); //成功返回ERROR_SUCCESS

lpSubKey子键可以是多层目录,hKey为根键时用常量HKEY_LOCAL_MACHINE等。sammDesired有:

枚举值 访问权限
KEY_QUERY_VALUE 查询键值项数据
KEY_CREATE_SUB_KEY 创建下一层子键
KEY_ENUMERATE_SUB_KEYS 枚举子键
KEY_NOTIFY 子键及下面子键发生更改时接收到通知
KEY_SET_VALUE 创建、修改和删除键值项
KEY_READ/KEY_EXECUTE STANDARD_RIGHTS_READ、KEY_QUERY_VALUE、KEY_ENUMERATE_SUB_KEYS、KEY_NOTIFY
KEY_WRITE STANDARD_RIGHTS_WRITE、KEY_SET_VALUE、KEY_CREATE_SUB_KEY
KEY_WOW64_32KEY 应用程序在32位注册表视图上运行
KEY_WOW64_64KEY 应用程序在64位注册表视图上运行
KEY_ALL_ACCESS STANDARD_RIGHTS_REQUIRED、KEY_QUERY_VALUE、KEY_SET_VALUE、KEY_CREATE_SUB_KEY、KEY_ENUMERATE_SUB_KEYS、KEY_NOTIFY、KEY_CREATE_LINK

RegCloseKey

关闭子键句柄:

1
2
3
LONG WINAPI RegCloseKey(
_In_ HKEY hKey //子键句柄
); //成功返回ERROR_SUCCESS

RegCreateKeyEx

创建一个子键并返回子键句柄,已存在则打开,一些参数同RegOpenKeyEx。注意不能在两个大根键下创建直系子键。

1
2
3
4
5
6
7
8
9
10
11
LONG WINAPI RegCreateKeyEx(
_In_ HKEY hKey,
_In_ LPCTSTR lpSubKey,
_Reserved_ DWORD Reserved, //必须0
_In_opt_ LPTSTR lpClass, //用户定义子键类名 通常NULL
_In_ DWORD dwOptions, //子键创建选项 通常REG_OPTION_NON_VOLATILE
_In_ REGSAM samDesired,
_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, //通常NULL
_Out_ PHKEY phkResult,
_Out_opt_ LPDWORD lpdwDisposition //返回函数处理结果
);

dwOptions有:

枚举值 含义
REG_OPTION_NON_VOLATILE 子键在系统重启时保留在注册表中
REG_OPTION_VOLATILE 子键保存在内存中,系统重启即消失
REG_OPTION_BACKUP_RESTORE 忽略samDesired,尝试用备份或还原子键所需访问权限打开子键

lpdwDisposition有:

枚举值 含义
REG_CREATED_NEW_KEY 子键不存在且已经被创建
REG_OPENED_EXISTING_KEY 子键已存在,直接打开的

例如:

1
2
3
4
5
6
7
8
HKEY hKey;
LPCTSTR lpSubKey = TEXT("Software\\INIDemo");
LONG lRet;
lRet = RegCreateKeyEx(HKEY_CURRENT_USER, lpSubKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL);
if (lRet != ERROR_SUCCESS) {
MessageBox(hwndDlg, TEXT("创建或打开子键失败!"), TEXT("提示"), MB_OK);
return FALSE;
};

RegDeleteKey

删除一个子键和该子键中所有键值项,要删除的子键下必须无子键。要递归删除子键用RegDeleteTreeSHDeleteKey,这俩与RegDeleteKey参数完全一样。

1
2
3
4
LONG WINAPI RegDeleteKey(
_In_ HKEY hKey, //父键句柄
_In_ LPCTSTR lpSubKey //子键名称字符串
); //成功ERROR_SUCCESS

RegSetValueEx

在指定子键中创建或设置键值项:

1
2
3
4
5
6
7
8
LONG WINAPI RegSetValueEx(
_In_ HKEY hKey, //子键句柄
_In_opt_ LPCTSTR lpValueName, //键名字符串
_Reserved_ DWORD Reserved, //必须0
_In_ DWORD dwType, //lpData指向的数据类型
_In_ CONST PBYTE lpData, //要存储的键值数据
_In_ DWORD cbData //lpData指向数据大小 单位字节
); //成功返回ERROR_SUCCESS

lpValueName不存在则创建,已存在则更改键值,为空字符或NULL则创建或设置子键默认键。lpData要求包含1个或2个终止\0。

RegQueryValueEx

获取指定子键中指定键名的键值数据或键值数据类型:

1
2
3
4
5
6
7
8
LONG WINAPI RegQueryValueEx(
_In_ HKEY hKey, //子键句柄
_In_opt_ LPCTSTR lpValueName, //键名字符串
_Reserved_ LPDWORD lpReserved, //必须NULL
_Out_opt_ LPDWORD lpType, //返回键值数据的数据类型
_Out_opt_ LPBYTE lpData, //返回键值数据的缓冲区指针
_Inout_opt_ LPDWORD lpcbData //指定缓冲区大小 单位字节 当作返回值为复制到lpData数据大小
); //成功返回ERROR_SUCCESS lpData缓冲区过小返回ERROR_MORE_DATA且所需缓冲区大小在lpcbData返回

hKey为空字符串或NULL表示子键中默认键。

RegGetValue

RegQueryValueEx,但不需要子键句柄:

1
2
3
4
5
6
7
8
9
LONG WINAPI RegGetValue(
_In_ HKEY hKey, //父键句柄
_In_opt_ LPCTSTR lpSubKey, //子键名字符串
_In_opt_ LPCTSTR lpValueName, //键名字符串 为空字符串或NULL表示默认键
_In_opt_ DWORD dwFlags, //限制要查询的键值数据类型标志 一般RRT_RT_ANY
_Out_opt_ LPDWORD lpType, //返回键值数据类型
_Out_opt_ PVOID pvData, //返回键值数据的缓冲区指针
_Inout_opt_ LPDWORD lpcbData //缓冲区大小 单位字节 当作返回值为复制到pvData数据大小
); //成功返回ERROR_SUCCESS pvData不够大返回ERROR_MORE_DATA且所需大小在lpcbData返回

dwFlags可取值有:

枚举值 含义
RRT_RT_ANY
RRT_RT_REG_SZ
RRF_RT_REG_DWORD
RRF_RT_REG_QWORD
RRT_RT_REG_BINARY
RRT_RT_DWORD
RRT_RT_QWORD
RRT_RT_REG_EXPAND_SZ
RRT_RT_REG_MULTI_SZ
RRF_RT_REG_NONE
RRF_NOEXPAND 数据类型为REG_EXPAND_SZ则不要自动扩展环境变量字符串
RRF_ZEROONFAILURE 当pvData不为NULL则函数执行失败时将缓冲区清零
RRF_SUBKEY_WOW6464KEY 当lpSubKey不为NULL则打开lpSubKey指定有KEY_WOW64_64KEY访问权限的子键
RRF_SUBKEY_WOW6432KEY 当lpSubKey不为NULL则打开lpSubKey指定有KEY_WOW64_32KEY访问权限的子键

例如:

1
RegGetValue(HKEY_CURRENT_USER, lpSubKey, lpValueNameX, RRF_RT_ANY, NULL, &dwX, &dwcbData);

RegDeleteValue

删除指定子键中指定键值项:

1
2
3
4
LONG WINAPI RegDeleteValue(
_In_ HKEY hKey, //子键句柄
_In_opt_ LPCTSTR lpValueName //键名字符串
); //成功返回ERROR_SUCCESS

RegEnumKeyEx

枚举指定子键下所有子键名称、类类型和最后写入时间:

1
2
3
4
5
6
7
8
9
10
LONG RegEnumKeyEx(
_In_ HKEY hKey, //子键句柄
_In_ DWORD dwIndex, //子键下子键索引 初始0
_Out_opt_ LPTSTR lpName, //返回子键名称 包括终止空字符
_Inout_ LPDWORD lpcchName, //lpName缓冲区大小 单位字符 返回实际大小但不含终止空字符
_Reserved_ LPDWORD lpReserved, //必须NULL
_Out_opt_ LPTSTR lpClass, //返回子键类名 通常NULL
_Inout_opt_ LPDWORD lpcchClass, //lpClass缓冲区大小 单位字符 返回实际大小但不含终止空字符
_Out_opt_ PFILETIME lpftLastWriteTime //子键最后写入时间
); //成功返回ERROR_SUCCESS 缓冲区太小返回ERROR_MORE_DATA

dwIndex枚举时循环调用,第一次设置为0,后续每次增加1,直到函数返回ERROR_NO_MORE_ITEMS。

例如枚举方式:

1
2
3
4
5
6
7
8
9
10
11
12
DWORD dwIndex;
TCHAR szName[MAX_PATH] = { 0 };
DWORD dwchName;
dwIndex = 0;
while (TRUE) {
dwchName = _countof(szName);
lRet = RegEnumKeyEx(hKey, dwIndex, szName, &dwchName, NULL, NULL, NULL, NULL);
if (lRet == ERROR_NO_MORE_ITEMS)
break;
//...
dwIndex++;
};

RegEnumValue

枚举指定子键下所有键值项,部分参数同RegEnumKeyEx

1
2
3
4
5
6
7
8
9
10
LONG WINAPI RegEnumValue(
_In_ HEKY hKey,
_In_ DWORD dwIndex,
_Out_ LPTSTR lpValueName, //返回键名 含终止空字符
_Inout_ LPDWORD lpcchValueName, //lpValueName缓冲区大小 单位字符 返回实际大小
_Reserved_ LPDWORD lpReserved,
_Out_opt_ LPDWORD lpType, //返回键值数据类型
_Out_opt_ LPBYTE lpData, //返回键值数据
_Inout_opt_ LPDWORD lpcbData //lpData缓冲区大小 单位字节 返回实际大小
); //成功返回ERROR_SUCCESS

缓冲区过小时函数返回ERROR_MOR_DATA,lpcbData指向变量中返回所需缓冲区大小。当lpData为NULL时表示获取所需缓冲区大小,单位字节,返回在lpcbData指向DWORD变量中,这时函数返回ERROR_SUCCESS。

例如枚举方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
DWORD dwIndex;
TCHAR szValueName[MAX_PATH] = { 0 };
BYTE bData[512] = { 0 };
DWORD dwchValueName, dwcbData;
dwIndex = 0;
while (TRUE) {
dwchValueName = _countof(szValueName);
dwcbData = sizeof(bData);
lRet = RegEnumValue(hKey, dwIndex, szValueName, &dwchValueName, NULL, NULL, bData, &dwcbData);
if (lRet == ERROR_NO_MORE_ITEMS)
break;
//...
dwIndex++;
};

RegQueryInfoKey

查询指定子键相关信息,如一个子键下子键数量、键值项数量、子键名称、键名字符串最大长度、键值数据最大长度等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
LONG WINAPI RegQueryInfoKey(
_In_ HKEY hKey, //子键句柄
_Out_opt_ LPTSTR lpClass, //返回子键类名
_Inout_opt_ LPDWORD lpcClass, //lpClass缓冲区大小单位字符
_Reserved_ LPDWORD lpReserved, //必须NULL
_Out_opt_ LPDWORD lpcSubKeys, //返回hKey下所有子键数量
_Out_opt_ LPDWORD lpcMaxSubKeyLen, //返回子键名称最大长度 按Unicode 不含终止字符
_Out_opt_ LPDWORD lpcMaxClassLen, //返回子键类名最大长度 按Unicode 不含终止字符
_Out_opt_ LPDWORD lpcValues, //返回hKey下所有键值项数量
_Out_opt_ LPDWORD lpcMaxValueNameLen, //返回键名最大长度 按Unicode 不含终止字符
_Out_opt_ LPDWORD lpcMaxValueLen, //返回键值数据最大长度 单位字节
_Out_opt_ LPDWORD lpcbSecurityDescriptor, //返回hKey安全描述符大小 单位字节
_Out_opt_ PFILETIME lpftLastWriteTime //返回hKey最后写入时间
); //成功返回ERROR_SUCCESS

应用:程序开机自动运行

下面路径选一个子键创建键值项,键值数据为程序完整路径,选后两个路径只运行1次:

1
2
3
4
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunOnce
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunOnce

例如:

1
2
3
4
5
6
7
8
HKEY hKey;
LPCTSTR lpSubKey = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Run");
LPCTSTR lpValueName = TEXT("INIDemo");
LPTSTR lpData = TEXT("F:\\Source\\Windows\\Chapter7\\INIDemo\\Debug\\INIDemo.exe");
DWORD dwcbData = (_tcslen(lpData) + 1) * sizeof(TCHAR);
RegOpenKeyEx(HKEY_LOCAL_MACHINE, lpSubKey, 0, KEY_WRITE, &hKey);
RegSetValueEx(hKey, lpValueName, NULL, REG_SZ, (LPBYTE)lpData, dwcbData);
RegCloseKey(hKey);

注册表重定向不用管。

应用:文件关联程序

在注册表HKEY_CLASSES_ROOT根键下新建2个子键。第1个子键为“.扩展名”,该子键下设置一个默认键,默认键键值数据类型为REG_SZ,键值数据是根键下新建的第2个子键名。对于第2个子键,当默认动作是“打开”时继续在该子键中创建“shell\open\command”子键,该子键下设置默认键,键值数据类型是REG_SZ或REG_EXPAND_SZ,键值数据为可执行文件完整路径;当默认动作是“打印”时继续在该子键中创建“shell\print\command”子键,默认键设置为执行打印操作的可执行文件名。

例如.txt文件用notepad.exe打开:HKEY_CLASSES_ROOT\.txt子键默认键键值为“txtfile”,HKEY_CLASSES_ROOT\txtfile\shell\open\command子键默认键键值为“%System Root%\system32\NOTEPAD.EXE %1”。