[32位汇编系列]002 - 创建标准的windows窗口(2)
2009-07-13 23:52
375 查看
接着上面一篇文章
代码跟上个例子相比,
长了很多,这个例子中用了大量的
windows api函数,
下面我们来慢慢的解释:
这里面多了一个
gdi32.inc和
gdi32.lib,
这里要用
windows的画图函数,所以要用到这个库和头文件
start:
...
end start
这个
start:表示一个标签,
后面有一个伪指令
end start, 这告诉编译器,
程序从
start这个标签的位置开始运行,
同时到
end start 这个位置结束,也就是说:
end 标签
这个伪指令表明了程序的开始和结束位置
call _WinMain
表示调用自定义过程
_WinMain,相当于
windows编程中的
WinMain函数,
这里换成伪指令
invoke也是一样,
即:
call _WinMain 和
invoke
_WinMain等价,
实际上经过我的测试
call _WinMain
invoke _WinMain
invoke offset _WinMain
invoke addr _WinMain
call offset _WinMain
都是正确的,
注意,
addr 伪指令只能和
invoke配合使用,不能这样写:
call addr _WinMain
_WinMain proc
....
_WinMain endp
定义一个过程,不带参数,
过程的名字可以任意,
这里的过程类似
c语言的函数
程序从这里开始运行,
我们来主意看看每行代码的含义:
local
@stWC:WNDCLASSEX
local @stMsg:MSG
定义
2个局部变量,
local表示局部变量的意思,
这个是局部变量定义伪指令
局部变量的名称分别为
@stWC和
@stMsg,类型分别为结构
WNDCLASSEX和
MSG
在汇编中,
根据个人习惯的不同,
各种变量以及标识符的写法不一样,但是必须遵循以下原则:
1. 可以用字母、数字、下划线以及符号
$、
@和
?
2. 第一个符号不能是数字
3. 长度不能超过
240个字符
4. 不能使用汇编关键字
5. 在作用域内必须唯一,这个跟高级语言的作用域是一样的
invoke
GetModuleHandle, NULL
mov hInstance, eax
调用
GetModuleHandle函数,
获取模块句柄,如果参数是
NULL,
则获取当前模块句柄
获取句柄后,
将句柄值存入全局变量
hInstance,
注意:
汇编语言中,所有函数调用的返回值都将放在
eax中
invoke
RtlZeroMemory, addr @stWC, sizeof @stWC
调用
RtlZeroMemory函数,将结构
@stWC填充,
填充大小为
sizeof @stWC
WNDCLASSEX结构在
msdn中定义如下:
typedef struct _WNDCLASSEX {
UINT cbSize;
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCTSTR lpszMenuName;
LPCTSTR lpszClassName;
HICON hIconSm;
} WNDCLASSEX, *PWNDCLASSEX;
cbSize
为本结构的大小,
必须填充为
sizeof WNDCLASSEX
style
为窗口风格,
典型设置为
CS_VREDRAW or CS_HREDRAW,
or表示或,
取两者的组合
lpfnWndProc 为窗口过程,
窗口过程的原型必须符合下列形式【过程名任意】:
WndProc Proto :dword, :dword, :dword, :dword
cbClsExtra 窗口类附加数据,
通常为
0
cbWndExtra 窗口附加数据,通常为
0
hInstance 当前实例句柄,
可以通过
GetModuleHandle获取
hIcon
程序图标
hCursor
光标
hbrBackground 窗口背景色
lpszMenuName 菜单名称,这里没有菜单,为
NULL
lpszClassName 窗口类名称
hIconSm
小图标
mov @stWC.cbSize, sizeof WNDCLASSEX
mov @stWC.style, CS_HREDRAW or CS_VREDRAW
mov @stWC.lpfnWndProc, offset _WinProc
push hInstance
pop @stWC.hInstance
invoke LoadIcon, NULL, IDI_APPLICATION
mov @stWC.hIcon, eax
mov @stWC.hIconSm, eax
invoke LoadCursor, NULL, IDC_ARROW
mov @stWC.hCursor, eax
invoke GetStockObject, BLACK_BRUSH
mov @stWC.hbrBackground, eax
mov @stWC.lpszClassName, offset szAppName
这段代码填充
WNDCLASSEX结构,
push hInstance
pop @stWC.hInstance
相当于
mov @stWC.hInstance,
hInstance,
但是这条指令是错误的,不能直接在
2个内存变量之间传值,需要借助一个寄存器间接传值,
可以写成如下形式
:
mov eax, hInstance
mov @stWC.hInstance, eax
invoke LoadIcon, NULL, IDI_APPLICATION 获取程序的默认图标
invoke LoadCursor, NULL, IDC_ARROW 获取默认光标
invoke GetStockObject, BLACK_BRUSH 获取一个黑色的画刷,用于将窗口背景画成黑色
invoke
RegisterClassEx, addr @stWC
.if !eax
invoke MessageBox,
NULL, offset szErrorText, offset szError, MB_OK or MB_ICONERROR
ret
.endif
注册窗口类,
如果注册失败,
则弹出一个提示对话框,
并且退出程序
invoke
CreateWindowEx, /
WS_EX_CLIENTEDGE,
/
offset
szAppName, /
offset
szAppName,
/
WS_OVERLAPPEDWINDOW,
/
CW_USEDEFAULT,
/
CW_USEDEFAULT,
/
CW_USEDEFAULT,
/
CW_USEDEFAULT,
/
NULL,
/
NULL,
/
hInstance,
/
NULL
.if !eax
invoke MessageBox,
NULL, offset szErrCreateWnd, offset szError, MB_OK or MB_ICONERROR
ret
.endif
mov hWinMain, eax
invoke ShowWindow, hWinMain, SW_SHOWNORMAL
invoke UpdateWindow, hWinMain
创建一个标准的
windows窗口,
CreateWindowEx在
msdn中定义如下:
HWND CreateWindowEx(
DWORD dwExStyle, // extended window style
LPCTSTR lpClassName, // registered class name
LPCTSTR lpWindowName, // window name
DWORD dwStyle, // window style
int
x,
// horizontal position of window
int
y,
// vertical position of window
int nWidth,
// window width
int nHeight, //
window height
HWND hWndParent, // handle to parent or
owner window
HMENU hMenu, //
menu handle or child identifier
HINSTANCE hInstance, // handle to application instance
LPVOID lpParam //
window-creation data
);
dwExStyle
窗口扩展风格,
这里用了
WS_EX_CLIENTEDGE,
表示下沉的边框
lpClassName 注册的窗口类名称,必须和
RegisterClassEx中用到的类名称一样
lpWindowName 窗口标题
dwStyle
窗口风格,通常为
WS_OVERLAPPEDWINDOW表示一个层叠的窗口
x, y, nWidth, nHeight 表示窗口的位置和大小,这里全部默认为
CW_USEDEFAULT
hWndParent 父窗口,
这里没有,
设置为
NULL
hMenu
窗口菜单,
这里没有,
设置为
NULL
hInstance
当前实例句柄
lpParam
附加数据,
通常不用,
设置为
NULL
如果窗口注册失败,
则弹出一个错误提示对话框,并且退出程序,否则把创建的窗口句柄保存到全局变量
hWinMain中
窗口创建成功后,
调用
ShowWindow显示窗口,
然后必须再调用
UpdateWindow发送一个
WM_PAINT消息,让程序重画窗口,
这样你才能看到窗口
.while TRUE
invoke GetMessage, addr
@stMsg, NULL, 0, 0
.break .if !eax
invoke
TranslateMessage, addr @stMsg
invoke DispatchMessage,
addr @stMsg
.endw
这里是一个消息循环,
程序不断的用
GetMessage获取消息,
如果
GetMessage返回值为
0,
表示退出程序,于是
.break .if !eax 退出循环,
整个程序结束运行
TranslateMessage 表示翻译消息,
这里主要用来翻译键盘消息
DispatchMessage 表示分发消息,
根据消息,调用窗口过程进行相应的处理
接下来,
我们要看看窗口过程:
_WinProc proc hWnd, uMsg, wParam, lParam
.............
_WinProc endp
这个和主过程
_WinMain不同的是,
它多了
4个参数,
分别是窗口句柄
hWnd, 消息
uMsg, 消息参数
wParam和
lParam
如果你在程序中用到了
ebx, esi, edi 等寄存器,
还需要通过以下的形式提前保存寄存器的值:
_WinProc proc uses ebx esi edi hWnd, uMsg, wParam, lParam
.............
_WinProc endp
local @stPS:PAINTSTRUCT
local @stRC:RECT
local @hDC
同样,这里定义三个变量,
2个结构变量和一个
DWOD变量,
在
32位汇编中,
用
local伪指令定义局部变量时,如果不指明变量类型,默认为
DWORD
接下来,
处理
2个我们感兴趣的消息
WM_PAINT和
WM_CLOSE,
不处理的消息用
DefWindowProc来处理
WM_PAINT消息主要用于绘制窗口客户区,
这里,我们在窗口的正中间,显示一行字:
invoke
BeginPaint, hWnd, addr @stPS
mov @hDC, eax
invoke SetTextColor,
@hDC, 0ff00h
invoke SetBkColor,
@hDC, 0
invoke GetClientRect,
hWnd, addr @stRC
invoke DrawText, @hDC,
offset szText, -1, addr @stRC, DT_SINGLELINE or DT_VCENTER or DT_CENTER
invoke EndPaint, hWnd,
addr @stPS
在绘画之前必须调用
BeginPaint获取绘画的
DC(device context)句柄,绘画结束,
需要调用
EndPaint释放资源
SetTextColor和
SetBkColor设定文字颜色和文字背景色
GetClientRect 获取客户区的坐标,
存储在结构变量
@stRC中,供后面绘画使用
DrawText 在窗口客户区显示文字,
它在
msdn中定义如下:
int DrawText(
HDC hDC, // handle
to DC
LPCTSTR lpString, // text to draw
int nCount, // text length
LPRECT lpRect, // formatting dimensions
UINT uFormat // text-drawing options
);
hDC
DC句柄
lpString 要显示的文字
nCount 显示的长度,
如果指定为
-1,
表示该字符串是以
0结尾的,函数会自动计算它的长度
lpRect
显示的矩形区域
uFormat 显示选项
DT_SINGLELINE 表示显示一行
DT_CENTER 表示水平居中
DT_VCENTER 表示垂直居中
程序中采用了它们
3个的组合
当窗口关闭的时候,
将收到
WM_CLOSE消息,
对于
WM_CLOSE消息处理如下:
invoke DestroyWindow, hWinMain
invoke PostQuitMessage,
0
先调用
DestroyWindow销毁主窗口,
然后再调用
PostQuitMessage向主线程发送一个
WM_QUIT消息,
让主线程结束消息循环,退出程序
WM_CLOSE消息必须处理,如果你不处理,
那么窗口关闭后,
主线程并不结束消息循环,
这样程序虽然看不到窗口了,
但是在任务管理器里面还存在这个进程。
对于整个源程序的解释就到这里,
很多
windows api函数我并没有很详细的说明,
自己用
google或者是
msdn查看一下,
然后对照着程序,
仔细研究一下就
ok了。
程序反汇编代码解析, 请看下一篇文章
。
代码跟上个例子相比,
长了很多,这个例子中用了大量的
windows api函数,
下面我们来慢慢的解释:
这里面多了一个
gdi32.inc和
gdi32.lib,
这里要用
windows的画图函数,所以要用到这个库和头文件
start:
...
end start
这个
start:表示一个标签,
后面有一个伪指令
end start, 这告诉编译器,
程序从
start这个标签的位置开始运行,
同时到
end start 这个位置结束,也就是说:
end 标签
这个伪指令表明了程序的开始和结束位置
call _WinMain
表示调用自定义过程
_WinMain,相当于
windows编程中的
WinMain函数,
这里换成伪指令
invoke也是一样,
即:
call _WinMain 和
invoke
_WinMain等价,
实际上经过我的测试
call _WinMain
invoke _WinMain
invoke offset _WinMain
invoke addr _WinMain
call offset _WinMain
都是正确的,
注意,
addr 伪指令只能和
invoke配合使用,不能这样写:
call addr _WinMain
_WinMain proc
....
_WinMain endp
定义一个过程,不带参数,
过程的名字可以任意,
这里的过程类似
c语言的函数
程序从这里开始运行,
我们来主意看看每行代码的含义:
local
@stWC:WNDCLASSEX
local @stMsg:MSG
定义
2个局部变量,
local表示局部变量的意思,
这个是局部变量定义伪指令
局部变量的名称分别为
@stWC和
@stMsg,类型分别为结构
WNDCLASSEX和
MSG
在汇编中,
根据个人习惯的不同,
各种变量以及标识符的写法不一样,但是必须遵循以下原则:
1. 可以用字母、数字、下划线以及符号
$、
@和
?
2. 第一个符号不能是数字
3. 长度不能超过
240个字符
4. 不能使用汇编关键字
5. 在作用域内必须唯一,这个跟高级语言的作用域是一样的
invoke
GetModuleHandle, NULL
mov hInstance, eax
调用
GetModuleHandle函数,
获取模块句柄,如果参数是
NULL,
则获取当前模块句柄
获取句柄后,
将句柄值存入全局变量
hInstance,
注意:
汇编语言中,所有函数调用的返回值都将放在
eax中
invoke
RtlZeroMemory, addr @stWC, sizeof @stWC
调用
RtlZeroMemory函数,将结构
@stWC填充,
填充大小为
sizeof @stWC
WNDCLASSEX结构在
msdn中定义如下:
typedef struct _WNDCLASSEX {
UINT cbSize;
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCTSTR lpszMenuName;
LPCTSTR lpszClassName;
HICON hIconSm;
} WNDCLASSEX, *PWNDCLASSEX;
cbSize
为本结构的大小,
必须填充为
sizeof WNDCLASSEX
style
为窗口风格,
典型设置为
CS_VREDRAW or CS_HREDRAW,
or表示或,
取两者的组合
lpfnWndProc 为窗口过程,
窗口过程的原型必须符合下列形式【过程名任意】:
WndProc Proto :dword, :dword, :dword, :dword
cbClsExtra 窗口类附加数据,
通常为
0
cbWndExtra 窗口附加数据,通常为
0
hInstance 当前实例句柄,
可以通过
GetModuleHandle获取
hIcon
程序图标
hCursor
光标
hbrBackground 窗口背景色
lpszMenuName 菜单名称,这里没有菜单,为
NULL
lpszClassName 窗口类名称
hIconSm
小图标
mov @stWC.cbSize, sizeof WNDCLASSEX
mov @stWC.style, CS_HREDRAW or CS_VREDRAW
mov @stWC.lpfnWndProc, offset _WinProc
push hInstance
pop @stWC.hInstance
invoke LoadIcon, NULL, IDI_APPLICATION
mov @stWC.hIcon, eax
mov @stWC.hIconSm, eax
invoke LoadCursor, NULL, IDC_ARROW
mov @stWC.hCursor, eax
invoke GetStockObject, BLACK_BRUSH
mov @stWC.hbrBackground, eax
mov @stWC.lpszClassName, offset szAppName
这段代码填充
WNDCLASSEX结构,
push hInstance
pop @stWC.hInstance
相当于
mov @stWC.hInstance,
hInstance,
但是这条指令是错误的,不能直接在
2个内存变量之间传值,需要借助一个寄存器间接传值,
可以写成如下形式
:
mov eax, hInstance
mov @stWC.hInstance, eax
invoke LoadIcon, NULL, IDI_APPLICATION 获取程序的默认图标
invoke LoadCursor, NULL, IDC_ARROW 获取默认光标
invoke GetStockObject, BLACK_BRUSH 获取一个黑色的画刷,用于将窗口背景画成黑色
invoke
RegisterClassEx, addr @stWC
.if !eax
invoke MessageBox,
NULL, offset szErrorText, offset szError, MB_OK or MB_ICONERROR
ret
.endif
注册窗口类,
如果注册失败,
则弹出一个提示对话框,
并且退出程序
invoke
CreateWindowEx, /
WS_EX_CLIENTEDGE,
/
offset
szAppName, /
offset
szAppName,
/
WS_OVERLAPPEDWINDOW,
/
CW_USEDEFAULT,
/
CW_USEDEFAULT,
/
CW_USEDEFAULT,
/
CW_USEDEFAULT,
/
NULL,
/
NULL,
/
hInstance,
/
NULL
.if !eax
invoke MessageBox,
NULL, offset szErrCreateWnd, offset szError, MB_OK or MB_ICONERROR
ret
.endif
mov hWinMain, eax
invoke ShowWindow, hWinMain, SW_SHOWNORMAL
invoke UpdateWindow, hWinMain
创建一个标准的
windows窗口,
CreateWindowEx在
msdn中定义如下:
HWND CreateWindowEx(
DWORD dwExStyle, // extended window style
LPCTSTR lpClassName, // registered class name
LPCTSTR lpWindowName, // window name
DWORD dwStyle, // window style
int
x,
// horizontal position of window
int
y,
// vertical position of window
int nWidth,
// window width
int nHeight, //
window height
HWND hWndParent, // handle to parent or
owner window
HMENU hMenu, //
menu handle or child identifier
HINSTANCE hInstance, // handle to application instance
LPVOID lpParam //
window-creation data
);
dwExStyle
窗口扩展风格,
这里用了
WS_EX_CLIENTEDGE,
表示下沉的边框
lpClassName 注册的窗口类名称,必须和
RegisterClassEx中用到的类名称一样
lpWindowName 窗口标题
dwStyle
窗口风格,通常为
WS_OVERLAPPEDWINDOW表示一个层叠的窗口
x, y, nWidth, nHeight 表示窗口的位置和大小,这里全部默认为
CW_USEDEFAULT
hWndParent 父窗口,
这里没有,
设置为
NULL
hMenu
窗口菜单,
这里没有,
设置为
NULL
hInstance
当前实例句柄
lpParam
附加数据,
通常不用,
设置为
NULL
如果窗口注册失败,
则弹出一个错误提示对话框,并且退出程序,否则把创建的窗口句柄保存到全局变量
hWinMain中
窗口创建成功后,
调用
ShowWindow显示窗口,
然后必须再调用
UpdateWindow发送一个
WM_PAINT消息,让程序重画窗口,
这样你才能看到窗口
.while TRUE
invoke GetMessage, addr
@stMsg, NULL, 0, 0
.break .if !eax
invoke
TranslateMessage, addr @stMsg
invoke DispatchMessage,
addr @stMsg
.endw
这里是一个消息循环,
程序不断的用
GetMessage获取消息,
如果
GetMessage返回值为
0,
表示退出程序,于是
.break .if !eax 退出循环,
整个程序结束运行
TranslateMessage 表示翻译消息,
这里主要用来翻译键盘消息
DispatchMessage 表示分发消息,
根据消息,调用窗口过程进行相应的处理
接下来,
我们要看看窗口过程:
_WinProc proc hWnd, uMsg, wParam, lParam
.............
_WinProc endp
这个和主过程
_WinMain不同的是,
它多了
4个参数,
分别是窗口句柄
hWnd, 消息
uMsg, 消息参数
wParam和
lParam
如果你在程序中用到了
ebx, esi, edi 等寄存器,
还需要通过以下的形式提前保存寄存器的值:
_WinProc proc uses ebx esi edi hWnd, uMsg, wParam, lParam
.............
_WinProc endp
local @stPS:PAINTSTRUCT
local @stRC:RECT
local @hDC
同样,这里定义三个变量,
2个结构变量和一个
DWOD变量,
在
32位汇编中,
用
local伪指令定义局部变量时,如果不指明变量类型,默认为
DWORD
接下来,
处理
2个我们感兴趣的消息
WM_PAINT和
WM_CLOSE,
不处理的消息用
DefWindowProc来处理
WM_PAINT消息主要用于绘制窗口客户区,
这里,我们在窗口的正中间,显示一行字:
invoke
BeginPaint, hWnd, addr @stPS
mov @hDC, eax
invoke SetTextColor,
@hDC, 0ff00h
invoke SetBkColor,
@hDC, 0
invoke GetClientRect,
hWnd, addr @stRC
invoke DrawText, @hDC,
offset szText, -1, addr @stRC, DT_SINGLELINE or DT_VCENTER or DT_CENTER
invoke EndPaint, hWnd,
addr @stPS
在绘画之前必须调用
BeginPaint获取绘画的
DC(device context)句柄,绘画结束,
需要调用
EndPaint释放资源
SetTextColor和
SetBkColor设定文字颜色和文字背景色
GetClientRect 获取客户区的坐标,
存储在结构变量
@stRC中,供后面绘画使用
DrawText 在窗口客户区显示文字,
它在
msdn中定义如下:
int DrawText(
HDC hDC, // handle
to DC
LPCTSTR lpString, // text to draw
int nCount, // text length
LPRECT lpRect, // formatting dimensions
UINT uFormat // text-drawing options
);
hDC
DC句柄
lpString 要显示的文字
nCount 显示的长度,
如果指定为
-1,
表示该字符串是以
0结尾的,函数会自动计算它的长度
lpRect
显示的矩形区域
uFormat 显示选项
DT_SINGLELINE 表示显示一行
DT_CENTER 表示水平居中
DT_VCENTER 表示垂直居中
程序中采用了它们
3个的组合
当窗口关闭的时候,
将收到
WM_CLOSE消息,
对于
WM_CLOSE消息处理如下:
invoke DestroyWindow, hWinMain
invoke PostQuitMessage,
0
先调用
DestroyWindow销毁主窗口,
然后再调用
PostQuitMessage向主线程发送一个
WM_QUIT消息,
让主线程结束消息循环,退出程序
WM_CLOSE消息必须处理,如果你不处理,
那么窗口关闭后,
主线程并不结束消息循环,
这样程序虽然看不到窗口了,
但是在任务管理器里面还存在这个进程。
对于整个源程序的解释就到这里,
很多
windows api函数我并没有很详细的说明,
自己用
google或者是
msdn查看一下,
然后对照着程序,
仔细研究一下就
ok了。
程序反汇编代码解析, 请看下一篇文章
。
相关文章推荐
- [32位汇编系列]002 - 创建标准的windows窗口(1)
- [32位汇编系列]002 - 创建标准的windows窗口(3)
- windows下32位汇编语言学习笔记 第四章 第一个窗口程序 1 (消息的使用和入口代码)
- 微机原理课程设计32位汇编学习之二(创建简单的窗口)
- windows下32位汇编语言学习笔记 第四章 第一个窗口程序 (windows的消息机制)
- Windows窗口程序从创建到关闭产生的消息
- Windows窗口创建
- Windows 7的预备知识系列之二:认识Windows 7中的窗口
- Windows 8 动手实验系列教程 实验1:创建Windows应用商店应用
- OpenGL_002创建窗口
- windows下创建控制台窗口
- 简单的windows窗口创建实例
- Windows环境下32位汇编程序设计C版code--第五章(二)
- Windows环境下32位汇编程序设计C版code--第五章(三)
- 游戏编程之DirectX的修炼:二(创建属于自己的windows窗口程序:下)
- Qt在Windows下如何创建无CMD窗口控制台程序
- 记录我的Windows编程(一)创建窗口
- 谈谈windows窗口注册和创建
- Delphi使用Windows API 创建最基本的窗口程序Hello,Windows演示
- 【Windows编程】系列第二篇:Windows SDK创建基本控件