本课程中我们将进一步学习对话框。特别地我们将要探讨如何把对话框当成输入设备。如果您学了上节课,那就会发现本课的例子只有少量的改动,就是把我们的对话框窗口附属到主窗口上。另外我们还要学习通用对话框的用法。
理论:
把对话框当成一个输入设备来用确实非常简单,创建完主窗口后,您只要调用DialogBoxParam和CreateDialogParam就可以了,前一个函数只要在对话框的过程处理函数中处理相关的消息就可以了,二后者您必须调用IsDialogMessage的调用让它来处理键盘的逻辑按键。因为这两个程序段来说比较容易,我们就不详解。您可以下载并仔细研究。
下面我们来讨论通用对话框。windows已经给您准备好了预定义的类,您直接就可以拿来用,这些通用对话框提供给用户以统一的界面。它们包括:打开文件、打印、选择颜色、字体和搜索等。您应该尽可能地用它们。处理这些对话框的代码放在comdlg32.dll中,为了在您的应用程序中使用它们,就必须在编译器链接的时候在输入表中添加comdlg32.dll以及相关代码。然后调用其中的相关函数即可。对于”打开文件”通用对话框,该函数名为GetOpenFileName,“保存为”通用对话框,该函数名为GetSaveFileName,打印通用对话框PrintDlg,等等。每一个这样的函数都接受一个指向结构体的指针的参数,您可以参考win api手册得到更详细的资料,本课中我将讲解创建和使用打开文件对话框。
下面是打开对话框的原型
proc GetOpenFileName lpofn:DWORD
您可以看到该函数只有一个参数,及指向结构体OPENFILENAME的指针。当用户选择一个文件打开,该函数返回True,否则返回False。接下来我们来看看OPENFILENAME结构的定义。
STRUCT OPENFILENAME
lStructSize DWORD ?
hwndOwner HWND ?
hInstance HINSTANCE ?
lpstrFilter LPCSTR ?
lpstrCustomFilter LPSTR ?
nMaxCustFilter DWORD ?
nFilterIndex DWORD ?
lpstrFile LPSTR ?
nMaxFile DWORD ?
lpstrFileTitle LPSTR ?
nMaxFileTitle DWORD ?
lpstrInitialDir LPCSTR ?
lpstrTitle LPCSTR ?
Flags DWORD ?
nFileOffset WORD ?
nFileExtension WORD ?
lpstrDefExt LPCSTR ?
lCustData LPARAM ?
lpfnHook DWORD ?
lpTemplateName LPCSTR ?
ENDS
好接下来我们来看下结构体成员的含义:
lStructSize 结构体OPENFILENAME的大小。
hwndOwner 拥有打开对话框的窗口的句柄。
hInstance 拥有该打开文件对话框的应用程序的实例句柄 。
lpstrFilter 以NULL结尾的一个或多个通配符。通配符是成对出现的,前一部分是描述部分,后一部分则是通配符的格式,譬如:
FilterString db "All Files (*.*)",0, "*.*",0
db "Text Files (*.txt)",0,"*.txt",0,0
注意:只有每一对中的第二部分是WINDOWS用来过滤所需选择的文件的,另外您必须在该部分后放置一个0,以示字符串的结束。
nFilterIndex 用来指定打开文件对话框第一次打开时所用的过滤模式串,该索引是从1开始算的,即第一个通配符模式的索引是1,第二个是2,譬如上面的例子中,若指定该值为2,则缺省显示的模式串就是"*.txt"。
lpstrFile 需要打开的文件的名称的地址,该名称将会出现在打开文件对话框的编辑控件中,该缓冲区不能超过260个字符长,当用户打开文件后,该缓冲区中包含该文件的全路径名,您可以从该缓冲区中抽取您所需要的路径或文件名等信息。
nMaxFile lpstrFile的大小。
lpstrTitle 指向对话框标题的字符串。
Flags 该标志决定决定了对话框的风格和特点。
nFileOffset 在用户打开了一个文件后该值是全路径名称中指向文件名第一个字符的索引。譬如:若全路径名为"c:/windows/system/lz32.dll", 则该值为18。
nFileExtension 在用户打开了一个文件后该值是全路径名称中指向个文件扩展名第一个字符的索引。
例子:
format PE GUI 4.0
include 'win32ax.inc'
macro memmov [dst, src]
{
common
push [src]
pop [dst]
}
IDM_TEST equ 1
IDM_OPEN equ 2
IDM_GOODBYE equ 3
IDM_EXIT equ 4
.data
;**************数据********************
szClassName db 'first Window',0
szWndName db 'My first program',0
szMenuName db 'MyMenu',0
szTestString db 'you selected test menu item', 0
HelloString db 'you selected hello menu item', 0
goodstring db 'you selected goodbyte menu item',0
FilterString db 'all Files',0, '*.*',0,0
OurTitle db 'Our First Open File Dialog Box',0
FullPathName db 'The Full FileName with path is:',0
FullName db 'The Full FileName is: ',0
buffer db 256 dup (?)
OutputString db 512 dup (?)
lpofn OPENFILENAME <>
newline db 0Dh,0Ah,0
hInstanse rd 1
hIcon rd 1
hCursor rd 1
hWndow rd 1
lpCommand rd 1
.code
entry $
xor edi, edi
invoke GetModuleHandle, edi
or eax, eax
jz .fail
mov [hInstanse], eax
invoke GetCommandLine,edi
mov [lpCommand], eax
stdcall _WndMain, [hInstanse], edi, [lpCommand],SW_SHOWDEFAULT
.fail:
invoke ExitProcess,NULL
proc _WndMain hInstance:DWORD, hPrevInstance:DWORD, lpCmdLine:DWORD, nCmdShow:DWORD
local @wc:WNDCLASSEX
local @Msg:MSG
invoke RtlZeroMemory, addr @wc, sizeof.WNDCLASSEX
invoke LoadIcon, NULL,IDI_ASTERISK
mov [hIcon], eax
invoke LoadCursor, NULL,IDC_ARROW
mov [hCursor], eax
mov [@wc.cbSize], sizeof.WNDCLASSEX
mov [@wc.style], CS_HREDRAW or CS_VREDRAW
mov [@wc.lpfnWndProc], _WndProc
mov [@wc.cbClsExtra], NULL
mov [@wc.cbWndExtra], NULL
memmov @wc.hInstance, hInstance
memmov @wc.hIcon, hIcon
memmov @wc.hCursor, hCursor
mov [@wc.hbrBackground], COLOR_WINDOW + 1
mov [@wc.lpszMenuName], szMenuName
mov [@wc.lpszClassName], szClassName
invoke RegisterClassEx, addr @wc
invoke CreateWindowEx, NULL, szClassName, szWndName, WS_OVERLAPPEDWINDOW,/
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,/
NULL, NULL , [hInstanse] , NULL
mov [hWndow], eax
invoke ShowWindow,[hWndow],SW_SHOWNORMAL
invoke UpdateWindow,[hWndow]
.GetMsg:
invoke GetMessage,addr @Msg,NULL, 0, 0
or eax, eax
je .QUIT
invoke TranslateMessage,addr @Msg
invoke DispatchMessage,addr @Msg
jmp .GetMsg
.QUIT:
mov eax, [@Msg.wParam]
ret
endp
proc _WndProc uses ebx esi edi, hWnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
cmp [uMsg], WM_DESTROY
je finish
cmp [uMsg], WM_COMMAND
je .command
invoke DefWindowProc,[hWnd],[uMsg],[wParam], [lParam]
ret
.command:
mov eax ,[wParam]
cmp ax, IDM_TEST
je _test
cmp ax, IDM_OPEN
je _open
cmp ax, IDM_GOODBYE
je _good
cmp ax, IDM_EXIT
je _exit
jmp Myend
_test:
invoke MessageBox,NULL, szTestString, '提示', MB_OK
jmp Myend
_open:
mov [lpofn.lStructSize], sizeof.OPENFILENAME
memmov lpofn.hwndOwner, hWnd
memmov lpofn.hInstance, hInstanse
mov [lpofn.lpstrFilter], FilterString
mov [lpofn.lpstrFile], buffer
mov [lpofn.nMaxFile], 256
mov [lpofn.Flags], OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST or /
OFN_EXPLORER or OFN_HIDEREADONLY
mov [lpofn.lpstrTitle], OurTitle
invoke GetOpenFileName, lpofn
or eax, eax
je Myend
invoke lstrcat,OutputString , FullPathName
invoke lstrcat,OutputString , [lpofn.lpstrFile]
invoke lstrcat,OutputString , newline
invoke lstrcat,OutputString , FullName
mov eax, [lpofn.lpstrFile]
push ebx
xor ebx, ebx
mov bx, [lpofn.nFileOffset]
add eax, ebx
pop ebx
invoke lstrcat,OutputString, eax
invoke MessageBox,NULL, OutputString , 'test', MB_OK
invoke RtlZeroMemory,OutputString, 512
jmp Myend
_good:
invoke MessageBox,NULL, goodstring, '提示', MB_OK
jmp Myend
_exit:
invoke DestroyWindow, [hWnd]
jmp Myend
finish:
invoke PostQuitMessage, NULL
Myend:
xor eax, eax
ret
endp
.import
library kernel32, 'kernel32.dll',/
user32, 'user32.dll',/
comdlg32, 'comdlg32.dll'
include 'api/kernel32.inc'
include 'api/user32.inc'
include 'api/comdlg32.inc'
section '.rsrc' data readable resource from 'RESOURCENAME.RES'
分析:
mov [lpofn.lStructSize], sizeof.OPENFILENAME
memmov lpofn.hwndOwner, hWnd
memmov lpofn.hInstance, hInstanse
mov [lpofn.lpstrFilter], FilterString
mov [lpofn.lpstrFile], buffer
mov [lpofn.nMaxFile], 256
mov [lpofn.Flags], OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST or /
OFN_EXPLORER or OFN_HIDEREADONLY
mov [lpofn.lpstrTitle], OurTitle
我们在此填充结构体OPENFILENAME变量lpofn的成员。
mov [lpofn.lpstrFilter], FilterString
这里FilterString是文件过滤的字符串地址,我们指定的过滤模式如下:
FilterString db 'all Files',0, '*.*',0,0
注意:所有的模式串都是配对的,后一个才是真正的模式,此处“*.*”是windows用来寻找匹配欲打开的文件的。我们当然可以指定任何模式,但是不要忘记在结尾处加0以表示字符串结束,否则你的对话框可能不稳定。
mov [lpofn.lpstrFile], buffer
mov [lpofn.nMaxFile], 256
我们把缓冲区的地址放到结构中,并设置大小。
mov [lpofn.Flags], OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST or /
OFN_EXPLORER or OFN_HIDEREADONLY
flags中放入的是对话框的风格和特性值。
其中OFN_FILEMUSTEXIST 和OFN_PATHMUSTEXIST 要求用户在选中的文件时 文件的路径和文件名必须存在。
OFN_EXPLORER告诉windows对话框的外观必须类似对话框。
OFN_HIDEREADONLY 指定不要显示只读文件(既使它的扩展名符合过滤模式)。
除此之外,还有很多其他的标志,您可以参考win32 api手册。
mov [lpofn.lpstrTitle], OurTitle
指定打开文件对话框的标题。
invoke GetOpenFileName, lpofn
调用GetOpenFileName函数,并传入指向结构体的指针。
这时候打开文件对话框就显示出来了,GetOpenFileName函数一直等到用户选择了一个文件后才返回,或者当用户按下CANCAL键或关闭对话框时。
当用户选择了打开一个文件时,该函数返回true,否则返回false。
or eax, eax
je Myend
判断如果eax为0,则表示false我们则跳到窗口过程退出的标号处退出。
invoke lstrcat,OutputString , FullPathName
invoke lstrcat,OutputString , [lpofn.lpstrFile]
invoke lstrcat,OutputString , newline
invoke lstrcat,OutputString , FullName
我们通过调用lstrcat函数将其字符串连接起来。首先将我们之前声明的一段字符字符串连接起来,然后将我们lpofn.lpstrFile指向的缓冲区连接起来,这个缓冲区保存的就是我们通过打开文件对话框打开的文件的路径,然后我们需要分行显示我们的文件名,所以我们通过设置一个回车换行键,大家可以自己查ASCII码表,然后我们在将其字符串连接起来。
mov eax, [lpofn.lpstrFile]
push ebx
xor ebx, ebx
mov bx, [lpofn.nFileOffset]
add eax, ebx
pop ebx
invoke lstrcat,OutputString, eax
invoke MessageBox,NULL, OutputString , 'test', MB_OK
我们看这里nFileOffset保存的是打开的文件路径的文件名第一个字母的偏移,所以我们通过我们的保存文件路径的缓冲区的地址+这个偏移地址,这样显示的则是从文件名开始显示的。
最后我们通过MessageBox将其文件路径和文件名显示出来。
等用户点击完确定按钮。
invoke RtlZeroMemory,OutputString, 512
用0填充缓冲区以便下一次正确显示。