📄 015.txt
字号:
15.1 注册表和INI文件简介
在一个操作系统中,无论是操作系统本身还是运行于其中的大部分应用程序,都需要使用某种方式保存配置信息。在DOS系统中,配置信息往往是软件的开发者根据自己的喜好用各种途径加以保存的,比如在磁盘上面写一个二进制的 .dat文件,或者写一个文本文件等,这些配置文件中数据的格式也是各不相同的。
到了Windows 3.x系统中,这种情况发生了变化,虽然软件的开发者还可以沿用这种自成体系的方法,但是系统也提供了一种标准的初始化文件格式(Initialization File)来保存配置信息。初始化文件是一种以INI为扩展名的文本文件,操作系统提供了一套专用的函数对INI文件进行操作。Windows 3.x不仅鼓励程序员使用INI文件,操作系统本身也使用INI文件来保存配置信息,比如Windows安装目录下的Win.ini文件中保存了桌面设置和与应用程序运行有关的信息;System.ini文件中保存了与硬件配置有关的信息,另外,Control.ini与Program.ini等文件也是很重要的配置文件。
INI文件在使用中存在诸多缺陷。由于INI文件是文本文件,使用任何文本编辑器都可以随意对它进行修改,所以安全性不是很好。另外,INI文件的结构比较简单,无法保存格式复杂的数据,如很长的二进制数据或带回车的字符串等。最主要的缺点还是单个INI文件的大小不能超过64 KB。如果不同的应用程序都将自己的配置信息保存于Win.ini或者System.ini中,那么这些文件的规模很快就会超过限制的长度。如果不同应用程序都使用自己的INI的话,那么集中管理又成了一个问题。
在Windows 9x和Windows NT系列操作系统中,改用了一种全新的方法来管理配置信息,那就是使用集中管理的注册表(Registry)。注册表并不像它的中文名称所称的那样是“表”,而是一种格式由系统定义的数据库,它存放于某些二进制文件中。不同操作系统中对应的文件名可能有所不同,比如Windows 9x系统中的注册表文件由位于Windows安装目录中的System.dat和User.dat两个文件组成,而NT系统的注册表往往由位于Windows安装目录下的System32\Config目录中的多个文件构成。操作系统将这些不同的文件集中“虚拟”成整个注册表供系统自身及应用程序使用。
相对于INI文件对于Windows 3.x系统的重要性,注册表对于Windows 9x和NT系统来说显得更加重要。因为绝大多数系统使用的配置信息都存放于此,如系统的硬件配置、安装的驱动程序列表、文件的关联信息、系统的网络配置和权限配置等,这些配置信息直接关系到Windows系统的启动和初始化过程。如果注册表受到了破坏,那么轻者Windows的启动过程出现异常,重者可能会使整个系统无法启动。另外,大部分应用程序的配置信息也存放于此,如果注册表受到了破坏,即使操作系统能正常启动,应用程序的运行也可能会受影响。
正是因为注册表结构的封闭性和重要性,我们无法再使用像编辑INI文件那样的简单方法来编辑注册表文件。实际上,Windows系统对注册表文件的保护很严格,当系统在运行的时候,注册表文件是被操作系统以独占方式打开的,其他应用程序即使是用最基本的读权限也无法打开它们,更不用说对它们进行写操作了。
要对注册表进行操作,必须使用系统提供的接口。Windows为此提供了一系列的注册表操作函数,应用程序可以通过它们来完成注册表编辑器(Regedit程序)能完成的全部功能,甚至包括远程操作注册表以及对 .reg文件进行导入和导出等操作。
为了提供向下兼容性,Windows 9x和NT系统在支持注册表操作的同时也支持INI文件的操作。实际上对于某些“Copy and play”的小程序来说,需要保存的配置信息并不复杂,使用INI文件可能更加简单实用,而且保存于注册表中的配置信息是无法随文件拷贝到其他计算机中的。如果某些应用程序希望拷贝程序的同时可以拷贝配置信息,那么最好还是使用INI文件,所以在Windows 9x和NT系统中,INI文件的使用还是相当广泛的。
本章用两个单独的程序例子,详细介绍INI文件和注册表的使用方法。
15.2 INI文件的操作(1)
15.2.1 INI文件的结构
INI文件是一种文本格式的文件,其中的数据组织格式为:
;注释
[Section1 Name]
KeyName1=value1
;注释
KeyName2=value2
...
[Section2 Name]
KeyName1=value1
KeyName2=value2
...
INI文件中可以存在多个小节(Section),每个小节的开始用包括在一对方括号中的小节名称指定,不同的小节不能重名,一个小节的内容从小节名称的下一行开始,直到下一个小节开始为止。用户程序可以按照自己的需求建立多个小节。
在每个小节中可以定义多个键(Key),每一个键由一个“键名=键值”格式的字符串组成,并独自占用一行。在同一个小节中不能存在同名的键,但是在不同的小节中可以存在同名的键。
如果需要在INI文件的某些地方加注释,可以将注释放在单独的一行中,行首以分号开始,注释行出现的地方并没有什么限制,既可以出现在文件的最前面,也可以出现在文件的任何一行中。
一般来说,如果在自己开发的应用程序中使用系统定义的INI文件,如Win.ini等,由于文件中已经存在多个小节,那么自己建立一个独立的小节比较合适,然后在这个小节中定义不同的键值,比如,下面是笔者的计算机上Win.ini文件的片断:
...
[MCI Extensions]
asf=MPEGVideo
asx=MPEGVideo
m3u=MPEGVideo
mp2v=MPEGVideo
mp3=MPEGVideo
mpv2=MPEGVideo
wma=MPEGVideo
wmv=MPEGVideo
[Hex Workshop]
Path=C:\PROGRA~1\BREAKP~1\HEXWOR~1.1\hworks32.exe
CurrentVersion=3.11
...
其中的“MCI Extensions”小节是Windows系统自身使用的小节,Windows在这里定义了一些媒体文件的关联方式,而“Hex Workshop”小节是安装了HexWorkshop16进制编辑器后由软件创建并使用的,编辑器在小节中用“Path”键定义了软件的安装目录、在“CurrentVersion”键中定义了软件的版本号。
如果觉得往系统INI文件中写数据显得不是那么“绿色环保”,那么应用程序可以建立一个独立的INI文件。如本节的例子文件就在自己运行的目录中建立了一个Option.ini文件,并在“Windows Position”小节的“X”,“Y”键中保存窗口的位置,以便在下一次运行的时候将窗口移动到上一次退出时所处的位置,内容如下:
[Windows Position]
X=194
Y=162
...
Windows系统提供了一系列函数对INI文件进行操作,其中包括读取和设置键值,获取小节名称列表及获取和删除整个小节内容等函数。下面的例子演示了这些功能的使用方法。
15.2.2 管理键值
本节的例子程序存放在所附光盘的Chapter14\ini目录中,运行后的界面如图15.1所示。例子程序在运行目录下创建了一个Option.ini文件,程序中的所有操作都是针对这个文件进行的。当用户在“Section”一栏中输入小节名称、在“Key”一栏中输入键名后,如果INI文件中对应的小节和键定义是已经存在的,那么按下“读取Key”按钮后就会将键值读取到“Value”一栏中;而按下“删除Key”按钮的时候,可以将这个键删除。
在输入小节和键名后继续在“Value”一栏中输入一个字符串,并按下“保存Key”按钮,如果指定键已经存在,那么程序用新的键值替换原来的键值;如果键名不存在,则程序创建这个键;如果创建键的时候小节名是不存在的,那么程序在创建键值之前会自动创建小节;在最极端的情况下,当INI文件也不存在的时候,那么程序也会创建INI文件。
图15.1 INI文件操作例子的运行界面
当用户按下“删除Key”按钮将一个小节中的键逐一删除直到全部键都被删除的时候,小节名称并不会被删除,INI文件中还会留有一个空的小节名称。按下“删除Section”按钮就可以将Section一栏中指定的小节全部删除(包括小节中的全部键和小节名称)。
每次进行操作后,程序自动将INI文件中的所有小节和键值枚举一遍并将内容显示在图15.1下面的编辑框中,以便观察操作的结果。下面通过分析这个程序来了解这些功能的实现方法。
源文件目录中的Ini.rc文件定义了如图15.1所示的对话框,代码如下:
#include
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#define ICO_MAIN 1000
#define DLG_MAIN 1000
#define IDC_SEC 1001
#define IDC_KEY 1002
#define IDC_VALUE 1003
#define IDC_INI 1004
#define IDC_DEL_SEC 1005
#define IDC_DEL_KEY 1006
#define IDC_GET_KEY 1007
#define IDC_SET_KEY 1008
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ICO_MAIN ICON "Main.ico"
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
DLG_MAIN DIALOG 205, 128, 245, 168
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "INI 文件操作"
FONT 9, "宋体"
15.2 INI文件的操作(2)
{
RTEXT "Section", -1, 4, 7, 30, 8
EDITTEXT IDC_SEC, 39, 5, 78, 12, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP
RTEXT "Key", -1, 4, 23, 30, 8
EDITTEXT IDC_KEY, 39, 21, 78, 12, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP
RTEXT "Value", -1, 4, 39, 30, 8
EDITTEXT IDC_VALUE, 39, 37, 78, 12, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP
LTEXT "当前 INI 文件内容:", -1, 8, 57, 141, 8
EDITTEXT IDC_INI, 6, 71, 232, 91, ES_MULTILINE | ES_AUTOVSCROLL |
ES_AUTOHSCROLL | ES_READONLY | WS_BORDER | WS_VSCROLL | WS_TABSTOP
PUSHBUTTON "删除 &Section", IDC_DEL_SEC, 122, 4, 57, 14
PUSHBUTTON "删除 K&ey", IDC_DEL_KEY, 183, 4, 57, 14
PUSHBUTTON "读取 &Key", IDC_GET_KEY, 122, 20, 57, 14
PUSHBUTTON "保存 Ke&y", IDC_SET_KEY, 183, 20, 57, 14
}
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
汇编源文件Ini.asm的内容如下:
.386
.model flat, stdcall
option casemap :none
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Include 文件定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include windows.inc
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Equ 等值定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ICO_MAIN equ 1000
DLG_MAIN equ 1000
IDC_SEC equ 1001
IDC_KEY equ 1002
IDC_VALUE equ 1003
IDC_INI equ 1004
IDC_DEL_SEC equ 1005
IDC_DEL_KEY equ 1006
IDC_GET_KEY equ 1007
IDC_SET_KEY equ 1008
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data?
hInstance dd ?
hWinMain dd ?
szProfileName dd MAX_PATH dup (?)
szBuffer1 db 32760 dup (?)
szBuffer2 db 32760 dup (?)
.const
szFileName db ~\Option.ini~,0
szSecPos db ~Windows Position~,0
szKeyX db ~X~,0
szKeyY db ~Y~,0
szFmt1 db ~%d~,0
szFmtSection db ~[%s]~
szCrLf db 0dh,0ah,0
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 代码段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.code
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 枚举全部 Section 和全部 Key
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_EnumINI proc
local @szBuffer[256]:byte
invoke SetDlgItemText,hWinMain,IDC_INI,NULL
;********************************************************************
; 读取 Section 列表并循环处理
;********************************************************************
invoke GetPrivateProfileSectionNames,addr szBuffer1,\
sizeof szBuffer1,addr szProfileName
mov esi,offset szBuffer1
.while byte ptr [esi]
invoke wsprintf,addr @szBuffer,addr szFmtSection,esi
invoke SendDlgItemMessage,hWinMain,IDC_INI,\
EM_REPLACESEL,FALSE,addr @szBuffer
;********************************************************************
; 读取 Key 列表并循环显示
;********************************************************************
invoke GetPrivateProfileSection,esi,addr szBuffer2,\
sizeof szBuffer2,addr szProfileName
mov edi,offset szBuffer2
.while byte ptr [edi]
invoke SendDlgItemMessage,hWinMain,\
IDC_INI,EM_REPLACESEL,FALSE,edi
invoke SendDlgItemMessage,hWinMain,IDC_INI,\
EM_REPLACESEL,FALSE,addr szCrLf
invoke lstrlen,edi
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -