通行证: 用户 密码 域名空间  下载中心 社区论坛 信息公告 MY小屋
联系我们
设为首页
加入收藏

 

QQ,ASP,PHP,JSP,XML,SQL,.Net,编程 程序 网页图象 建站经验 私服
首页 | 新闻资讯 | 编程开发 | 网页设计 | 图形图象 | 网络媒体 | 网站模板 | 数 据 库 | 投稿
论坛 | 操作系统 | 系统优化 | 网络安全 | 黑客技术 | 硬件学堂 | 硬件报价 | 服 务 器 | 地图
专题 | 应用软件 | 聊天通讯 | Q Q 专栏 | 建站经验 | 在线工具 | 站长Club | 注 册 表 | 旧版
社会 | 游戏娱乐 | 设计欣赏 | 疑难解答 | 社区论坛 | 韩国素材 | 素材图库 | 广告服务 | 服务
当前位置:首页>>编程开发>>汇编语言>>正文 新版上线![旧版]
注:打开慢时请稍等

win32消息框

http://www.iyit.net  日期:2006-5-22 12:34:25  来源:网络转载   点击:
参加讨论】win32消息框:

  在本课中,我们将用汇编语言写一个 Windows 程序,程序运行时将弹出一个消息框并显示"Win32 assembly is great!"。

理论:

Windows 为编写应用程序提供了大量的资源。其中最重要的是Windows API (Application Programming Interface)。 Windows API是一大组功能强大的函数,它们本身驻扎在 Windows 中供人们随时调用。这些函数的大部分被包含在几个动态链接库(DLL)中,譬如:kernel32.dll、 user32.dll 和 gdi32.dll。 Kernel32.dll中的函数主要处理内存管理和进程调度;user32.dll中的函数主要控制用户界面;gdi32.dll中的函数则负责图形方面的操作。除了上面主要的三个动态链接库,您还可以调用包含在其他动态链接库中的函数,当然您必须要有关于这些函数的足够的资料。

动态链接库,顾名思义,这些 API 的代码本身并不包含在 Windows 可执行文件中,而是当要使用时才被加载。为了让应用程序在运行时能找到这些函数,就必须事先把有关的重定位信息嵌入到应用程序的可执行文件中。这些信息存在于引入库中,由链接器把相关信息从引入库中找出插入到可执行文件中。您必须指定正确的引入库,因为只有正确的引入库才会有正确的重定位信息。

当应用程序被加载时 Windows 会检查这些信息,这些信息包括动态链接库的名字和其中被调用的函数的名字。若检查到这样的信息,Windows 就会加载相应的动态链接库,并且重定位调用的函数语句的入口地址,以便在调用函数时控制权能转移到函数内部。

如果从和字符集的相关性来分,API 共有两类:一类是处理 ANSI 字符集的,另一类是处理 UNICODE 字符集的。前一类函数名字的尾部带一个"A"字符,处理UNICODE的则带一个"W"字符(我想"W"也许是代表宽字符的意思吧)。我们比较熟悉的ANSI字符串是以 NULL 结尾的一串字符数组,每一个ANSI字符是一个 BYTE 宽。对于欧洲语言体系,ANSI 字符集已足够了,但对于有成千上万个唯一字符的几种东方语言体系来说就只有用 UNICODE 字符集了。每一个 UNICODE 字符占有两个 BYTE 宽,这样一来就可以在一个字符串中使用 65336 个不同字符了。

这也是为什么引进 UNICODE 的原因。在大多数情况下我们都可以用一个包含头文件,在其中定义一个宏,然后在实际调用函数时,函数名后不需要加后缀"A"或"W"。
<译者注:如在头文件中定义函数foo();
#ifdef UNICODE
#define foo() fooW()
#else
#define foo() fooA()
#endif
>

例子:

我先把框架程序放在下面,然后我们再向里面加东西。

.386
.model flat, stdcall
.data
.code
start:
end start

应用程序的执行是从 END 定义的标识符后的第一条语句开始的。在上面的框架程序中就是从 START 开始。程序逐条语句执行一直到遇到 JMP,JNE,JE,RET 等跳转指令。这些跳转指令将把执行权转移到其他语句上,若程序要退出 Windows,则必须调用函数 ExitProcess。

ExitProcess proto uExitCode:DWORD

上面一行是函数原型。函数原型会告诉编译器和链接器该函数的属性,这样在编译和链接时,编译器和链接器就会作相关的类型检查。 函数的原型定义如下:

FunctionName PROTO [ParameterName]:DataType,[ParameterName]:DataType,...

简言之,就是在函数名后加伪指令PROTO,再跟一串由逗号相隔的数据类型链表。在前面的 ExitProcess 定义中,该函数有一个 DWORD 类型的参数。当您使用高层调用语句 INVOKE 时,使用函数原型定义特别有用,您可以简单地认为 INVOKE 是一个有参数类型检查的调用语句。譬如,假设您这样写:

call ExitProcess

若您事先没把一个DWORD类型参数压入堆栈,编译器和链接器都不会报错,但毫无疑问,在您的程序运行时将引起崩溃。但是,当您这样写:

invoke ExitProcess

连接器将报错提醒您忘记压入一个 DWORD 类型参数。所以我建议您用 INVOKE 指令而不是CALL去调用一个函数。INVOKE 的语法如下:

INVOKE expression [,arguments]

expression 既可以是一个函数名也可以是一个函数指针。参数由逗号隔开。大多数API函数的原型放在头文件中。 如果您用的是 hutch 的 MASM32,这些头文件在文件夹MASM32/include 下, 这些头文件的扩展名为 INC,函数名和 DLL 中的函数名相同,譬如:KERNEL32.LIB 引出的函数 ExitProcess 的函数原形声明于kernel.inc中。您也可以自己声明函数原型。 在我的教学课程中都使用 hutch 的windows。inc,这些头文件您可以从http://win32asm.cjb.net下载。

好,我们现在回到ExitProcess 函数,参数uExitCode 是您希望当您的应用程序结束时传递 Windows 的。 您可以这样写:

invoke ExitProcess,0

把这一行放到开始标识符下,这个应用程序就会立即退出 Windows,当然毫无疑问个应用程序本身是一个完整的 Windows 程序。

.386
.model flat, stdcall
option casemap:none

include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib

.data
.code
start:
invoke ExitProcess,0
end start

option casemap:none 一句的意思是告诉 MASM 要区分标号的大小写,譬如:start 和 START 是不同的。请注意新的伪指令 include,跟在其后的文件名所指定的文件在编译时将“插”在该处。在我们上面的程序段中,当MASM处理到语句 include \masm\include\windows.inc 时,它就会打开文件夹\MASM32\include 中的文件windows.inc,这和您把整个文件都粘贴到您的源程序中的效果是一样的。 hutch 的 windows.inc 包含了 WIN32 编程所需要的常量和结构体的定义。 但是它不包含函数原型的定义。尽管 hutch 和我尽力包含所有的常量和结构体的定义,但仍会有不少遗漏,为此我们将不断加入新的内容。请随时注意我们主页,下载最新的头文件。

您的应用程序除了从 windows.inc 中得到相关变量结构体的定义外,还需要从其他的头文件中得到函数原型的声明,这些头文件都放在 \masm32\include 文件夹中。 在我们上面的例子中调用了驻扎在 kernel.dll 中的函数,所以需要包含有这个函数原型声明的头文件 kernel.inc。如果用文本编辑器打开该文件您会发现里面全是从 kernel.dll中引出的函数的声明。如果您不包含kernel.inc,您仍然可以调用(call)ExitProcess,但不能够调用(invoke)ExitProcess(这会无法通过编译器和连接器的参数合法性检查)。所以若用 invoke 去调用一个函数,您就必须事先声明,当然不一定要包含我们的头文件,您完全可以在调用该函数前在源代码的适当位置进行声名。包含头文件主要是为了节省时间(译者:当然还有正确性)

接下来我们来看看 includelib 伪指令,和 include 不同,它仅仅是告诉编译器您的程序引用了哪个库。当编译器处理到该指令时会在生成的目标文件中插入链接命令告诉链接器链入什么库。当然您还可以通过在链接器的命令行指定引入库名称的方法来达到和用includelib指令相同的目的,但考虑到命令行仅能够传递128个字符而且要不厌其烦地在命令行敲字符,所以这种方法是非常不可取的。

好了,现在保存例子,取名为msgbox.asm。把 ml.exe 的路径放到 PATH 环境变量中,键入下面一行 进行编译:

ml /c /coff /Cp msgbox。asm (译者注:命令行参数大小写是有区别的)

/c 是告诉MASM只编译不链接。这主要是考虑到在链接前您可能还有其他工作要做。
/coff 告诉MASM产生的目标文件用 coff 格式。MASM 的 coff 格式是COFF(Common Object File Format:通用目标文件格式) 格式的一种变体。在 UNIX 下的 COFF 格式又有不同。
/Cp 告诉 MASM 不要更改用户定义的标识符的大小写。若您用的是 hutch 的包含文件的话,在.model 指令下加入 "option casemap:none" 语句,可达到同样的效果。
当您成功的编译了 msgbox.asm 后,编译器会产生 msgbox.obj 目标文件,目标文件和可执行文件只一步之遥,目标文件中包含了以二进制形式存在的指令和数据,比可执行文件相差的只是链接器加入的重定位信息。

好,我们来链接目标文件:

link /SUBSYSTEM:WINDOWS /LIBPATH:c:\masm32\lib msgbox.obj

/SUBSYSTEM:WINDOWS 告诉链接器可执行文件的运行平台
/LIBPATH:〈path to import library〉 告诉链接器引入库的路径。
链接器做的工作就是根据引入库往目标文件中加入重定位信息,最后产生可执行文件。 既然得到了可执行文件,我们来运行一下。好,一、二、三,GO!屏幕上什么都没有。哦,对了,我们除了调用了 ExitProcess 函数外,甚麽都还没做呢!但是别一点成就感都没有哦,因为我们用汇编所写的是一个真正 Windows 程序,不信的话,查查您磁盘上的 msgbox.exe文件,在我的机器上它的大小足有1,536字节呢。

下面我们来做一点可以看的见摸的着的,我们在程序中加入一个对话框。该函数的原型如下:

MessageBox PROTO hwnd:DWORD, lpText:DWORD, lpCaption:DWORD, uType:DWORD

hWnd 是父窗口的句柄。句柄代表您引用的窗口的一个地址指针。它的值对您编 Windows 程序并不重要(译者注:如果您想成为高手则是必须的),您只要知道它代表一个窗口。当您要对窗口做任何操作时,必须要引用该窗口的指针。
lpText 是指向您要显示的文本的指针。指向文本串的指针事实上就是文本串的首地址。
lpCaption 是指向您要显示的对话框的标题文本串指针。
uType 是显示在对话框窗口上的小图标的类型。
下面是源程序

.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib

.data
MsgBoxCaption db "Iczelion Tutorial No.2",0
MsgBoxText db "Win32 Assembly is Great!",0

.code
start:
invoke MessageBox, NULL, addr MsgBoxText, addr MsgBoxCaption, MB_OK
invoke ExitProcess, NULL
end start

编译、链接上面的程序段,得到可执行文件。运行,哈哈,窗口上弹出了一个对话框,上面有一行字:“Win32 Assembly is Great!”。想一想,我们是用汇编写出来的,所以我们有理由为编写了一个最简单的 WIN32 程序感到高兴。(译者注:如果明天我们能够像在 DOS 下那样每一行都用汇编写,那我们有理由为自己感到自豪。)

好,我们回过头来看看上面的源代码。我们在.DATA“分段”定义了两个NULL结尾的字符串。我们用了两个常量:NULL 和 MB_OK。这些常量在windows.inc 文件中有定义,使用常量使得您的程序有较好的可读性。 addr 操作符用来把标号的地址传递给被调用的函数,它只能用在 invoke 语句中,譬如您不能用它来把标号的地址赋给寄存器或变量,如果想这样做则要用 offset 操作符。在 offset 和 addr 之间有如下区别:

addr不可以处理向前引用,offset则能。所谓向前引用是指:标号的定义是在invoke 语句之后,譬如在如下的例子:
invoke MessageBox,NULL, addr MsgBoxText,addr MsgBoxCaption,MB_OK

