在桌面显示一个窗口的步骤:
- 得到您应用程序的句柄(必需);
- 得到命令行参数(如果您想从命令行得到参数,可选);
- 注册窗口类(必需,除非您使用 Windows 预定义的窗口类,如 MessageBox 或 dialog box;
- 产生窗口(必需);
- 在桌面显示窗口(必需,除非您不想立即显示它);
- 刷新窗口客户区;
- 进入无限的获取窗口消息的循环;
- 如果有消息到达,由负责该窗口的窗口回调函数处理;
- 如果用户关闭窗口,进行退出处理。
.386 ;告诉编译器程序使用80386指令集编写的
.model flat,stdcall ;告诉MASM 我们用的内存寻址模式,此处也可以加入stdcall告诉MASM我们所用的参数传递约定
option casemap:none ;告诉编译器要区分标号的大小写
include windows.inc ; windows.inc 包含了 WIN32 编程所需要的常量和结构体的定义。 但是它不包含函数原型的定义
include kernel32.inc ;函数原型声明的头文件 kernel.inc
includelib kernel32.lib ;仅仅是告诉编译器您的程序引用了哪个库
include user32.inc
includelib user32.lib
WinMain proto:DWORD,:DWORD,:DWORD,:DWORD
.data
ClassName db "SimpleWinClass",0 ;Windows 类名
AppName db "Our First Window",0 ;窗口的名字
.data? ;存放未进行初始化的变量
hInstance HINSTANCE ? ;代表应用程序的句柄
CommandLine LPSTR ? ;保存从命令行传入的参数
.code
start:
invoke GetModuleHandle,NULL ;查找我们应用程序的句柄,可以把实例句柄看成是您的应用程序的 ID 号,特别注意:WIN32下的实例句柄实际上是您应用程序在内存中的线性地址。WIN32 中函数的函数如果有返回值,那它是通过 eax 寄存器来传递的
mov hInstance,eax
invoke GetCommandLine ;如果您的应用程序不处理命令行那么就无须调用 GetCommandLine,这里只是告诉您如果要调用应该怎么做
mov CommandLine,eax
invoke WinMain,hInstance,NULL,CommandLine,SW_SHOWDEFAULT
invoke ExitProcess,eax
WinMain proc hInst:HINSTANCE,hPreInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX ;伪指令为局部变量在栈中分配内存空间,所有的 LOCAL 指令必须紧跟在 PROC 之后。LOCAL 后跟声明的变量,其形式是 变量名:变量类型。
LOCAL msg:MSG ;譬如 LOCAL wc:WNDCLASSEX 即是告诉 MASM 为名字叫 wc 的局部边量在栈中分配长度为 WNDCLASSEX 结构体长度的内存空间,然后我们在用该局部变量是无须考虑堆栈的问题
LOCAL hwnd:HWND
mov wc.cbSize,SIZEOF WNDCLASSEX ;窗口类(window class),一个窗口类就是一个有关窗口的规范,这个规范定义了几个主要的窗口的元素,如:图标、光标、背景色、和负责处理该窗口的函数。您产生一个窗口时就必须要有这样的一个窗口类
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,NULL
push hInstance
pop wc.hInstance
mov wc.hbrBackground,COLOR_WINDOW+1
mov wc.lpszMenuName,NULL
mov wc.lpszClassName,OFFSET ClassName
invoke LoadIcon,NULL,IDI_APPLICATION
mov wc.hIcon,eax
mov wc.hIconSm,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor,eax
invoke RegisterClassEx, addr wc ;注册窗口类
invoke CreateWindowEx,NULL,/ ;注册窗口类后,我们将调用CreateWindowEx来产生实际的窗口。请注意该函数有12个参数
ADDR ClassName,/
ADDR AppName,/
WS_OVERLAPPEDWINDOW,/
CW_USEDEFAULT,/
CW_USEDEFAULT,/
CW_USEDEFAULT,/
CW_USEDEFAULT,/
NULL,/
NULL,/
hInst,/
NULL
mov hwnd,eax ;调用CreateWindowEx成功后,窗口句柄在eax中。我们必须保存该值以备后用。我们刚刚产生的窗口不会自动显示,所以必须调用 ShowWindow 来按照我们希望的方式来显示该窗口。接下来调用 UpdateWindow 来更新客户区。
invoke ShowWindow, hwnd,CmdShow
invoke UpdateWindow, hwnd
.WHILE TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg ;TranslateMessage 函数是一个是实用函数,它从键盘接受原始按键消息,然后解释成 WM_CHAR,在把 WM_CHAR 放入消息队列,由于经过解释后的消息中含有按键的 ASCII 码,这比原始的扫描码好理解得多。如果您的应用程序不处理按键消息的话,可以不调用该函数
invoke DispatchMessage, ADDR msg ;DispatchMessage 会把消息发送给负责该窗口过程的函数
.endw
mov eax,msg.wParam
ret
WinMain endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM ;窗口处理函数
.IF uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc endp
end start