首页
社区
课程
招聘
[study-note]Windows环境下32位汇编语言程序设计--第4章 第一个窗口程序4.1 开始了解窗口(2)
发表于: 2008-4-14 21:26 7417

[study-note]Windows环境下32位汇编语言程序设计--第4章 第一个窗口程序4.1 开始了解窗口(2)

2008-4-14 21:26
7417
第4章 第一个窗口程序4.1 开始了解窗口(2)    
      先通过一个简单的例子来说明两种程序设计方式的不同,以DOS下的文件比较命令comp为例,程序运行时先提示输入第一个文件名,然后是输入第二个文件名,程序比较后退出,同时把结果输出在屏幕上。假如有一个窗口版的comp程序,那么运行时会在屏幕上出现一个对话框,上面有两个文本框用来输入两个文件名,还会有个“比较”按钮,按下后开始比较文件,用户可以随时按下“关闭”按钮来退出程序。(dos程序是典型的过程程序,做什么事情都按过程来执行。)

      两种程序的运行会有相当大的不同,如图4.2所示,DOS程序必须按照顺序运行,当运行到输入第二个文件名时,用户不可能回到第一步修改第一个文件名,这时候用户也不能退出(除非用户强制用Ctrl+C键,但这不是程序的本意);而在窗口程序中用户可以随意选择先输入哪个文件名,同时也可以对窗口进行各种操作,当用户做任何一个操作的时候,相当于发出了一个消息,这些消息没有任何顺序关系,程序中必须随时准备处理不同的消息。(消息处理机制下的程序具有的灵活性远远要比按照顺序运行的程序要有优越性。)


图4.2 不同的程序结构模式

    这就决定了窗口程序必定在结构上和DOS程序有很大的不同,窗口程序实现大部分功能的代码应该呆在同一个模块——图中的“消息处理”模块中,这个模块可以随时应付所有类型的消息,只有这样才能随时响应用户的各种操作。(如果所有的代码都集中在一个模块里,那么代码是否会失去平衡呢?即导致某些地方代码很庞大。)
下面先来看一个地地道道的Win32汇编窗口程序。(这是最激动人心的时刻!)

2. FirstWindow源代码

    读者可以在所带光盘的Chapter04\FirstWindow目录中找到源代码,目录里面有两个文件,它们是汇编源文件FirstWindow.asm和nmake工具使用的makefile,汇编源程序如下:(大多数好的教程里都会写着,虽然可以从光盘里轻松的将代码复制到电脑上,但最好应该自己将它们打进去,而不是使用现成的。这样对初学者来说可以加深对程序的印象。据说微软前掌门人BILL在他很少写代码时,却还是会一句一句的将其它的工程师写的程序的代码打在电脑上。用意何在可显而易见。以下是我自己打的代码,我还添加了非常详细的注释进去,当然这并不是一个好习惯,但是可以大大加深对程序的理解,虽然有可能是不正确的,但至少有了自己的理解。比起对程序什么都不了解的要好点。btw:在我一句一句代码打出来时,居然少打了处理消息的缺省函数进去,害我得花了好长时间来检查程序错在哪里,=-=||,有了这个教训,以后应该不会再犯这个错误了。)

;=============================================
;=>使用386指令集
;=>内存模式为平坦模式
;=>函数调用为标准调用方式,即参数从右到左压栈,由被调用者恢复堆栈
;============================================

.386
.model  flat,stdcall
option  casemap:none

;=========
;=>文件定义
;=========
include  windows.inc
include  gdi32.inc
includelib  gdi32.lib
include  user32.inc
includelib  user32.lib
include  kernel32.inc
includelib  kernel32.lib

;==============
;=>数据段
;==============
.data?
hInstance    dd  ?
hWinMain    dd  ?

.const
szClassName  db  'MyClass',0
szCaptionMain  db  'My first Window!',0
szText    db  'Win32 Assembly,Simple and powerful!',0

;==========
;=>代码段
;==========
.code

;==========================================
;=>窗口过程
;=>告诉编译器在函数中使用ebx edi esi寄存器,参数缺省时为dword类型
;==========================================
_ProcWinMain  proc  uses ebx edi esi,hWnd,uMsg,wParam,lParam

    ;定义一个绘制结构体变量和一个矩形结构体变量
    ;绘制结构体变量存储一些关于在屏幕上绘制图形的信息
    ;矩形结构体存储窗口的大小

    local  @stPs:PAINTSTRUCT
    local  @stRect:RECT
    local  @hDc
    
    ;将接受到的消息传送到eax中去,以作进一步的处理
    ;为什么要传送到eax中去呢?不喜欢也可以换ebx的
    ;重要的是里面的消息,而不是存储这个消息的寄存器
    mov  eax,uMsg

    .if  eax==WM_PAINT  ;绘制消息,当屏幕变动时触发该消息事件

      ;调用开始绘制API取得窗口DC并将其传送到DC句柄
      invoke  BeginPaint,hWnd,addr @stPs
      mov  @hDc,eax

      ;取得窗口的坐标并将其传送到结构体变量
      invoke  GetClientRect,hWnd,addr @stRect

      ;完成上面工作后,在客户区中以水平垂直居中形式输出一行文字
      invoke  DrawText,@hDc,addr szText,-1,addr @stRect,DT_SINGLELINE or DT_CENTER or \
                  DT_VCENTER

      ;结束绘制图形并进行扫尾工作
      invoke  EndPaint,hWnd,addr @stPs

    .elseif  eax==WM_CLOSE  ;如果接收到关闭窗口的消息

      ;调用销毁窗口API销毁创建的窗口
      invoke  DestroyWindow,hWinMain
    .else
      ;如果不是自己感兴趣的消息,就交给缺省消息处理函数去处理吧
      invoke  DefWindowProc,hWnd,uMsg,wParam,lParam
      
      ret
    .endif

    xor  eax,eax

    ret
_ProcWinMain  endp

;========================================
;=>主函数,用于创建窗口及接受消息和分发消息到相应窗口中去
;========================================
_WinMain    proc

    ;定义一个窗口类结构体变量,这里的类不是C++里说的类,而是一个结构体,仅是名字叫类而已
    ;这个类限制了将要创建的窗口的风格,利用这个类可以创建N个窗口,一般只要一个就可以了
    local  @stWndClass:WNDCLASSEX
    
    ;定义一个消息结构体变量,同样是一个结构体,这说明了结构体类型是多么的重要
    local  @stMsg:MSG

    ;调用取模块句柄API,参数是NULL,并将结果传送到实例句柄变量
    invoke  GetModuleHandle,NULL
    mov  hInstance,eax

    ;将窗口类结构体变量清空为0,这是为了防止不必要的意外发生
    invoke  RtlZeroMemory,addr @stWndClass,sizeof @stWndClass

;==========
;=>注册窗口类
;==========  
    ;加载光标资源到内存,并将其句柄传送到窗口类变量,这里加载的是系统的光标
    ;其实我们可以轻松将其替换成我们自己的光标,但这超出了第一个窗口程序的要求了
    invoke  LoadCursor,0,IDC_ARROW
    mov  @stWndClass.hCursor,eax

    ;将模块实例句柄传送到窗口类中去
    ;这里不是直接用mov指令传送,而是通过堆栈间接传送的
    push  hInstance
    pop  @stWndClass.hInstance

    ;将窗口类结构体类型大小传送到窗口类结构体变量的cbsize成员中
    mov  @stWndClass.cbSize,sizeof WNDCLASSEX

    ;设置窗口风格为当窗口水平或垂直方向大小发生改变时要重新刷新窗口
    ;CS_HREDRAW 表示 窗口类风格(CS) 为 水平方向(H)发生改变时重绘窗口(REDRAW)
    ;CS_VREDRAW 表示 窗口类风格(CS) 为 垂直方向(V)发生改变时重绘窗口(REDRAW)
    ;更多风格请参照微软MSDN,其实这两个风格一般情况下已经足够了
    mov  @stWndClass.style,CS_HREDRAW or CS_VREDRAW

    ;将所谓的窗口消息处理函数的地址传送到窗口类成员lpfnWndProc中去
    ;lpfnWndProc 要分四个部分看:lp 表示 long point 即变量类型为 长指针(指针就指针还长指针,不可理解)
    ;      Wnd 表示 window(s) 即窗口
    ;      Proc 表示 process 即处理
    ;四个部分合起来就是类型为指向窗口处理函数的指针,即存储函数的地址的变量
    ;恐怖的术语常常成为我们学习的阻碍,现在开始要透过现象看本质,指针不过是一个变量而已,只是它里面存储的是另一个
    ;内存单元的地址
    mov  @stWndClass.lpfnWndProc,offset _ProcWinMain

    ;设置窗口背景颜色为窗口缺省颜色再加1
    ;这里COLOR_WINDOW是一个常量,是已经设置好的颜色值
    mov  @stWndClass.hbrBackground,COLOR_WINDOW+1
    
    ;设置窗口类名为 szClassName
    ;窗口类变量名和窗口类名有什么关系呢?  简单点说一个是结构体变量,一个是字符串
    ;关于其作用可百度一下
    ;lpszClassName 这个名字好长啊=-=|| @#$%(&*^
    ;我们可以这样理解:lp 为变量类型,即long point长指针
    ;    sz 为变量类型,即字符串。和上面的合起来就是字符串指针
    ;    ClassName 为类名,即变量的名字,这样命名易于理解很多
    ;上面几个意思合起来就是:指向一个类名字符串的指针
    mov  @stWndClass.lpszClassName,offset szClassName

    invoke  RegisterClassEx,addr @stWndClass
;============
;=>建立并显示窗口
;============
    invoke  CreateWindowEx,WS_EX_CLIENTEDGE,\
      offset szClassName,offset szCaptionMain,\  ;参数为窗口类名和窗口标题,窗口标题才会显示
      WS_OVERLAPPEDWINDOW,\  
      100,100,600,400,\        ;窗口大小
      NULL,NULL,hInstance,NULL      ;其中有一个参数是本模块句柄

    ;将创建的窗口的句柄传送到主窗口句柄变量,其实它这个变量不过是一个双字变量而已
    ;没什么好怕的,见多用多就熟悉了
    mov  hWinMain,eax

    ;显示创建好的窗口
    invoke  ShowWindow,hWinMain,SW_SHOWNORMAL

    ;刷新窗口的客户区
    invoke  UpdateWindow,hWinMain
;===========
;=>消息循环
;===========
    .while  TRUE      ;死循环,一般都有这么一个循环在消息处理程序中的
    
      ;获取系统发送到这个程序对应的消息队列里的消息
      invoke  GetMessage,addr @stMsg,NULL,0,0
      
      
      .break  .if  eax==0

      ;对获取的消息进行"解密",即如果有键盘键按下,则将两个键盘消息合并为一个字符消息处理
      invoke  TranslateMessage,addr @stMsg

      ;将消息发送到各自的处理函数中去处理,即什么窗口的消息,就发送到什么窗口的
      ;消息处理函数去处理,一个窗口可以有一个处理函数,二个则可能有两个处理函数
      invoke  DispatchMessage,addr @stMsg

    .endw

    ret
_WinMain    endp

;=======================================
start:  
    call  _WinMain
    invoke  ExitProcess,NULL
;=======================================
end  start




/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
本资料来自罗云彬的WIN32汇编教程,本人只是添加一点点注解而已(见括号内容),而这仅是个人见解,如有雷同,纯属巧合。如有不同见解,也可发表出来,大家一起讨论,本文章最终解释权归罗去彬所有。谢谢。
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

[课程]Android-CTF解题方法汇总!

上传的附件:
收藏
免费 7
支持
分享
最新回复 (7)
雪    币: 486
活跃值: (13)
能力值: ( LV9,RANK:430 )
在线值:
发帖
回帖
粉丝
2
如果觉得看上面的代码繁杂,可以看"原版"的清晰版的代码

.386
                 .model flat,stdcall
                 option casemap:none

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Include 文件定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include          windows.inc
include          gdi32.inc
includelib       gdi32.lib
include          user32.inc
includelib       user32.lib
include          kernel32.inc
includelib       kernel32.lib

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
                 .data?

hInstance        dd         ?

hWinMain         dd         ?

                 .const

szClassName      db    'MyClass',0

szCaptionMain    db    'My first Window !',0

szText           db    'Win32 Assembly, Simple and powerful !',0

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 代码段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
                 .code

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 窗口过程
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_ProcWinMain     proc  uses ebx edi esi,hWnd,uMsg,wParam,lParam

                 local @stPs:PAINTSTRUCT

                 local @stRect:RECT

                 local @hDc

                 mov   eax,uMsg

;********************************************************************
                 .if      eax ==    WM_PAINT

                          invoke    BeginPaint,hWnd,addr @stPs

                          mov       @hDc,eax

                          invoke    GetClientRect,hWnd,addr @stRect

                          invoke    DrawText,@hDc,addr szText,-1,\

                                   addr @stRect,\

                                   DT_SINGLELINE or DT_CENTER or DT_VCENTER

                          invoke    EndPaint,hWnd,addr @stPs

;********************************************************************
                 .elseif  eax ==    WM_CLOSE

                          invoke    DestroyWindow,hWinMain

                          invoke    PostQuitMessage,NULL

;********************************************************************
                 .else
                          invoke    DefWindowProc,hWnd,uMsg,wParam,lParam

                                   ret

                          .endif
;********************************************************************
                 xor      eax,eax

                 ret
_ProcWinMain     endp

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_WinMain         proc
                 local    @stWndClass:WNDCLASSEX

                 local    @stMsg:MSG

                 invoke   GetModuleHandle,NULL

                 mov                hInstance,eax

                 invoke   RtlZeroMemory,addr @stWndClass,sizeof @stWndClass

;********************************************************************
; 注册窗口类
;********************************************************************
                 invoke   LoadCursor,0,IDC_ARROW

                 mov      @stWndClass.hCursor,eax

                 push     hInstance

                 pop      @stWndClass.hInstance

                 mov      @stWndClass.cbSize,sizeof WNDCLASSEX

                 mov      @stWndClass.style,CS_HREDRAW or CS_VREDRAW

                 mov      @stWndClass.lpfnWndProc,offset _ProcWinMain

                 mov      @stWndClass.hbrBackground,COLOR_WINDOW + 1

                 mov      @stWndClass.lpszClassName,offset szClassName

                 invoke   RegisterClassEx,addr @stWndClass

;********************************************************************
; 建立并显示窗口
;********************************************************************
                 invoke   CreateWindowEx,WS_EX_CLIENTEDGE,\

                          offset szClassName,offset szCaptionMain,\

                          WS_OVERLAPPEDWINDOW,\

                          100,100,600,400,\

                          NULL,NULL,hInstance,NULL

                 mov      hWinMain,eax

                 invoke   ShowWindow,hWinMain,SW_SHOWNORMAL

                 invoke   UpdateWindow,hWinMain

;********************************************************************
; 消息循环
;********************************************************************
                 .while   TRUE

                          invoke    GetMessage,addr @stMsg,NULL,0,0

                          .break    .if eax  == 0

                          invoke    TranslateMessage,addr @stMsg

                          invoke    DispatchMessage,addr @stMsg

                 .endw

                 ret
_WinMain         endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
start:
                 call     _WinMain

                 invoke   ExitProcess,NULL
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
                 end      start
2008-4-14 21:27
0
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3

整个架子出来鸟。。
Copy了去添砖添瓦~~
无风。顶死你。。霸王绝顶!!
2008-4-14 21:45
0
雪    币: 205
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
感觉没什么意义啊。如果单纯的只是写注释,我觉的没任何意义。网络上的文章也很多或者老罗的书上已经很详细。。
我觉得你应该把自己的见解表达的多一些,并不是一味的去摘抄,加个注释。。。。
2008-4-15 01:59
0
雪    币: 1852
活跃值: (504)
能力值: (RANK:1010 )
在线值:
发帖
回帖
粉丝
5
可能你觉得没意义,但对楼主来说却非常有益,学习别人的代码真正掌握原理,这才是最重要的。
楼主在学习之后能够与大家分享经验自然就该得到尊重。
至于所写的内容到底如何,我们管理团队自然会给出相应评价。
2008-4-15 09:49
0
雪    币: 212
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
6
不错啊,顶!如果每章每个例子都有人做出注释那就好了!
2008-4-15 13:05
0
雪    币: 290
活跃值: (11)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
7
uses  ebx edi esi

是 在过程中

push ebx
push edi
push esi

....

pop ebx  
pop edi  
pop esi

是嘛??
2008-4-15 13:39
0
雪    币: 271
活跃值: (18)
能力值: ( LV12,RANK:370 )
在线值:
发帖
回帖
粉丝
8
楼上。保存寄存器,防止被破坏...。感觉写的蛮好的。...
2008-4-15 15:19
0
游客
登录 | 注册 方可回帖
返回
//