[32位汇编系列]002 - 创建标准的windows窗口(2)

接着上面一篇文章

 

 

代码跟上个例子相比, 长了很多,这个例子中用了大量的 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了。

 

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

<script type="text/javascript"> </script> <script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript"></script>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值