《WINDOWS 环境下32位汇编语言程序设计》第8章 通用对话框
Windows操作系统为一些常用功能提供了一些通用对话框(Common Dialog Box),比如,在不同的应用程序中进行打开文件、选择字体、选择颜色等操作时,不同程序显示的对话框的模样都是一样的。另外,把同样的应用程序放到不同版本的Windows下执行就会发现,这些对话框会随着操作系统版本的不同而不同,如图8.1所示,选择同样的“打开”文件菜单项时,在Windows XP下显示的对话框是左边的样子,而在Windows 98下显示的是右边的样子,但程序中并没有为不同版本的操作系统设计不同的对话框。造成这些现象的原因就是这些对话框是操作系统提供的,实现对话框的代码包括在Comdlg32.dll库文件中,由于不同版本的Comdlg32.dll在设计上可能有所不同,所以不同版本Windows下的对话框会有所不同。
图8.1 不同操作系统下的“打开”文件对话框
8.1 通用对话框简介
通用对话框函数由Comdlg32.dll提供,在使用之前需要在源程序中包含相应的include和includelib语句:
include comdlg32.incincludelib comdlg32.lib
Windows提供多种通用对话框,每种通用对话框都使用一个专用的函数来创建和显示,另外,提供一个数据结构供初始化对话框使用,并在同一个数据结构中返回用户在对话框中输入的数据。使用这些对话框是很简单的,只需要填写结构中的初始化数据,然后调用函数并把结构地址传入就可以了。
Comdlg32.dll中提供的通用对话框如表8.1所示,表中还包括创建这些对话框使用的函数,以及数据结构名称。
表8.1 通用对话框列表
在这些对话框中,查找字符串和替换字符串的对话框是非模态的,方便用户在查找到一个字符串以后,不必关闭对话框就可以继续查找另一个字符串,其他的对话框都是模态对话框。通用对话框使用的例子可以参考所附光盘的Chapter08\CommDlg目录中的代码,包括汇编源代码CommDlg.asm和资源脚本文件CommDlg.rc。CommDlg.asm文件的内容如下:
;CommDlg.asm-------------“打开文件”、“打印”、“查找文本”等通用对话框的使用例子
;使用 nmake 或下列命令进行编译和链接:
;ml /c /coff CommDlg.asm
;rc CommDlg.rc
;Link /subsystem:windows CommDlg.obj CommDlg.res
.386
.model flat,stdcall
option casemap:none
;include文件定义
include c:/masm32/include/windows.inc
include c:/masm32/include/user32.inc
includelib c:/masm32/lib/user32.lib
include c:/masm32/include/kernel32.inc
includelib c:/masm32/lib/kernel32.lib
include c:/masm32/include/Comdlg32.inc
includelib c:/masm32/lib/Comdlg32.lib
;equ 等值定义
ICO_MAIN equ 1000
DLG_MAIN equ 1000
IDM_MAIN equ 1000
IDM_OPEN equ 1101
IDM_SAVEAS equ 1102
IDM_PAGESETUP equ 1103
IDM_EXIT equ 1104
IDM_FIND equ 1201
IDM_REPLACE equ 1202
IDM_SELFONT equ 1203
IDM_SELCOLOR equ 1204
;数据段
.data?
hInstance dword ?
hWinMain dword ?
dwFontColor dword ?
dwBackColor dword ?
dwCustColors dword 16 dup(?)
stLogFont LOGFONT <?>
szFileName dword MAX_PATH dup(?)
szBuffer dword 1024 dup(?)
;查找替换对话框使用
idFindMessage dword ?
stFind FINDREPLACE <?>
szFindText byte 100 dup(?)
szReplaceText byte 100 dup(?)
.const
FINDMSGSTRING byte 'commdlg_FindReplace',0
szSaveCaption byte '请输入保存的文件名',0
szFormatColor byte '您选择的颜色值:%08x',0
szFormatFont byte '您的选择:',0dh,0ah,'字体名称:%s',0dh,0ahbyte '字体颜色值:%08x,字体大小:%d',0
szFormatFind byte '您按下了“%s”按钮',0dh,0ah,'查找字符串:%s',0dh,0ahbyte '替换字符串:%s',0
szFormatPrt byte '您选择的打印机:%s',0
szCaption byte '执行结果',0
szFindNext byte '查找下一个',0
szReplace byte '替换',0
szReplaceAll byte '全部替换',0
szFilter byte 'Text Files(*.txt)',0,'*.txt',0,'All Files(*.*)',0,'*.*',0,0
szDefExt byte 'txt',0
;代码段
.code
;页面设置对话框
_PageSetup proc local @stPS:PAGESETUPDLG invoke RtlZeroMemory, addr @stPS, sizeof @stPS mov @stPS.lStructSize, sizeof @stPS push hWinMain pop @stPS.hwndOwner invoke PageSetupDlg, addr @stPS .if eax && @stPS.hDevMode mov eax, @stPS.hDevMode mov eax, [eax]invoke wsprintf, addr szBuffer, addr szFormatPrt, eax invoke MessageBox, hWinMain, addr szBuffer, addr szCaption, MB_OK .endif ret
_PageSetup endp
;显示“保存文件”对话框
_SaveAs proc local @stOF:OPENFILENAME invoke RtlZeroMemory, addr @stOF, sizeof @stOF mov @stOF.lStructSize, sizeof @stOF push hWinMain pop @stOF.hwndOwner mov @stOF.lpstrFilter, offset szFilter mov @stOF.lpstrFile, offset szFileName mov @stOF.nMaxFile, MAX_PATH mov @stOF.Flags, OFN_PATHMUSTEXIST mov @stOF.lpstrDefExt, offset szDefExt mov @stOF.lpstrTitle, offset szSaveCaption invoke GetSaveFileName, addr @stOF .if eax invoke MessageBox, hWinMain, addr szFileName, addr szCaption, MB_OK .endif ret
_SaveAs endp
;显示“打开文件”对话框
_OpenFile proc local @stOF:OPENFILENAME invoke RtlZeroMemory, addr @stOF, sizeof @stOF mov @stOF.lStructSize, sizeof @stOF push hWinMainpop @stOF.hwndOwner mov @stOF.lpstrFilter, offset szFilter mov @stOF.lpstrFile, offset szFileName mov @stOF.nMaxFile, MAX_PATH mov @stOF.Flags, OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST invoke GetOpenFileName, addr @stOF .if eax invoke MessageBox, hWinMain, addr szFileName, addr szCaption, MB_OK .endif ret
_OpenFile endp
;选择颜色
_ChooseColor proc local @stCC:CHOOSECOLOR invoke RtlZeroMemory, addr @stCC, sizeof @stCC mov @stCC.lStructSize, sizeof @stCC push hWinMain pop @stCC.hwndOwner push dwBackColor pop @stCC.rgbResult mov @stCC.Flags, CC_RGBINIT or CC_FULLOPEN mov @stCC.lpCustColors, offset dwCustColors invoke ChooseColor, addr @stCC .if eax push @stCC.rgbResult pop dwBackColor invoke wsprintf, addr szBuffer, addr szFormatColor, dwBackColor invoke MessageBox, hWinMain, addr szBuffer, addr szCaption, MB_OK .endif ret
_ChooseColor endp ;选择字体
_ChooseFont proc local @stCF:CHOOSEFONT invoke RtlZeroMemory, addr @stCF, sizeof @stCF mov @stCF.lStructSize, sizeof @stCF push hWinMain pop @stCF.hwndOwner mov @stCF.lpLogFont, offset stLogFont push dwFontColor pop @stCF.rgbColors mov @stCF.Flags, CF_SCREENFONTS or CF_INITTOLOGFONTSTRUCT or CF_EFFECTS invoke ChooseFont, addr @stCF .if eax push @stCF.rgbColors pop dwFontColor invoke wsprintf, addr szBuffer, addr szFormatFont, addr stLogFont.lfFaceName, \dwFontColor, @stCF.iPointSize invoke MessageBox, hWinMain, addr szBuffer, addr szCaption, MB_OK .endif ret
_ChooseFont endp ;--------------------------------------------------------------------
_ProcDlgMain proc uses ebx edi esi hWnd, wMsg, wParam, lParam local @szBuffer[128]:byte mov eax, wMsg .if eax == WM_CLOSE invoke EndDialog, hWnd, NULL .elseif eax == WM_INITDIALOG ;注册“查找”对话框消息,初始化“查找”对话框的结构mov eax, hWnd mov hWinMain, eax mov stFind.hwndOwner, eax mov stFind.lStructSize, sizeof stFindmov stFind.Flags, FR_DOWN mov stFind.lpstrFindWhat, offset szFindText mov stFind.wFindWhatLen, sizeof szFindTextmov stFind.lpstrReplaceWith, offset szReplaceText mov stFind.wReplaceWithLen, sizeof szReplaceText invoke RegisterWindowMessage, addr FINDMSGSTRING mov idFindMessage, eax .elseif eax == WM_COMMAND mov eax, wParam .if ax == IDM_EXIT invoke EndDialog, hWnd, NULL ;关闭对话框.elseif ax == IDM_OPEN invoke _OpenFile ;打开文件.elseif ax == IDM_SAVEASinvoke _SaveAs ;保存文件 .elseif ax == IDM_PAGESETUP invoke _PageSetup ;页面设置对话框.elseif ax == IDM_FIND and stFind.Flags, not FR_DIALOGTERM invoke FindText, addr stFind ;查找文本 .elseif ax == IDM_REPLACE and stFind.Flags, not FR_DIALOGTERM invoke ReplaceText, addr stFind ;替换字符串:%s.elseif ax == IDM_SELFONT invoke _ChooseFont ;选择字体 .elseif ax == IDM_SELCOLOR invoke _ChooseColor ;选择颜色.endif .elseif eax == idFindMessage xor ecx, ecx .if stFind.Flags & FR_FINDNEXT mov ecx, offset szFindNext .elseif stFind.Flags & FR_REPLACE mov ecx, offset szReplace .elseif stFind.Flags & FR_REPLACEALL mov ecx, offset szReplaceAll .endif .if ecx invoke wsprintf, addr szBuffer, addr szFormatFind, \ecx, addr szFindText, addr szReplaceText invoke MessageBox, hWinMain, addr szBuffer, addr szCaption, MB_OK .endif .else mov eax, FALSE ret .endif mov eax, TRUE ret
_ProcDlgMain endp ;main函数
main proc invoke GetModuleHandle, NULL mov hInstance, eax invoke DialogBoxParam, hInstance, DLG_MAIN, NULL, offset _ProcDlgMain, NULL invoke ExitProcess, 0
main endp
end main
编译运行:
选择子菜单【查找字符串】
在资源脚本文件中定义了一个对话框用做主窗口,同时定义了一个菜单用来选择各种通用对话框。下面结合这个例子说明各种通用对话框的用法。
8.2 使用通用对话框
8.2.1 “打开”文件和“保存”文件对话框
“打开”文件和“保存”文件的对话框大概是读者最熟悉的,如图8.1所示。由于Windows操作系统随着版本的升级会对用户界面的风格做一些调整,不同版本的Comdlg32.dll库文件也随之改变,但不管主流的用户界面如何改变,对程序员来说,编程的接口是不会变的,从Windows 95到Windows XP,通用对话框函数的使用方式并没有什么不同。
显示“打开”文件对话框的函数是GetOpenFileName,显示“保存”文件对话框的函数是GetSaveFileName。这两个对话框可以让用户选择驱动器、目录,以及一个文件名(打开文件对话框还允许选择多个文件),但这两个对话框并不对文件进行任何操作,也就是说,它们仅给用户提供一个统一的界面来“选择”文件名,获取文件名以后,对文件的打开、读写等操作还需要程序员自己解决。
这两个函数的语法是:
invoke GetOpenFileName,lpofn ;显示打开文件对话框invoke GetSaveFileName,lpofn ;显示保存文件对话框
lpofn参数是一个指针,指向一个OPENFILENAME结构,程序在调用函数前需要在结构中填写初始化数据,两个函数使用的结构是一样的,只是使用的初始化数据有所不同而已。OPENFILENAME结构是这样定义的:
OPENFILENAMEA STRUCTlStructSize DWORD ? ;结构的长度,用户填写hwndOwner DWORD ? ;所属窗口,可以为NULLhInstance DWORD ? ;lpstrFilter DWORD ? ;文件筛选字符串lpstrCustomFilter DWORD ?nMaxCustFilter DWORD ?nFilterIndex DWORD ?lpstrFile DWORD ? ;全路径的文件名缓冲区nMaxFile DWORD ? ;文件名缓冲区长度lpstrFileTitle DWORD ? ;不包含路径的文件名缓冲区nMaxFileTitle DWORD ? ;文件名缓冲区长度lpstrInitialDir DWORD ? ;初始目录lpstrTitle DWORD ? ;对话框标题Flags DWORD ? ;标志nFileOffset WORD ? ;文件名在字符串中的起始位置nFileExtension WORD ? ;扩展名在字符串中的起始位置lpstrDefExt DWORD ? ;默认扩展名lCustData DWORD ?lpfnHook DWORD ?lpTemplateName DWORD ?OPENFILENAMEA ENDSOPENFILENAME equ <OPENFILENAMEA>
结构中一些重要的字段含义如下。
● lpstrFilter——指定文件名筛选字符串,该字段决定了对话框中“文件类型”下拉式列表框中的内容,字符串可以由多组内容组成,每组包括一个说明字符串和一个筛选字符串,字符串的最后用两个0结束。如下面的字符串将在列表框中显示两项内容,选择不同项目的时候分别列出“*.txt”文件或者所有文件“*.*”:
'Text Files(*.txt)',0,'*.txt',0,'All Files(*.*)',0,'*.*',0,0
筛选字符串中也可以同时指定多个扩展名,中间用分号隔开,如'*.txt;*.doc'。
● lpstrFile——指向一个包含文件名的缓冲区。如果这个缓冲区中已经包含了一个文件名,那么对话框初始化的时候将显示这个文件名。当用户选择了一个文件的时候,函数在这里返回新的文件名。
● nMaxFile——指定lpstrFile参数指向的缓冲区的长度。
● lpstrFileTitle——指向一个缓冲区,用来接收用户选择的不含路径的文件名。这个字段可以为NULL。
● nMaxFileTitle——指明lpstrFileTitle参数指向的缓冲区的长度。
● lpstrInitialDir——对话框的初始化目录,这个字段可以为NULL。
● lpstrTitle——指向自定义的对话框标题,如果这个字段是NULL,那么“打开”对话框和“保存”对话框的默认标题是“打开”和“另存为”。
● nFileOffset—返回文件名字符串中文件名的起始位置,如当用户选择了文件“c:\dir1\file.ext”时,这里将返回8。
● nFileExtension——返回文件名字符串中扩展名的起始位置,同样是上面的字符串,这里返回13。如果文件名的最后一个字符是“.”,这里返回0,表示文件没有扩展名,这个字段和nFileOffset字段为分析文件名提供了方便。
● lpstrDefExt——指定默认扩展名,如果用户输入了一个没有扩展名的文件名,那么函数会自动加上这个默认扩展名。
● Flags字段——该标志字段决定了对话框的不同行为,它可以是一些取值的组合。下面是一些比较重要的标志:
■ OFN_ALLOWMULTISELECT——允许同时选择多个文件名。
■ OFN_CREATEPROMPT——如果用户输入了一个不存在的文件名,对话框向用户提问“是否建立文件”。
■ OFN_FILEMUSTEXIST——用户只能选择一个已经存在的文件名,使用这个标志的时候必须同时使用OFN_PATHMUSTEXIST标志。
■ OFN_HIDEREADONLY——对话框中不显示“以只读方式打开”复选框。
■ OFN_OVERWRITEPROMPT——在“保存”文件对话框中使用的时候,当选择一个已存在的文件时,对话框会提问“是否覆盖文件”。
■ OFN_PATHMUSTEXIST——用户输入文件名时,路径必须存在。
■ OFN_READONLY——对话框中的“以只读方式打开”复选框初始化的时候处于选中状态。
调用显示“打开”或“保存”文件对话框函数时,函数会停留直到对话框关闭为止,当用户单击了对话框中的“确定”按钮时,函数返回TRUE,用户单击“取消”按钮退出时,函数返回FALSE,程序可以由此判断是否需要继续进行打开或保存文件的操作。具体的代码可以参考例子中的_SaveAs和_OpenFile子程序。
8.2.2 字体选择对话框
“字体”通用对话框如图8.2所示,对话框列出了系统中安装的字体,用户可以在上面选择字体名称,同时可以选择字号大小、颜色,以及一些效果如斜体、粗体、删除线或下划线等,显示选择“字体”对话框的函数是ChooseFont:
图8.2 “字体选择”对话框
invoke ChooseFont,lpcf
lpcf指向一个CHOOSEFONT结构,这个结构是这样定义的:
CHOOSEFONT STRUCTlStructSize DWORD ? ;结构长度hwndOwner DWORD ? ;所属窗口hdc DWORD ?lpLogFont DWORD ? ;指向一个LOGFONT结构iPointSize DWORD ? ;选择的字体大小Flags DWORD ? ;标志rgbColors DWORD ? ;选择的字体颜色lCustData DWORD ?lpfnHook DWORD ?lpTemplateName DWORD ?hInstance DWORD ?lpszStyle DWORD ?nFontType WORD ?Alignment WORD ?nSizeMin DWORD ?nSizeMax DWORD ?CHOOSEFONT ENDS
结构中一些重要的字段含义如下:
● hDC——当Flags字段中指定CF_PRINTERFONTS标志时,它是打印机的DC句柄。
● lpLogFont——指向一个包含LOGFONT结构的缓冲区。LOGFONT结构可以用来指定字体的名称和属性。如果Flags字段中指定CF_INITTOLOGFONTSTRUCT标志的话,对话框将根据这个结构初始化对话框,函数也在这里返回用户选择的字体名称。
● iPointSize——返回用户选择的字号大小,单位是1/10磅。
● rgbColors——如果Flags字段的CF_EFFECTS标志被设置,对话框将根据这个数值初始化“颜色”下拉式列表框。另外,函数返回时在这里返回用户选择的字体颜色。
● nFontType—返回用户选择的字体是属于哪一类的,可能返回的值有BOLD_FONTTYPE,ITALIC_FONTTYPE,PRINTER_FONTTYPE,REGULAR_FONTTYPE和SCREEN_FONTTYPE等。
另一个关键的字段是Flags字段,Flags字段的初始值决定了对话框的不同行为,函数返回的时候也会在这里返回一些用户的选择,它可以是下面取值的组合:
● CF_BOTH——对话框同时列出打印机字体和屏幕字体。
● CF_TTONLY——对话框只列出TrueType字体。
● CF_EFFECTS——对话框中显示“效果”复选框。
● CF_FIXEDPITCHONLY——对话框的字体列表中只显示等宽字体。
● CF_LIMITSIZE——对话框显示的字体尺寸限于nSizeMin和nSizeMax字段指定的数值之间。
● CF_NOSTYLESEL——对话框中不显示“字形”组合列表框。
● CF_NOSIZESEL——对话框中不显示“大小”组合列表框。
● CF_SCREENFONTS——字体列表中只显示屏幕字体。
调用ChooseFont函数时,函数会停留直到对话框关闭为止,当用户单击了对话框中的“确定”按钮时,函数返回TRUE,用户单击“取消”按钮退出时,函数返回FALSE。具体的使用例子可以参考例子中的_ChooseFont子程序。
在调用ChooseFont之前,lpLogFont字段被指向一个LOGFONT结构,对话框关闭的时候,函数在LOGFONT结构的lfFaceName字段中返回字体的名称,字体的效果和字形也在LOGFONT结构中返回。
用户选择的颜色在rgbColors字段中返回,字号大小在iPointSize字段中返回,由于单位是1/10磅,所以返回的数值等于对话框中选择的字号大小乘以10。
8.2.3 “颜色”选择对话框
“颜色”选择对话框如图8.3所示,左边是基本的选择系统预定义颜色的区域,右边是扩展的区域,可以由用户自己选择或输入颜色值。
图8.3 “颜色”选择对话框
打开“颜色”选择对话框使用函数ChooseColor:
invoke ChooseColor,lpcc
lpcc指向一个CHOOSECOLOR结构,这个结构是这样定义的:
CHOOSECOLOR STRUCTLStructSize DWORD ? ;结构长度HwndOwner DWORD ? ;所属窗口HInstance DWORD ?rgbResult DWORD ? ;用户选择的颜色值lpCustColors DWORD ? ;用户自定义颜色缓冲区Flags DWORD ? ;标志lCustData DWORD ?lpfnHook DWORD ?lpTemplateName DWORD ?CHOOSECOLOR ENDS
结构中几个重要的字段说明如下。
● rgbResult——如果Flags字段指定了CC_RGBINIT标志,那么创建对话框的时候使用这个字段来初始化选择框中的颜色;函数返回的时候在这里返回用户选择的颜色。
● lpCustColors——指向一个16个双字长度的缓冲区,定义16种自定义颜色。
● Flags——标志,可以是下面取值的组合:
■ CC_FULLOPEN——对话框显示右边的扩展部分,如果不指定这个标志,初始化的时候扩展部分不显示,但用户可以通过单击“规定自定义颜色”按钮将对话框展开。
■ CC_PREVENTFULLOPEN——禁止“规定自定义颜色”按钮,也就是说不允许用户展开对话框的扩展部分。
■ CC_RGBINIT——对话框显示的时候用rgbResult字段的值初始化选择框中的颜色。
如果用户单击“确定”按钮,函数返回TRUE,否则函数返回FALSE。读者可以在例子文件中的_ChooseColor子程序中找到使用这个函数的详细代码。
使用ChooseColor函数要注意的是:lpCustColors指针不能为NULL,所以必须分配一个16个双字长度缓冲区,如果指针是NULL会导致函数违规访问00000000h处的内存,就等着看“非法操作”吧!
8.2.4 “查找”和“替换”文本对话框
“查找”文本对话框如图8.4的下图所示,“替换”文本对话框如图8.4的上图所示,要显示这两种通用对话框可分别使用FindText和ReplaceText函数:
图8.4 “查找”和“替换”对话框
invoke FindText,lpfr
invoke ReplaceText,lpfr
这两个函数都使用同样的FINDREPLACE结构:
FINDREPLACEA STRUCTlStructSize DWORD ? ;结构长度hwndOwner DWORD ? ;所属窗口hInstance DWORD ?Flags DWORD ? ;标志LpstrFindWhat DWORD ? ;查找字符串lpstrReplaceWith DWORD ? ;替换字符串wFindWhatLen WORD ? ;查找字符串长度wReplaceWithLen WORD ? ;替换字符串长度lCustData DWORD ?lpfnHook DWORD ?lpTemplateName DWORD ?FINDREPLACEA ENDSFINDREPLACE equ <FINDREPLACEA>
结构中关键的字段说明如下。
● Flags——标志,创建对话框的时候,函数根据标志中的数值初始化对话框中各控件的状态,返回的时候根据用户的选择来设置标志字段的内容,标志字段可以是以下取值的组合:
■ FR_FINDNEXT,FR_REPLACE,FR_REPLACEALL,FR_DIALOGTERM——分别表示用户单击了“查找下一个”、“替换”、“全部替换”和“取消”按钮。
■ FR_HIDEUPDOWN,FR_HIDEMATCHCASE与FR_HIDEWHOLEWORD——初始化时使用,表示对话框中不显示“方向”、“区分大小写”与“全字匹配”按钮。
■ FR_NOMATCHCASE,FR_NOUPDOWN与FR_NOWHOLEWORD——初始化时将“区分大小写”、“方向”与“全字匹配”按钮灰化。
■ FR_MATCHCASE或FR_WHOLEWORD——表示用户选中了“区分大小写”或“全字匹配”复选框。
■ FR_DOWN——把“方向”单选钮设置为“向下”。
● lpstrFindWhat——指向包含查找字符串的指针,缓冲区的长度必须至少为80字节,这个字符串在初始化的时候出现在“查找内容”编辑框中,函数也在这里返回用户输入的内容。
● lpstrReplaceWith——指向包含替换字符串的指针,这个字符串在初始化的时候出现在“替换为”编辑框中,函数也在这里返回用户输入的内容。这个字段在使用FindText函数的时候可以为NULL,但在使用ReplaceText函数的时候必须设置,否则对话框不会显示。
● wFindWhatLen和wReplaceWithLen——lpstrFindWhat和lpstrReplaceWith指示缓存区的长度。
“查找”和“替换”对话框的使用有些特殊,因为这两种对话框是非模态对话框,也就是说,FindText和ReplaceText函数被调用后,系统显示对话框后马上返回,对话框保持显示状态,直到用户按下了“取消”按钮后对话框才关闭。如果用户按下了对话框中的某个按钮,对话框设置FINDREPLACE结构的相关字段并通过自定义的消息通知父窗口的窗口过程,程序中处理查找和替换的功能集中在这个自定义消息中完成。另外,由于对话框必须向父窗口发送消息,所以hwndOwner字段中必须指定父窗口的句柄,而不能像其他通用对话框一样可以把hwndOwner字段设置为NULL。
为了让对话框能够使用自定义消息,程序必须首先使用RegisterWindowMessage函数注册自定义消息,这个函数注册消息并返回消息ID,输入的参数是消息名称字符串,Microsoft的编程手册中说明要为查找和替换对话框注册FINDMSGSTRING消息,但没有任何资料说明FINDMSGSTRING究竟代表什么,是代表消息名称字符串为“FINDMSGSTRING”吗?不是,实际上它代表字符串“commdlg_FindReplace”,所以,在对话框的初始化消息中如下注册消息:
idFindMessage dd ?FINDMSGSTRING db 'commdlg_FindReplace',0...invoke RegisterWindowMessage,addr FINDMSGSTRINGmov idFindMessage,eax
注意:要把RegisterWindowMessage函数返回的消息ID保存下来,并在主窗口的消息循环中通过判断uMsg是否等于这个消息ID来判断对话框是否发回消息:
mov eax,uMsg.if eax == WM_XXX....elseif eax == idFindMessage.if stFind.Flags & FR_DIALOGTERM;用户按下了"取消"按钮,对话框关闭.elseif stFind.Flags & FR_FINDNEXT;用户按下了"查找下一个"按钮.elseif stFind.Flags & FR_REPLACE;用户按下了"替换"按钮.elseif stFind.Flags & FR_REPLACEALL;用户按下了"全部替换"按钮.endif
由于用户按下了“查找下一个”、“替换”、“全部替换”和“取消”等按钮时,对话框都要发回消息,所以在处理时还要根据不同的标志进行不同的处理。
因为查找和替换对话框是非模态对话框,所以使用时要把FINDREPLACE结构和字符串变量放在全局变量中,如果放在窗口过程的局部变量中,对话框还没有关闭的情况下,这些局部变量就已经被释放,以后对话框存取的就会是无效的地址。
8.2.5 “页面设置”对话框
“页面设置”对话框用来设置打印机参数,用户可以在对话框中选择打印机、打印纸张大小、页边距和纸张方向,还可以引出一个打印机属性的设置对话框。用户可以在这里完成与打印有关的所有设置工作,页面设置对话框如图8.5所示。
图8.5 “页面设置”对话框
显示“页面设置”对话框使用PageSetupDlg函数:
invoke PageSetupDlg,lppsd
lppsd参数指向一个PAGESETUPDLG结构:
PAGESETUPDLG STRUCTLStructSize DWORD ? ;结构长度HwndOwner DWORD ? ;所属窗口hDevMode DWORD ? ;指向DEVMODE结构hDevNames DWORD ? ;指向DEVNAMES结构Flags DWORD ? ;标志PtPaperSize POINT <> ;返回纸张尺寸RtMinMargin RECT <> ;返回最小允许的页边距rtMargin RECT <> ;返回用户选择的页边距hInstance DWORD ?lCustData DWORD ?lpfnPageSetupHook DWORD ?lpfnPagePaintHook DWORD ?lpPageSetupTemplateName DWORD ?hPageSetupTemplate DWORD ?PAGESETUPDLG ENDS
结构中的有关字段说明如下。
● hDevMode——如果用户选择了打印机,那么这里返回一个指针,指向包含DEVMODE结构的内存块地址,DEVMODE结构中包含了打印机的名称。
● hDevNames——如果用户选择了打印机,那么这里返回一个指针,指向包含DEVNAMES结构的内存块的地址,DEVNAMES结构包含了打印机的各种属性。
● Flags——标志,可以是以下取值的组合:
■ PSD_DEFAULTMINMARGINS——将页边距设置为打印机允许的最小页边距。
■ PSD_DISABLEMARGINS,PSD_DISABLEORIENTATION和PSD_DISABLEPA PER——灰化页边距设置输入框、纸张方向选择框和纸张选择框。
■ PSD_DISABLEPAGEPAINTING——不绘画最上方的打印示例图示。
■ PSD_DISABLEPRINTER——灰化“打印机”按钮。
■ PSD_MARGINS——函数用rtMargin字段的值初始化对话框中的数值。
■ PSD_RETURNDEFAULT——函数不显示对话框,马上返回并在hDevNames和hDevMode字段中返回默认打印机的设置情况。
■ PSD_INTHOUSANDTHSOFINCHES和PSD_INHUNDREDTHSOFMILLIMETE RS——指明ptPagerSize,rtMinMargin与rtMargin等字段使用的单位是英寸还是毫米。
● ptPaperSize——一个POINT结构,返回纸张大小。
● rtMinMargin——打印机允许的最小页边距。
● rtMargin——用户选择的页边距数据。
当用户选择了打印机时,hDevMode中返回的是一个指向内存块的指针,所以需要用下面的代码获取DEVMODE结构的地址:
mov eax,@stPS.hDevMode
mov eax,[eax] ;现在eax是DEVMODE结构的地址
总结通用对话框的使用方法可以发现,每种通用对话框函数都使用一个特定的结构来当做输入输出的缓冲区,初始化的时候函数根据结构中的数据和标志设置对话框中的子窗口控件,返回的时候在结构的相应位置返回用户的输入或选择。另外,所有结构都有几个类似的字段,如lStructSize字段必须设置为正确的结构长度;hwndOwner指定对话框的父窗口,模态对话框在关闭之前是不允许切换到这个父窗口中去的。
8.2.6 “浏览目录”对话框
在众多的系统提供的对话框中,还有一个很常用的浏览文件夹对话框,对话框如图8.6所示,这个对话框虽然也是通用型的,但是它是由Shell32.dll提供的,而不是由Comdlg32.dll提供的,在实现的方法上也和上面介绍的通用对话框有很大的不同,由于篇幅较大,有关该对话框的详细介绍放在附录C中(以电子版方式放在附书光盘中),有兴趣的读者可以自行阅读。
图8.6 “浏览文件夹”对话框