??xml version="1.0" encoding="utf-8" standalone="yes"?> ?6位存放X坐标 If an application processes this message, it should return zero.
mov eax,lParam
and eax,0FFFFh Q高16位清0Q保留低16位,即X坐标
mov hitpoint.x,eax
mov eax,lParam
shr eax,16 QIParam逻辑右移16位,保留Y坐标
mov hitpoint.y,eax
mov MouseClick,TRUE
invoke InvalidateRect,hWnd,NULL,TRUELRESULT CALLBACK WindowProc(
HWND hwnd, // handle to window
UINTuMsg, // WM_LBUTTONDOWN
WPARAMwParam, // key indicator
LPARAMlParam // 鼠标的X,Y坐标
);
?6位存放Y坐标Return Values
and eax,ffff000 取高位?and eax,0FFFFh 取低?br />
点击此处下蝲全教E?/a>
本课中,我们学习如何在H口的客户区“绘制”字W串。我们还学习关于?font color="#0000ff">讑֤环境”的概念。点击这里下载实例here.
理论Q?/font>
在?zhn)在客户区“绘制”字W串前,(zhn)必MWindows那里得到(zhn)客户区的大,实(zhn)无法像在DOS下那样随心所Ʋ地在屏q上M地方“绘制”,l制前?zhn)必须得到Windows的允许,然后Windows会告诉?zhn)客户区的大小Q字体,颜色和其它GUI对象的属性。?zhn)可以用这些来在客户区“绘制”?/p>
什么是?font color="#0000ff">讑֤环境
Windows 中的文本是一个GUIQ图形用L面)对象。每一个字W实际上是由许多的像素点l成Q这些点在有W画的地Ҏ(gu)C出来,q样׃出现字符。这也是Z么我说“绘制”字W,而不是写字符。通常(zhn)都是在(zhn)应用程序的客户区“绘制”字W串Q尽?zhn)也可以在客户区外“绘制”)。Windows 下的“绘制”字W串Ҏ(gu)和DOS下的截然不同Q在DOS下,(zhn)可以把屏幕惌?85 x 25 的一个^面,而Windows下由于屏q上同时有几个应用程序的画面Q所以?zhn)必须严格遵从规范。Windows 通过把每一个应用程序限制在他的客户区来做到q一炏V当然客户区的大是可变的,(zhn)随时可以调整?/p>
“设备环境”中的有些属性和l图有关Q比如:颜色Q字体等。?zhn)可以随时改动那些~省|之所以保存缺省值是Z方便。?zhn)可以把“设备环境”想象成是Windows 为?zhn)准备的一个绘囄境,而?zhn)可以随时?gu)需要改变某些缺省属性?
当应用程序需要绘制时Q?zhn)必须得到一个“设备环境”的句柄。通常有几U方法?br />
call BeginPaint响应WM_PAINT消息
call GetDC 响应其他消息
call CreateDC 创徏你自q讑֤环境QDCQ?/p>
(zhn)必ȝ记的是,在处理单个消息后你必释䏀设备环境”句?/font>。不要在一个消息处理中获得“设备环境”句柄,而在另一个消息处理中在释攑֮?br />
我们在Windows 发?WM_PAINT 消息时处理绘制客户区QWindows 不会保存客户区的内容Q它用的是方法是“重l”机Ӟ比如当客户区刚被另一个应用程序的客户盖)QWindows 会把 WM_PAINT 消息攑օ该应用程序的消息队列。重l窗口的客户区是各个H口自己的责任,(zhn)要做的是在H口q程处理WM_PAINT 的部分知道绘制什么和何如l制?br />
(zhn)必M解的另一个概忉|?font color="#0000ff">无效区域”?font color="#0000ff">Windows 把一个最的需要重l的正方形区域叫做“无效区域”?/font>?Windows 发现了一个”无效区域“后Q它?yu)׃向该应用E序发送一?WM_PAINT 消息Q在 WM_PAINT 的处理过E中Q窗口首先得C个有关绘囄l构体,里面包括无效区的坐标位置{。?zhn)可以通过调用BeginPaint 让“无效区”有效,如果(zhn)不处理 WM_PAINT 消息Q至要调用~省的窗口处理函?DefWindowProc Q或者调?ValidateRect 让“无效区”有效。否则?zhn)的应用程序将会收到无Ih的 WM_PAINT 消息?br />
下面是响应WM_PAINT消息的步骤:
1. 使用BeginPaint取得“设备环境”句?/font>
2. l制客户?/font>
3. 使用EndPaint释放“设备环境”句?/font>
注意Q?zhn)无须昑ּ地让“无效区”有效,q个动作?BeginPaint 自动完成。?zhn)可以?BeginPaint ?Endpaint 之间Q调用所有的l制函数。几乎所有的GDI 函数都需要“设备环境”的句柄作ؓ参数?br />
内容Q?br />
我们写一个应用程序,它会在客户区的中心显CZ?"Win32 assembly is great and easy!"
.386
.model flat,stdcall
option casemap:none
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
include \masm32\include\windows.inc
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
.DATA
ClassName db "SimpleWinClass",0
AppName db "Our First Window",0
OurText db "Win32 assembly is great and easy!",0
.DATA?
hInstance HINSTANCE ?
CommandLine LPSTR ?
.CODE
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
mov CommandLine,eax
invoke WinMain, hInstance, NULL, CommandLine, SW_SHOWDEFAULT
invoke ExitProcess,eax
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
mov wc.cbSize, SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra, NULL
mov wc.cbWndExtra, NULL
push hInst
pop wc.hInstance
mov wc.hbrBackground, COLOR_WINDOW+1
mov wc.lpszMenuName,NULL
mov wc.lpszClassName,OFFSET ClassName
invokeLoadIcon, NULL, IDI_APPLICATION
mov wc.hIcon,eax
mov wc.hIconSm,eax
invokeLoadCursor, NULL, IDC_ARROW
mov wc.hCursor,eax
invoke RegisterClassEx, addr wc
invokeCreateWindowEx,NULL, ADDR ClassName,ADDR AppName,\
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\
hInst,NULL
mov hwnd,eax
invokeShowWindow, hwnd, SW_SHOWNORMAL
invokeUpdateWindow, hwnd
.WHILETRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK.IF (!eax)
invoke TranslateMessage, ADDR msg
invokeDispatchMessage, ADDR msg
.ENDW
mov eax, msg.wParam
ret
WinMain endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL hdc:HDC
LOCAL ps:PAINTSTRUCT
LOCAL rect:RECT
.IF uMsg==WM_DESTROY
invokePostQuitMessage, NULL
.ELSEIF uMsg==WM_PAINT
invoke BeginPaint, hWnd, ADDR ps
mov hdc,eax
invokeGetClientRect, hWnd, ADDR rect
invoke DrawText, hdc, ADDR OurText,-1, ADDR rect, \
DT_SINGLELINE or DT_CENTER or DT_VCENTER
invoke EndPaint, hWnd, ADDR ps
.ELSE
invoke DefWindowProc, hWnd,uMsg, wParam,lParam
ret
.ENDIF
xor eax, eax
ret
WndProc endp
end start
分析Q?br />
q里的大多数代码和第三课中的一栗我只解释其中一些不相同的地斏V?/p>
LOCAL hdc:HDC
LOCAL ps:PAINTSTRUCT
LOCAL rect:RECT
q些局部变量由处理 WM_PAINT 消息中的 GDI 函数调用。hdc 用来存放调用 BeginPaint q回的“设备环境”句柄。ps 是一?PAINTSTRUCT 数据cd的变量。通常(zhn)不会用到其中的许多|它由 Windows 传递给 BeginPaintQ在l束l制后再原封不动的传递给 EndPaint。rect 是一?RECT l构体类型参敎ͼ它的定义如下Q?/p>
RECT Struct
left LONG ?
top LONG ?
right LONG ?
bottom LONG ?
RECT ends
left ?top 是正方Ş左上角的坐标。right ?bottom 是正方Ş右下角的坐标。客户区的左上角的坐标是 x=0Qy=0Q这样对?x=0Qy=10 的坐标点在它的下面?/p>
invoke BeginPaint,hWnd, ADDR ps
mov hdc,eax
invoke GetClientRect,hWnd, ADDR rect
invoke DrawText, hdc,ADDR OurText,-1, ADDR rect, \
DT_SINGLELINE or DT_CENTER or DT_VCENTER
invoke EndPaint,hWnd, ADDR ps
在处?WM_PAINT 消息Ӟ(zhn)调用BeginPaint函数Q传l它一个窗口句柄和未初始化?PAINTSTRUCT 型参数。调用成功后?eax 中返回“设备环境”的句柄。下一ơ,调用 GetClientRect 以得到客户区的大,大小攑֜ rect 中,然后把它传给 DrawText。DrawText 的语法如下:
DrawText proto hdc:HDC, lpString:DWORD, nCount:DWORD, lpRect:DWORD, uFormat:DWORD
DrawText是一个高层的调用函数。它能自动处理像换行、把文本攑ֈ客户Z间等q些杂事。所以?zhn)只管集中_֊“绘制”字W串可以了。我们会在下一课中讲解低一层的函数 TextOutQ该函数在一个正方Ş区域中格式化一个文本串。它用当前选择的字体、颜色和背景艌Ӏ它处理换行以适应正方形区域。它会返回以讑֤逻辑单位度量的文本的高度Q我们这里的度量单位是像素点。让我们来看一看该函数的参敎ͼ
Hdc : “设备环境”的句柄?br />
lpString : 要显C的文本Ԍ该文本串要么以NULLl尾Q要么在nCount中指出它的长短?/p>
nCount:要输出的文本的长度。若以NULLl尾Q该参数必须?1?
lpRect : 指向要输出文本串的正方Ş区域的指针,该方形必L一个裁剪区Q也是说超q该区域的字W将不能昄?
uFormat : 指定如何昄。我们可以用 OR把以下标志或C块:
DT_SINGLELINE 单行昄?/p>
DT_CENTER 水^居中?/p>
DT_VCENTER 垂直居中?/p>
l束l制后,必须调用 EndPaint 释放“设备环境”的句柄?好了Q现在我们把“绘制”文本串的要Ҏ(gu)ȝ如下Q?br />
1. 必须在开始和l束处分别调?font color="#ff0000">BeginPaint?font color="#ff0000">EndPaintQ?/font>
2.?font color="#ff0000">BeginPaint?font color="#ff0000">EndPaint之间调用所有的l制函数Q?
3. 如果在其它的消息处理中重新绘制客户区Q?zhn)可以有两U选择Q?/p>
(1) 用GetDC和ReleaseDC代替BeginPaint和EndPaintQ?/p>
(2) 调用InvalidateRect?font color="#0000ff">UpdateWindow让客户区无效Q这迫使WINDOWS?font color="#ff0000">WM_PAINT攑օ应用E序消息队列Q从而得客户区重绘
下面我就列出在桌面显CZ个窗口的几个步骤Q?/p>
1. 得到(zhn)应用程序的实例句柄(必需)Q?/font>
2. 得到命o行参?如果(zhn)想从命令行得到参数Q可?/font>)Q?/font>
3. 注册H口c?/font>(必需,除非(zhn)用Windows预定义的H口c??MessageBox,Dialogbox)
4. 创徏H口(必需)Q?/p>
5. 在桌面显C窗?/font>(必需Q除非?zhn)不想立即昄?Q?/p> 6. hH口客户?/font>Q?/p> 7. q入无限的获取窗口消息的循环Q?/p> 8. 如果有消息到达,p责该H口的窗口回调函数处?/font>Q?/p> 9. 如果用户关闭H口Q进行退出处?/font>?/p> 可以看的?相对于单用户的DOS下的~程来说,Windows下的E序框架l构是相当复杂的.但是Windows和DOS在系l架构上是截然不同的.Windows是一个多d的操作系l故pȝ中同时有多个应用E序彼此协同q行.q就要求WindowsE序员必M格遵守编E规?q养成良好的~程风格?br /> (zhn)应当把E序中要用到?font color="#000000">所有常量和l构体的声明攑ֈ一个头文g?/font>Qƈ且在源程序的开始处包含q个头文件。这么做会节省(zhn)大量的旉Q也免得一ơ又一ơ的敲键盘。目前,最完善的头文g是hutch写的Q?zhn)可以到hutch或我的网站下载。?zhn)也可以定义(zhn)自己的常量和l构体,但最好把它们攑ֈ独立的头文g中用includelib指oQ包含?zhn)的程序要引用的库文gQ譬如:若?zhn)的程序要调?"MessageBox"Q?(zhn)就应当在源文g中加入如下一行: includelib user32.lib q条语句告诉 MASM (zhn)的E序要用到一些引入库。如果?zhn)不止引用一个库Q只要简单地加入includelib语句Q不要担心链接器如何处理q么多的库,只要在链接时用链接开?/LIBPATH 指明库所在的路径卛_? WinMain proto :DWORD,:DWORD,:DWORD,:DWORD .DATA ;初始化数?br />ClassName db "SimpleWinClass",0 ; WindowsH口cȝ名字 .DATA? ; 未初始化数据 WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD mov wc.cbSize,SIZEOF WNDCLASSEX ; 填入wcl构的大? .WHILE TRUE ; q入消息循环 WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM end start 下面我们开始分析,(zhn)可得做好思想准备Q这可不是一件太L的活? .386 WinMain proto :DWORD,:DWORD,:DWORD,:DWORD include \masm32\include\windows.inc (zhn)可以把前三行看成是"必须"的?br />.386告诉MASN我们要用80386指o集?br />. model flat,stdcall告诉MASM 我们用的内存d模式Q此处也可以加入stdcall告诉MASM我们所用的参数传递约定?br /> 和MASM 相比QTASM 则要单得多,(zhn)只要引入一个库Q即Qimport32.lib?/p> .DATA .DATA? 接下来是"DATA"分段"?br /> ?.DATA? 中放了两个未q行初始化的变量Q其?hInstance 代表应用E序的句柄,CommandLine 保存从命令行传入的参数?/p> HINSTACE ?LPSTR 是两个数据类型名Q它们在头文件中定义Q可以看做是 DWORD 的别名,之所以要q么重新定义仅是Z易记。?zhn)可以查?windows.inc 文gQ在 .DATA? 中的变量都是未经初始化的Q这也就是说在程序刚启动时它们的值是什么无关紧要,只不q占有了一块内存,以后可以再利用而已?/p> .CODE .CODE "分段"包含了?zhn)应用E序的所有代码,q些代码必须都在 .code ?end 之间。至?label 的命名只要遵从MASM规范而且保证唯一则具体叫什么倒是无所谓?/p> 我们E序的第一条语句是调用 GetModuleHandle L找我们应用程序的句柄。在Win32下,应用E序的句柄和模块的句柄是一L。?zhn)可以把实例句柄看成是?zhn)的应用E序?ID 受我们在调用几个函数旉把它作ؓ参数来进行传递,所以在一开始便得到q保存它?yu)可以省许多的事?/p> 特别注意QWIN32下的实例句柄实际上是(zhn)应用程序在内存中的U性地址?/p> WIN32 中函数的函数如果有返回|那它是通过 eax 寄存器来传递的。其他的值可以通过传递进来的参数地址q行q回?/p> 一?WIN32 函数被调用时M保存好段寄存器和 ebxQediQesi和ebp 寄存器,?ecx和edx 中的值L不定的,不能在返回时应用?/p> 特别注意Q从Windows API 函数中返回后QeaxQecxQedx 中的值和调用前不一定相同?/p> 当函数返回时Q返回值放在eax中。如果?zhn)应用E序中的函数提供l?Windows 调用Ӟ也必遵守这一点,卛_函数入口处保存段寄存器和 ebxQespQesiQedi 的值ƈ在函数返回时恢复。如果不q样一来的话,(zhn)的应用E序很快会崩溃。从(zhn)的E序中提供给 Windows 调用的函数大体上有两U:Windows H口q程?Callback 函数。如果?zhn)的应用程序不处理命o行那么就无须调用 GetCommandLineQ这里只是告诉?zhn)如果要调用应该怎么做? 下面则是调用WinMain了。该函数共有4个参敎ͼ应用E序的实例句柄,该应用程序的前一实例句柄Q命令行参数串指针和H口如何昄。Win32 没有前一实例句柄的概念,所以第二个参数Mؓ0。之所以保留它是ؓ了和 Win16 兼容的考虑Q在 Win16下,如果 hPrevInst ?NULLQ则该函数是W一ơ运行?/p> 特别注意Q?zhn)不用必须x一个名?WinMain 函数Q事实上在这斚w(zhn)可以完全作主,(zhn)甚xL一个和 WinMain {同的函数。?zhn)只要?WinMain 中的代码拷到GetCommandLine 之后Q其所实现的功能完全相同。在 WinMain q回Ӟ把返回码攑ֈ eax 中。然后在应用E序l束旉过 ExitProcess 函数把该q回码传递给 Windows ?/p> WinMain proc Inst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD 上面是WinMain的定义。注意跟?proc 指o后的parameterQtype形式的参敎ͼ它们是由调用者传l?WinMain 的,我们引用是直接用参数名即可。至于压栈和退栈时的^衡堆栈工作由 MASM 在编译时加入相关的前序和后序汇编指o来进行?/p> LOCAL wc:WNDCLASSEX LOCAL 伪指令ؓ局部变量在栈中分配内存I间Q所有的 LOCAL 指o必须紧跟?PROC 之后。LOCAL 后跟声明的变量,其Ş式是 变量?变量cd。譬?LOCAL wcQWNDCLASSEX x告诉 MASM 为名字叫 wc 的局部边量在栈中分配长度?WNDCLASSEX l构体长度的内存I间Q然后我们在用该局部变量是无须考虑堆栈的问题,考虑?DOS 下的汇编Q这不能不说是一U恩赐。不q这p求这L明的局部变量在函数l束旉放栈I间Q?也即不能在函C外被引用)Q另一个缺Ҏ(gu)(zhn)因不能初始化?zhn)的局部变量,不得不在E后另外再对其赋倹{?/p> mov wc.cbSize,SIZEOF WNDCLASSEX 上面几行从概念上说确实是非常地简单。只要几行指令就可以实现。其中的主要概念是H口c(window classQ,一个窗口类是一个有关窗口的规范Q这个规范定义了几个主要的窗口的元素Q如Q图标、光标、背景色、和负责处理该窗口的函数。?zhn)产生一个窗口时必要有这L一个窗口类。如果?zhn)要生不止一个同U类型的H口Ӟ最好的Ҏ(gu)是把这个窗口类存储hQ这U方法可以节U许多的内存I间。也总天?zhn)不会太感觉到Q可是想想以?PC 大多数只?1M 内存Ӟq么做是非常有必要的。如果?zhn)要定义自q创徏H口cd必须Q在一?WINDCLASS ?WINDOWCLASSEXE l构体中指明(zhn)窗口的l成元素Q然后调?RegisterClass ?RegisterClassEx Q再Ҏ(gu)该窗口类产生H口。对不同特色的窗口必d义不同的H口cR?WINDOWS有几个预定义的窗口类Q譬如:按钮、编辑框{。要产生该种风格的窗口无预先再定义H口cMQ只要包预定义类的类名作为参数调?CreateWindowEx 卛_?/p> WNDCLASSEX 中最重要的成员莫q于lpfnWndProc了。前~ lpfn 表示该成员是一个指向函数的长指针。在 Win32中由于内存模式是 FLAT 型,所以没?near ?far 的区别。每一个窗口类必须有一个窗口过E,?Windows 把属于特定窗口的消息发送给该窗口时Q该H口的窗口类负责处理所有的消息Q如键盘消息或鼠标消息。由于窗口过E差不多地处理了所有的H口消息循环Q所以?zhn)只要在其中加入消息处理过E即可。下面我要讲解 WNDCLASSEX 的每一个成?/p> WNDCLASSEX STRUCT DWORD cbSizeQ?/font>WNDCLASSEX 的大。我们可以用sizeof(WNDCLASSEX)来获得准的倹{?/p> styleQ从q个H口cL生的H口h的风?(zhn)可以用"or"操作W来把几个风格或C赗? lpfnWndProcQ?/font>H口处理函数的指?/p> cbClsExtraQ?/font>指定紧跟在窗口类l构后的附加字节数。不使用的话其值就?Q?/p> cbWndExtraQ?/font>指定紧跟在窗口实例后的附加字节数。如果一个应用程序在资源中用CLASS伪指令注册一个对话框cL,则必Lq个成员设成DLGWINDOWEXTRA,不用的话其值就?Q?/p> hInstanceQ?/font>本模块的实例句柄?/p> hIconQ?/font>图标的句柄?/p> hCursorQ?/font>光标的句柄?/p> hbrBackgroundQ?/font>背景d的句柄?/p> lpszMenuNameQ?/font>指向菜单的指针?/p> lpszClassNameQ?/font>指向cdU的指针?/p> hIconSmQ?/font>和窗口类兌的小图标。如果该gؓNULL。则把hCursor中的图标转换成大合适的图标?/p> invoke CreateWindowEx, NULL,\ 注册H口cdQ我们将调用CreateWindowEx来生实际的H口。请注意该函数有12个参数?/p> CreateWindowExA proto dwExStyle:DWORD,\ 我们来仔l看一看这些的参数Q?/p> dwExStyleQ?/font>附加的窗口风根{相对于旧的CreateWindowq是一个新的参数。在9X/NT中?zhn)可以使用新的H口风格。?zhn)可以在Style中指定一般的H口风格Q但是一些特D的H口风格Q如层H口则必d此参C指定。如果?zhn)不想指定M特别的风|则把此参数设为NULL? lpClassName:Q?/font>必须Q。ASCIIZ形式的窗口类名称的地址。可以是(zhn)自定义的类Q也可以是预定义的类名。像上面所_每一个应用程序必L一个窗口类? lpWindowNameQ?/font>ASCIIZ形式的窗口名U的地址。该名称会显C在标题条上。如果该参数I白Q则标题条上什么都没有?/p> dwStyleQ?/font>H口的风根{在此?zhn)可以指定H口的外观。可以指定该参数为零Q但那样该窗口就没有pȝ菜单Q也没有最大化和最化按钮Q也没有关闭按钮Q那h不得不按Alt+F4 来关闭它。最为普遍的H口c风格是 WS_OVERLAPPEDWINDOW?一U窗口风格是一U按位的掩码Q这h可以用“or”把(zhn)希望的H口风格或v来。像 WS_OVERLAPPEDWINDOW 是由几U最Z便普遍的风格或v来的? XQYQ?/font>指定H口左上角的以像素ؓ单位的屏q坐标位|。缺省地可指定ؓ CW_USEDEFAULTQ这?Windows 会自动ؓH口指定最合适的位置? nWidthQ?nHeightQ?/font>以像素ؓ单位的窗口大。缺省地可指定ؓ CW_USEDEFAULTQ这?Windows 会自动ؓH口指定最合?适的大小? hWndParentQ?/font>父窗口的句柄Q如果有的话Q。这个参数告?Windows q是一个子H口和他的父H口是谁。这?MDIQ多文档l构Q不同,此处的子H口q不会局限在父窗口的客户区内。他只是用来告诉 Windows 各个H口之间的父子关p,以便在父H口销毁是一同把其子H口销毁。在我们的例子程序中因ؓ只有一个窗口,故把该参数设?NULL? hMenuQ?/font>WINDOWS菜单的句柄。如果只用系l菜单则指定该参CؓNULL。回头看一看WNDCLASSEX l构中的 lpszMenuName 参数Q它也指定一个菜单,q是一个缺省菜单,M从该H口cL生的H口若想用其他的菜单需在该参数中重新指定。其实该参数有双重意义:一斚w若这是一个自定义H口时该参数代表菜单句柄Q另一斚wQ若q是一个预定义H口Ӟ该参C表是该窗口的 ID 受Windows 是根据lpClassName 参数来区分是自定义窗口还是预定义H口的? hInstanceQ?/font> 产生该窗口的应用E序的实例句柄?/p> lpParamQ(可选)指向Ʋ传l窗口的l构体数据类型参数的指针。如在MDI中在产生H口时传?CLIENTCREATESTRUCT l构的参数。一般情况下Q该值MؓӞq表C没有参C递给H口。可以通过GetWindowLong 函数索该倹{? mov hwnd,eax 调用CreateWindowEx成功后,会q回H口句柄于eax中。我们必M存该g备后用。我们刚刚生的H口不会自动昄Q所以必调?ShowWindow 来按照我们希望的方式来显CH口。接下来调用 UpdateWindow 来更新客户区?/p> .WHILE TRUE q时候我们的H口已显C在屏幕上了。但是它q不能从外界接收消息。所以我们必ȝ它提供相关的消息。我们是通过一个消息@环来完成该项工作的。每一个模块仅有一个消息@环,我们不断地调?GetMessage ?Windows 中获得消息。GetMessage 传递一?MSG l构体给 Windows Q然?Windows 在该函数中填充有关的消息Q一直到 Windows 扑ֈq填充好消息?GetMessage 才会q回。在q段旉内系l控制权可能会{Uȝ其他的应用程序。这样就构成了Win16下的多Q务结构?/p> 如果 GetMessage 接收?WM_QUIT 消息后就会返?FALSEQ循环l束q出应用程序?/p> TranslateMessage 函数是一个是实用函数Q它从键盘接受原始按键消息,然后解释?WM_CHARQ在?WM_CHAR 攑օ消息队列Q由于经q解释后的消息中含有按键?ASCII 码,q比原始的扫描码好理解得多。如果?zhn)的应用程序不处理按键消息的话Q可以不调用该函数。 ? DispatchMessage 会把消息发送给负责该窗口过E的函数?/p> mov eax,msg.wParam 如果消息循环l束了,退出码存放?MSG 中的 wParam中,(zhn)可以通过把它攑ֈ eax 寄存器中传给 Windows目前 Windows 没有利用到这个结束码Q但我们最好还是遵从Windows规范已防意外?/p> WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM 是我们的H口处理函数。?zhn)可以随便l该函数命名。其中第一个参?hWnd 是接收消息的H口的句柄。uMsg 是接收的消息。注?uMsg 不是一?MSG l构Q其实上只是一?DWORD cd数。Windows 定义了成百上千个消息Q大多数(zhn)的应用E序不会处理到。当有该H口的消息发生时QWindows 会发送一个相x息给该窗口。其H口q程处理函数会智能的处理q些消息。wParam ?lParam 只是附加参数Q以方便传递更多的和该消息有关的数据?/p> .IF uMsg==WM_DESTROY 上面可以说是关键部分。这也是我们?Windows E序旉要改写的主要部分。此处?zhn)的程序检?Windows 传递过来的消息Q如果是我们感兴的消息则加以处理,处理完后Q在 eax 寄存器中传?0Q否则必调?DefWindowProcQ把该窗口过E接收到的参C递给~省的窗口处理函数?/p> 所有消息中(zhn)必d理的?WM_DESTROYQ当(zhn)的应用E序l束?Windows 把这个消息传递进来,当?zhn)的应用程序解说到该消息时它已l在屏幕上消׃Q这仅是通知(zhn)的应用E序H口已销毁,(zhn)必自己准备返?Windows 。在此消息中(zhn)可以做一些清理工作,但无法阻止退出应用程序。如果?zhn)要那样做的话Q可以处?WM_CLOSE 消息。在处理完清理工作后Q?zhn)必须调?PostQuitMessageQ该函数会把 WM_QUIT 消息传回(zhn)的应用E序Q而该消息会?GetMessage q回Qƈ?eax 寄存器中攑օ 0Q然后会l束消息循环q?WINDOWS。?zhn)可以在(zhn)的程序中调?DestroyWindow 函数Q它会发送一?WM_DESTROY 消息l?zhn)自己的应用程序,从而迫使它退出?br /> [ Iczelion's Win32 Assembly HomePage] 需要代理服务器Q但可通过以下镜像q行讉K
内容Q?/font>
下面是我们简单的H口E序的源代码。在q入复杂的细节前Q我指出几点要点以化你的设计:
在其它地方运用头文g中定义函数原型,常数和结构体Ӟ要严g持和头文件中的定义一_包括大小写。在查询函数定义Ӟq将节约(zhn)大量的旉Q?
在编译,链接时用makefile文gQ免去重复敲键?
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib ; 指明要调用user32.lib和kernel32.lib 两个库中的函?br />include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
AppName db "Our First Window",0 ; H口的名?
hInstance HINSTANCE ? ;本程序的实例句柄
CommandLine LPSTR ?
.CODE ; 从这里开始我们的代码
start:
invoke GetModuleHandle, NULL ;获得本程序实例句?/font>,在WIN32下hmodule==hinstance
mov hInstance,eax
invoke GetCommandLine ; 得到命o行参?如果你的E序不处理命令行无需调用此函?br />mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT ; 调用d?
invoke ExitProcess, eax ; 退出程序,退出码由WinMain函数q回Ceax
LOCAL wc:WNDCLASSEX ; 创徏局部变?br /> LOCAL msg:MSG
LOCAL hwnd:HWND
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,NULL
push hInstance
pop wc.hInstance
mov wc.hbrBackground,COLOR_WINDOW+1
mov wc.lpszMenuName,NULL
mov wc.lpszClassName,OFFSET ClassName
invoke LoadIcon,NULL,IDI_APPLICATION
mov wc.hIcon,eax
mov wc.hIconSm,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor,eax
invoke RegisterClassEx, addr wc ; 注册H口c?/font>
invoke CreateWindowEx,NULL,\ Q?font color="#0000ff">创徏H口
ADDR ClassName,\
ADDR AppName,\
WS_OVERLAPPEDWINDOW,\
CW_USEDEFAULT,\
CW_USEDEFAULT,\
CW_USEDEFAULT,\
CW_USEDEFAULT,\
NULL,\
NULL,\
hInst,\
NULL
mov hwnd,eax ;CreatwindowEx成功调用后返回到eax中的H口句柄攑օ到hwnd保存
invoke ShowWindow, hwnd,CmdShow ; 在桌Ҏ(gu)C窗?
invoke UpdateWindow, hwnd ; 更新客户?
invoke GetMessage, ADDR msg,NULL,0,0 ;从消息队列获取消?攑օeax
.BREAK .IF (!eax) ;有消息l执?无消息则退出@?br /> invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.ENDW
mov eax,msg.wParam ; 退出码q回到eax?
ret
WinMain endp
.IF uMsg==WM_DESTROY ; 如果用户点击了关闭按?br /> invoke PostQuitMessage,NULL ; 退出应用程?br /> .ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam ; ~省的窗口消息处理函数?br /> ret
.ENDIF
xor eax,eax
ret
WndProc endp
分析Q?/font>
你一定会吃惊一个简单的H口E序竟然会有如此之多的代码,但是(zhn)必要知道上面的大多数代码都是模板而已Q模板的意思是指这些代码对差不多所有标?Windows E序来说都是相同的。在?Windows E序时?zhn)可以把这些代码拷来拷去,当然把这些重复的代码写到一个库中也挺好。其实真正要写的代码集中?WinMain 中。这和一?C ~译器一P无须要关心其它杂务,集中_֊?WinMain 函数。唯一不同的是 C ~译器要求?zhn)的源代码有必L一个函数叫 WinMain。否?QC 无法知道哪个函数和有关的前后代码链接。相对CQ汇~语a提供了较大的灉|性,它不要求一个叫 WinMain 的函数?/p>
.model flat,stdcall
option casemap:none
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
接下来是函数 WinMain 的原型申明,因ؓ我们E后要用到该函数Q故必须先声明。?br />
我们必须包含window.inc文gQ因为其中包含大量要用到的常量和l构的定义,该文件是一个文本文Ӟ(zhn)可以用M文本~辑器打开它,window.incq没有包含所有的帔R和结构定义,不过 hutch 和我一直在不断加入新的内容。如果暂时在 window.inc 找不刎ͼ(zhn)也可以自行加入?br />
我们的程序调用驻扎在user32.dll (譬如QCreateWindowExQ?RegisterWindowClassEx) ?kernel32.dll (ExitProcess)中的函数Q所以必链接这两个库。接下来我如果问Q?zhn)需要把什么库铑օ(zhn)的E序?? {案是:先查到?zhn)要调用的函数在什么库中,然后包含q来。譬如:若?zhn)要调用的函数?gdi32.dll 中,(zhn)就要包含gdi32.inc头文件?/p>
ClassName db "SimpleWinClass",0
AppName db "Our First Window",0
hInstance HINSTANCE ?
CommandLine LPSTR ?
?.DATA 中我们定义了两个?NULL l尾的字W串 (ASCIIZ)Q其?ClassName ?Windows cdQ?font color="#0000ff">AppName
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
invoke ExitProcess,eax
.....
end start
LOCAL msg:MSG
LOCAL hwnd:HWND
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,NULL
push hInstance
pop wc.hInstance
mov wc.hbrBackground,COLOR_WINDOW+1
mov wc.lpszMenuName,NULL
mov wc.lpszClassName,OFFSET ClassName
invoke LoadIcon,NULL,IDI_APPLICATION
mov wc.hIcon,eax
mov wc.hIconSm,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor,eax
invoke RegisterClassEx, addr wc
cbSize DWORD ?
style DWORD ?
lpfnWndProc DWORD ?
cbClsExtra DWORD ?
cbWndExtra DWORD ?
hInstance DWORD ?
hIcon DWORD ?
hCursor DWORD ?
hbrBackground DWORD ?
lpszMenuName DWORD ?
lpszClassName DWORD ?
hIconSm DWORD ?
WNDCLASSEX ENDS
ADDR ClassName,\
ADDR AppName,\
WS_OVERLAPPEDWINDOW,\
CW_USEDEFAULT,\
CW_USEDEFAULT,\
CW_USEDEFAULT,\
CW_USEDEFAULT,\
NULL,\
NULL,\
hInst,\
NULL
lpClassName:DWORD,\
lpWindowName:DWORD,\
dwStyle:DWORD,\
X:DWORD,\
Y:DWORD,\
nWidth:DWORD,\
nHeight:DWORD,\
hWndParent:DWORD ,\
hMenu:DWORD,\
hInstance:DWORD,\
lpParam:DWORD
invoke ShowWindow, hwnd,CmdShow
invoke UpdateWindow, hwnd
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.ENDW
ret
WinMain endp
invoke PostQuitMessage,NULL
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc endp
http://win32assembly.online.fr/ http://users.daex.ufsc.br/~iczelion/ http://spiff.tripnet.se/~Iczelion
译Q?/span>
Lxx
校对Q?/span>
LuoYunBin's Win32 ASM Page
Q?/span>
http://asm.yeah.net