您的位置:首页 > 其它

[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了。

程序反汇编代码解析, 请看下一篇文章

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: