当前位置: 首页 > news >正文

汇编语言全接触-60.Win32汇编教程四

在这儿下载本节的所有源程序。

有关窗口的基本知识

窗口是屏幕上的矩形区域。一个窗口可以从键盘或者鼠标接受用户的输入,并在其内部显示图形输出。一个应用程序窗口通常包含程序的标题条、菜单、边框,滚动条。其中,对话框也是一种窗口。不同的是,对话框表面通常包含几个其它窗口,称之为“子窗口”。这些子窗口的形式有压入按钮、单选按钮、复选框、文本输入区域、列表框和滚动条等。 用户将这些窗口看成屏幕上的对象,可以通过按下一个按钮或者滚动一个滚动条与这些对象直接交互。

窗口以“消息”的形式接收窗口的输入,窗口也用消息与其它窗口通讯。比如在程序窗口的大小改变时,字处理器会重新格式化其中的文本。窗口大小改变的细节是由操作系统处理的,但程序能够响应这个系统功能。当用户改变窗口的大小时,Windows给程序发送一条消息指出新窗口的大小。然后,程序就可以调整窗口中的内容,以响应大小的变化。程序创建的每一个窗口都有相关的窗口过程。也就是给这个窗口指定一个子程序(窗口过程),Windows通过调用它来给窗口发送消息。窗口过程再根据此消息进行处理,然后将控制返回给Windows。

窗口在“窗口类”的基础上创建的。Windows定义了确省的窗口过程,如果你对所有的消息都让Windows自己处理,那么你就能得到一个标准的窗口,同样,你也可以选择处理自己感兴趣的消息,这样,相当于产生了不同的子类,也就形成了不同的应用程序。同样,子窗口也是基于同一个窗口类,并且使用同一个窗口过程。例如,所有Windows 程序中的所有按钮都基于同一窗口类。这个窗口类有一个处理所有按钮消息的窗口过程,但是,如果你按自己的设想设计一个按钮,如想把按钮的表面换成位图,你就可以自己处理按钮窗口的 WM_PAINT 消息,当 Windows 需要画按钮表面的时候,你就可以随自己的意思去画。

Windows程序开始执行后,Windows为该程序创建一个“消息队列”。这个消息队列用来存放该程序可能创建的各种不同窗口的消息。程序中有一段代码,叫做“消息循环”, 它用来从队列中取出消息,并且将它们发送给相应的窗口过程。在没有消息发生的时候,你的程序实际上就在消息循环中转圈子。

创建一个窗口的过程如下:

取得程序的实例句柄(hInstance)

注册窗口类,实际上就是为你的窗口指定处理消息的过程,定义光标,窗口风格,颜色等参数

创建窗口

显示窗口

然后进入消息循环,也就是不停地检测有无消息,并把它发送给窗口进程去处理。

创建一个窗口的代码在不同的程序中实际上是几乎一模一样的,所以你编一个新的程序时可以把这一段拷来拷去,稍微修改一下就行,程序的大部分代码实际上是用在窗口过程中,因为这才是不同程序的不同之处。窗口过程的编程要点如下:

从Windows传给窗口过程的参数 uMsg 得到消息类型,并转到不同的分枝去处理。

对自己已经处理的消息,返回 Windows 时必须在eax 中返回0。

自己不处理的消息,必须调用 DefWindowProc 处理,并把返回值传回Windows,否则,Windows会无法显示。

uMsg 参数中指定的消息有280多种,实际上我们需要处理的只有重要的几种,如Windows在创建的时候会发送 WM_CREATE 消息,我们就可以在这时候初始化,分配内存等等,而退出时会发送 WM_CLOSE,我们就可以进行释放内存等清除工作,当Windows上的菜单或按钮被按下时发送 WM_COMMAND 消息等等,具体可以参考 Win32 Programmer's Reference。下面,我们来看一个创建窗口的简单程序。

一个创建窗口的程序

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

; Programmed by 罗云彬, bigluo@telekbird.com.cn

; Website: http://asm.yeah.net

; LuoYunBin's Win32 ASM page (罗云彬的编程乐园)

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

.386

.model flat, stdcall

option casemap :none ; case sensitive

include windows.inc

include user32.inc

include kernel32.inc

include comctl32.inc

include comdlg32.inc

include gdi32.inc

includelib user32.lib

includelib kernel32.lib

includelib comctl32.lib

includelib comdlg32.lib

includelib gdi32.lib

IDI_MAIN equ 1000 ;icon

IDM_MAIN equ 4000 ;menu

IDM_EXIT equ 4001

.data?

hInstance dd ?

hWinMain dd ?

hMenu dd ?

szBuffer db 256 dup (?)

.data

szClassName db "Windows Template",0

szCaptionMain db '窗口模板',0

.code

start:

call _WinMain

invoke ExitProcess,NULL

_WinMain proc

local @stWcMain:WNDCLASSEX

local @stMsg:MSG

invoke InitCommonControls

invoke GetModuleHandle,NULL

mov hInstance,eax

invoke LoadIcon,hInstance,IDI_MAIN

mov hIcon,eax

invoke LoadMenu,hInstance,IDM_MAIN

mov hMenu,eax

;*************** 注册窗口类 *****************************************

invoke LoadCursor,0,IDC_ARROW

mov @stWcMain.hCursor,eax

mov @stWcMain.cbSize,sizeof WNDCLASSEX

mov @stWcMain.hIconSm,0

mov @stWcMain.style,CS_HREDRAW or CS_VREDRAW

mov @stWcMain.lpfnWndProc,offset WndMainProc

mov @stWcMain.cbClsExtra,0

mov @stWcMain.cbWndExtra,0

mov eax,hInstance

mov @stWcMain.hInstance,eax

mov @stWcMain.hIcon,0

mov @stWcMain.hbrBackground,COLOR_WINDOW + 1

mov @stWcMain.lpszClassName,offset szClassName

mov @stWcMain.lpszMenuName,0

invoke RegisterClassEx,addr @stWcMain

;*************** 建立输出窗口 ***************************************

invoke CreateWindowEx,WS_EX_CLIENTEDGE,\

offset szClassName,offset szCaptionMain,\

WS_OVERLAPPEDWINDOW OR WS_VSCROLL OR WS_HSCROLL,\

0,0,550,300,\

NULL,hMenu,hInstance,NULL

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

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

WndMainProc proc uses ebx edi esi, \

hWnd:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD

mov eax,uMsg

.if eax == WM_CREATE

mov eax,hWnd

mov hWinMain,eax

call _Init

;********************************************************************

.elseif eax == WM_COMMAND

.if lParam == 0

mov eax,wParam

.if ax == IDM_EXIT

call _Quit

.endif

.endif

;********************************************************************

.elseif eax == WM_CLOSE

call _Quit

;********************************************************************

.else

invoke DefWindowProc,hWnd,uMsg,wParam,lParam

ret

.endif

xor eax,eax

ret

WndMainProc endp

_Init proc

invoke SendMessage,hWinMain,WM_SETICON,ICON_SMALL,hIcon

ret

_Init endp

;********************************************************************

_Quit proc

invoke DestroyWindow,hWinMain

invoke PostQuitMessage,NULL

ret

_Quit endp

;********************************************************************

end start

窗口程序的分析

让我们来简单分析一下这个程序,首先程序调用 _WinMain,在_WinMain 中定义了两个局部变量 @stMsg 和 @stWinMain,数据类型分别是 MSG 和 WNDCLASSEX结构,在参考手册中,可以看到WNDCLASSEX定义了一个窗口的所有参数,如使用的菜单、光标、颜色、窗口过程等,接下来的一大堆 mov 指令实际上就是在填写这个数据结构,填写完成后,最重要的两句是 mov @stWcMain.lpfnWndProc,offset WndMainProc 定义了处理消息的窗口过程, mov @stWcMain.lpszClassName,offset szClassName 定义了你要创建的类的名称,然后就是使用 RegisterClassEx 注册这个窗口类,注意,这时候窗口并没有创建,你只不过是定义好了一个子类,接下去你要用你定义的类去创建一个窗口。也就是使用 CreateWindowEx 函数去创建它。在手册中,CreateWindowEx 是这样定义的:

HWND CreateWindowEx(

DWORD dwExStyle, // extended window style

LPCTSTR lpClassName, // pointer to registered class name

LPCTSTR lpWindowName, // pointer to 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, // handle to menu, or child-window identifier

HINSTANCE hInstance, // handle to application instance

LPVOID lpParam // pointer to window-creation data );

其中的参数 dwExStyle 是窗口的风格,lpClassName 就是我们自己定义的类的名字。如果大家要创建一个已经定义好的类,如 RichEdit 类等等,只要把 lpClassName 指向 "RichEdit32" 字符串就行了,当然这时就不用 RegisterClass 以及编写自己的窗口过程了。执行 CreateWindowEx 后,得到一个返回值就是窗口句柄,这个值在以后是要经常用到了,所以要先保存下来。这时窗口并没有在屏幕上显示出来,而是处于隐藏状态,我们要用 ShowWindow 来显示出窗口并用UpdateWindow 来绘窗口的内容。

窗口显示出来后,程序就进入一个循环----消息循环,前面我已经说过,作用是不停地接收 Windows 消息并发送给窗口过程去处理。GetMessage 从消息队列中取出一条消息,如果取得的消息不是 WM_QUIT,那么 GetMessage 返回一个非零值,否则返回零,这时候循环结束,程序执行 ExitProcess退回操作系统。TranslateMessage 将消息进行一些键盘转换,用于处理一些快捷键,DispatchMessage 将处理后的消息发回 Windows,由Windows调用窗口进程进行处理,当窗口进程处理完返回后,程序才从 DispatchMessage 返回,从而开始下一个 GetMessage 调用。这些函的参数可以参考手册。

窗口过程的分析

窗口过程有四个参数,hWnd 是本窗口的句柄,和创建窗口时返回的值相同,uMsg 是本次调用的消息类型,wParam 和lParam是消息的参数,其含义和数值根据消息的不同而不同。在本程序中,我们处理 WM_CREATE,WM_COMMAND 和 WM_QUIT 消息,然后返回0,对不处理的消息,使用 invoke DefWindowProc,hWnd,uMsg,wParam,lParam 来处理并直接用 ret 将返回值传回 Windows。在响应 WM_CLOSE 消息时,我们用 DestroyWindow 清除窗口并用PostQuitMessage 产生一条 WM_QUIT 消息,从而使程序在 消息循环调用GetMessage 时返回0,以结束消息循环并结束程序。

http://www.rkmt.cn/news/187738.html

相关文章:

  • 2026年1月份学校/幼儿园/医院/家装/儿童房/美容会所/酒店客房专用的环保涂料生产厂家权威测评榜单重磅来袭 - 一搜百应
  • java计算机毕业设计心理问题档案管理系统 心理健康信息档案平台 心理诊疗电子档案系统
  • 2020-JTS-Overlay-Next-Generation
  • 施工机械设备远程监控集中管理系统方案
  • 2025高性价比的成人书法培训专业机构TOP5排行 - 工业推荐榜
  • 【.NET性能优化秘籍】:利用表达式树模拟Lambda默认参数,提升代码复用率
  • 抄作业!2025厦门全案设计装修公司红榜攻略 - 品牌测评鉴赏家
  • 精选6个AI论文网站,涵盖降重和自然改写功能,有效降低重复率
  • 厦门家居装修市场观察:不同类型公司的服务模式分析 - 品牌测评鉴赏家
  • 从臃肿到优雅:6大实战案例教你简化C++模板元编程代码
  • 如何用C#拦截器优雅地实现日志、鉴权与重试?90%开发者忽略的关键设计
  • AI辅助论文写作:6个实用网站,支持一键降重与智能改写,提升原创性
  • [STM32C0] 【STM32C092RC 测评】+ 02 板载按键用作外部中断触发LED闪烁
  • docker部署filebrowser轻量级个人云盘
  • CF1749E - Cactus Wall
  • 2025大模型九大厂商全景复盘:从OpenAI到DeepSeek,2026十大趋势预判,小白程序员必学指南
  • 2025年耐水腻子粉厂家实力推荐:福州高彪建材,内墙/外墙/耐水腻子粉全品类供应 - 品牌推荐官
  • YOLOv8模型推理接口封装:构建RESTful API服务
  • Docker打造全能媒体中心Plex
  • rust生成器模式
  • 超详细PyTorch安装教程GPU版:支持YOLOv8高效运行
  • YOLOv8训练中断恢复技巧:断点续训配置方法
  • 微服务边界的“黄金分割律”:凭什么功能A和B不能放在一个服务里?
  • 震惊!国内188+26家大模型全解析,小白程序员秒变AI大神就靠这份清单!
  • 2025年路面步道板厂家实力推荐:哈尔滨钧楚建材,彩色/防滑/透水/水泥步道板全系供应 - 品牌推荐官
  • C# 集合表达式进阶指南(交错数组优化秘籍)
  • 【C# 12顶级语句增强深度解析】:掌握跨平台开发新利器,提升编码效率300%
  • 【.NET通信优化必修课】:基于拦截器的性能监控与故障预判方案
  • 快手知识付费课程:教小白学会使用AI开发环境
  • 2026现代简约风装修公司怎么选?这5家宝藏公司帮你划重点! - 品牌测评鉴赏家