......

MsgBoxCaption db "Iczelion Tutorial No.2",0
MsgBoxText db "Win32 Assembly is Great!",0

如果您是用 addr 而不是 offset 的话,那 MASM 就会报错。
addr可以处理局部变量而 offset 则不能。局部变量只是在运行时在堆栈中分配内存空间。而 offset 则是在编译时由编译器解释,这显然不能用offset 在运行时来分配内存空间。编译器对 addr 的处理是先检查处理的是全局还是局部变量,若是全局变量则把其地址放到目标文件中,这一点和 offset 相同,若是局部变量,就在执行 invoke 语句前产生如下指令序列:
lea eax, LocalVar
push eax
因为lea指令能够在运行时决定标号的有效地址,所以有了上述指令序列,就可以保证 invoke 的正确执行了。


编辑:黑鹰 [发送给好友] [打印本页] [关闭窗口] [返回顶部]
上一篇:创建简单的窗口
下一篇:基本概念(win32)汇编教程
转载请注明来源:www.iyit.net
特别声明: 本站除部分特别声明禁止转载的专稿外的其他文章可以自由转载,但请务必注明出处和原始作者。文章版权归文章原始作者所有。对于被本站转载文章的个人和网站,我们表示深深的谢意。如果本站转载的文章有版权问题请联系编辑人员,我们尽快予以更正。

 相关文章
基本概念(win32)汇编教程 WINDOWS钩子函数 Win32调试API 第一部分
Win32调试API 第二部分 Win32调试API 第三部分 Win32汇编的环境和基础
WinRAR也能实现智能备份 深度发掘WinXP 制作自解压文件 用WinRAR软件突破超级兔子的限制
给WinRAR插上翅膀 让它运行得再快些 图片处理与网络管理的结合体winsuperki 常用电脑软件被遗忘的角落之WINRAR篇
WinRAR也可以管理我的桌面 巧用WinRAR让加密文件变颜色 新手上路 WinZip 10使用全新功能
强行破解加密的WinRAR文件 Windows Update 阶段教学 WINDOWS系列工具 画图 阶段教学
WINDOWS六天通 阶段教学(第一天) Windows Installer 阶段教学 Windows Server 2003小技巧两则
Windows命令行(DOS命令)教程 WINDOWS系列工具 msconfig [系统]WinXP总管 软件评测
最新更新 热点排行 推荐新闻
基本概念(win32)汇编教程
win32消息框
创建简单的窗口
绘制文本
处理键盘输入消息
基本概念(win32)汇编教程
win32消息框
创建简单的窗口
绘制文本
处理键盘输入消息
处理键盘输入消息
菜单
子窗口控件
以对话框为主要界面的应用程序
进一步学习对话框
基本概念(win32)汇编教程
win32消息框
创建简单的窗口
绘制文本
处理键盘输入消息
优秀公益广告作品欣赏(8)
java数据类型转换
Windows XP专业版IIS连接数的更改
新开放QQ免费挂级网站
免费在QQ上看在线电影电视听音乐
优秀公益广告作品欣赏(7)
WEB服务器配置全攻略(三)
QQ珊瑚虫外挂4.0版本发布!
免费把QQ炫铃设为本机QQ的系统提示音
Office2007简体中文版浮出水面 美图抢
 AMD处理器AM2测试 风扇竟运行7分钟
exeplorer.exe错误的问题的总结、解决
ASP.NET 2.0 中的异步页功能应用
硬盘坏道修复及数据恢复宝典
免费登录搜索引擎入口大全
搜索引擎注册九大秘法
小心摄像头成为黑客偷窥你的眼睛
内存混插常见问题和解决方法
Office2007简体中文版浮出水面 美图抢
0689版Windows Live Messenger五大看点
 友情链接
设置首 页 - 版权声明 - 广告服务 - 关于我们 - 联系我们 - 友情连接
Copyrights © 2004-2006 iYiT.Net All Rights Reserved.
网站合作、广告联系QQ:147007642、466949678
易特网络技术 点击这里给我发消息