📄 015.txt
字号:
add edi,eax
inc edi
.endw
invoke lstrlen,esi
add esi,eax
inc esi
.endw
ret
_EnumINI endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_GetPosition proc
local @szBuffer[512]:byte
;********************************************************************
; 将当前路径和 ini 文件名组合起来
;********************************************************************
invoke GetCurrentDirectory,MAX_PATH,addr szProfileName
mov esi,offset szProfileName
invoke lstrlen,esi
mov ecx,offset szFileName
.if byte ptr [esi+eax-1] == ~\~
inc ecx
.endif
invoke lstrcat,esi,ecx
;********************************************************************
; 读存放在 ini 文件中的数据
;********************************************************************
invoke GetPrivateProfileInt,addr szSecPos,\
addr szKeyX,50,addr szProfileName
push eax
invoke GetPrivateProfileInt,addr szSecPos,\
addr szKeyY,50,addr szProfileName
pop ecx
invoke SetWindowPos,hWinMain,HWND_TOP,ecx,eax,0,0,SWP_NOSIZE
ret
_GetPosition endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_SavePosition proc
local @szBuffer[512]:byte,@szRect:RECT
15.2 INI文件的操作(3)
invoke GetWindowRect,hWinMain,addr @szRect
invoke wsprintf,addr @szBuffer,addr szFmt1,@szRect.left
invoke WritePrivateProfileString,addr szSecPos,addr szKeyX,\
addr @szBuffer,addr szProfileName
invoke wsprintf,addr @szBuffer,addr szFmt1,@szRect.top
invoke WritePrivateProfileString,addr szSecPos,addr szKeyY,\
addr @szBuffer,addr szProfileName
ret
_SavePosition endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_ProcDlgMain proc uses ebx edi esi hWnd,wMsg,wParam,lParam
local @szSection[256]:byte
local @szKey[256]:byte
local @szValue[256]:byte
local @szBuffer[256]:byte
mov eax,wMsg
;********************************************************************
.if eax == WM_CLOSE
invoke _SavePosition
invoke EndDialog,hWnd,NULL
;********************************************************************
.elseif eax == WM_INITDIALOG
push hWnd
pop hWinMain
invoke LoadIcon,hInstance,ICO_MAIN
invoke SendMessage,hWnd,WM_SETICON,ICON_BIG,eax
invoke _GetPosition
invoke _EnumINI
;********************************************************************
.elseif eax == WM_COMMAND
invoke GetDlgItemText,hWnd,IDC_SEC,addr @szSection,sizeof @szSection
invoke GetDlgItemText,hWnd,IDC_KEY,addr @szKey,sizeof @szKey
invoke GetDlgItemText,hWnd,IDC_VALUE,addr @szValue,sizeof @szValue
mov eax,wParam
.if ax >= IDC_SEC && ax <= IDC_INI
mov eax,TRUE
ret
.elseif ax == IDC_DEL_SEC
invoke WritePrivateProfileString,addr @szSection,\
NULL,NULL,addr szProfileName
.elseif ax == IDC_DEL_KEY
invoke WritePrivateProfileString,addr @szSection,\
addr @szKey,NULL,addr szProfileName
.elseif ax == IDC_SET_KEY
invoke WritePrivateProfileString,addr @szSection,\
addr @szKey,addr @szValue,addr szProfileName
.elseif ax == IDC_GET_KEY
invoke GetPrivateProfileString,addr @szSection,\
addr @szKey,NULL,addr @szBuffer,\
sizeof @szBuffer,addr szProfileName
invoke SetDlgItemText,hWnd,IDC_VALUE,addr @szBuffer
.endif
invoke _EnumINI
;********************************************************************
.else
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret
_ProcDlgMain endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
start:
invoke GetModuleHandle,NULL
mov hInstance,eax
invoke DialogBoxParam,eax,DLG_MAIN,NULL,\
offset _ProcDlgMain,NULL
invoke ExitProcess,NULL
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
end start
在对话框关闭的时候,程序在WM_CLOSE函数中调用_SavePosition子程序将对话框窗口当前的位置保存到INI文件中,以便在下一次运行的时候恢复原来的窗口位置。程序在WM_INITDIALOG中调用_GetPosition子程序以恢复上一次保存的位置,这样,程序就可以“记住”窗口的摆放位置。
枚举小节和键值的功能是在_EnumINI子程序中完成的。程序在一开始执行的时候或者在每次收到WM_COMMAND消息的时候都调用这个函数,所以每次用户有所操作,INI文件的变化就会马上在窗口中的编辑框中反映出来。
1. 键值的创建和删除
当按下“保存Key”按钮时,例子程序使用WritePrivateProfileString函数保存键值,这个函数可以往指定的INI文件中写入键值,函数的用法是:
invoke WritePrivateProfileString,lpAppName,lpKeyName,\
lpString,lpFileName
在函数的参数中,lpAppName参数指向包含Section名称的字符串,lpKeyName参数指向包含键名称的字符串,lpString参数指向键值字符串,最后一个参数指向INI文件名字符串。这些字符串都是以0字符结束的。
当这些参数全部指定为字符串的时候,函数将在指定INI文件的指定小节中写入“键名=键值”格式的行;当指定的INI文件、文件中的小节和小节中的键名都已经存在的时候,函数用新键值替换原来的键值;当指定的INI文件存在而小节不存在的时候,函数自动创建小节并将键写入;如果连指定的INI文件也不存在的话,函数会自动创建文件。总之,程序不必考虑INI文件是否存在,小节是否存在或键值定义是否存在等情况,只要调用WritePrivateProfileString函数就可以保证配置信息被正确保存。
WritePrivateProfileString函数也可以用来删除键或者小节,当lpAppName和lpKeyName参数指定了小节名称和键名,而lpString参数指定为NULL的时候,函数将指定的键删除,如例子文件中对“删除Key”按钮的操作就是这样的:
.elseif ax == IDC_DEL_KEY
invoke WritePrivateProfileString,addr @szSection,\
addr @szKey,NULL,addr szProfileName
但是使用这种方法逐一将某个小节中的键全部删除时,空白小节的定义字符串“[SectionName]”还保留在INI文件中。如果想要将小节的定义字符串连同小节的全部键定义全部删除的话,可以将lpKeyName和lpString参数全部指定为NULL,而lpAppName参数指定要删除的小节,如例子文件中对“删除Section”按钮的处理代码:
.elseif ax == IDC_DEL_SEC
invoke WritePrivateProfileString,addr @szSection,\
NULL,NULL,addr szProfileName
如果函数执行成功,将返回一个非0的值,如果执行失败将返回0。在定义键名的时候,注意不用在名称字符串中包括“=”号,因为等号被用来分隔键名和键值,键名也不能以注释字符“;”开始。在定义键值的时候可以使用等号和分号,但注意不要将键值定义为多行的文本。如果在字符串中包含换行和回车,比如将键值字符串指向下列所示的一个串:
“hello,world!”,0dh,0ah,“this is the second line”,0
那么函数会成功地被调用,但是最后的INI文件中会出现这样的内容:
[Section]
Key=hello,world!
this is the second line
显然,函数不加判断地将换行和回车也写到了INI文件中,但是当取回键值的时候,只有第一行能被正确取回,而底下的行将当做格式错误的“垃圾”留在INI文件中。
由于INI文件是以文本方式保存的,所以实际上键值也只能用字符串方式表示,如果需要保存一个数值类型的值,那么程序需要自己使用wsprintf函数将数值转换成字符串后再保存。比如例子程序在退出时为保存窗口位置,就是在_SavePosition子程序中首先用GetWindowRect函数获取窗口位置,然后使用wsprintf函数转换后再保存的。
15.2 INI文件的操作(4)
2. 获取键值
获取键值的操作比较方便,因为这时既可以用GetPrivateProfileString函数获取键值字符串,也可以使用GetPrivateProfileInt函数让Windows将键值字符串转换成数值后再返回,就像使用GetDlgItemInt函数转换对话框子窗口中的字符串一样。(比较奇怪的是保存键值的时候并没有一个WritePrivateProfileInt函数,结果每次还要首先使用wsprintf函数!)
GetPrivateProfileString函数的用法是:
invoke GetPrivateProfileString,lpAppName,lpKeyName,lpDefault,\
lpReturnedString,nSize,lpFileName
该函数的几个参数与WritePrivateProfileString的参数类似,也是使用lpAppName,lpKeyName和lpFileName参数分别指定小节名称、键名和INI文件名,但是其余几个参数则有所不同:
lpReturnedString参数指向一个缓冲区,函数在这里返回获取的键值字符串,缓冲区的长度用nSize参数指定,当缓冲区的长度太小以至于无法容纳返回的字符串时,字符串会被截止到nSize?1的长度后返回,余下的一个字节用来存放一个0字符用做结尾。
lpDefault参数指向一个默认字符串,当指定的键无法找到的时候,函数将这个字符串拷贝到返回缓冲区中。
GetPrivateProfileString还有两种特殊用法:首先,当lpAppName参数指定为NULL的时候,函数在缓冲区中返回的是全部小节名称的列表,每个小节名以0结尾,全部的名称列表再以一个附加的0结束,返回到缓冲区中的数据格式如下所示:
小节名称1,0,小节名称2,0,…,小节名称n,0,0
另外,当lpAppName参数指定了小节名称,而lpKeyName参数指定为NULL的时候,函数在缓冲区中返回该小节的全部键名列表,每个键名以0结尾,全部列表后面再以一个附加的0结束,如下所示:
键名1,0,键名2,0,…,键名n,0,0
所以用这两种方法调用GetPrivateProfileString函数可以实现枚举小节名称和枚举键名的功能。
不管用何种方式使用GetPrivateProfileString函数,函数的返回值是返回到缓冲区中的字符串长度(长度中并不包括结尾的0字符)。
如果保存的键值是全部由数字字符组成的话(比如例子程序的_SavePosition子程序中用wsprintf函数从窗口位置的坐标值转换过来的字符串),那么可以使用GetPrivateProfileInt函数直接将字符串转换成数值后再返回:
invoke GetPrivateProfileInt,lpAppName,lpKeyName,nDefault,lpFileName
其中lpAppName,lpKeyName和lpFileName参数的使用方法同上,函数将指定的键值字符串转换成数值类型以后返回,但是函数不支持负数,如果键值字符串是“?1234”格式的负数,那么函数的返回值是0。nDefault指定一个默认数值,如果指定的键名不存在的话,函数返回nDefault指定的数值。
例子程序在初始化的时候,在_GetPosition子程序中使用GetPrivateProfileInt函数读出上次退出时保存的窗口位置,并使用SetWindowPos函数将窗口移动到这个位置上。
15.2.3 管理小节
在键名已知的情况下固然可以使用GetPrivateProfileString等函数获取键值,但在某些情况下并不是所有的键名都是已知的,比如一个编辑文件需要保存近来编辑过的文件名列表,它可以建立一个小节如下:
[History]
file1=C:\My documents\Readme.txt
file2=D:\MyApp\Help.txt
file3=C:\download\win32asm.txt
...
这时小节中键的数量和名称就是不定的。另外,也有小节的数量和名称不定的情况,这时就需要对小节或键进行枚举。上一节中已经介绍了在GetPrivateProfileString函数中通过将lpAppName或lpKeyName参数设置为NULL来获取小节名称列表和键名列表的方法。实际上,Windows中还有专门用来实现此功能的函数,这些函数不仅可用来枚举小节和键,也可以用来一次性修改整个小节的内容。
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -