??xml version="1.0" encoding="utf-8" standalone="yes"?>亚洲精品动漫人成3d在线 ,久久99国产亚洲精品观看,亚洲av成人一区二区三区观看在线http://www.tkk7.com/cuiyuelei/category/19563.htmlW渐不闻声渐(zhn)?多情却被无情?zh-cnTue, 24 Jul 2007 15:07:40 GMTTue, 24 Jul 2007 15:07:40 GMT60处理鼠标消息http://www.tkk7.com/cuiyuelei/archive/2007/07/24/132026.html催月?Jaclick)催月?Jaclick)Tue, 24 Jul 2007 05:11:00 GMThttp://www.tkk7.com/cuiyuelei/archive/2007/07/24/132026.htmlhttp://www.tkk7.com/cuiyuelei/comments/132026.htmlhttp://www.tkk7.com/cuiyuelei/archive/2007/07/24/132026.html#Feedback0http://www.tkk7.com/cuiyuelei/comments/commentRss/132026.htmlhttp://www.tkk7.com/cuiyuelei/services/trackbacks/132026.html  .ELSEIF uMsg== WM_LBUTTONDOWN
        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,TRUE

LRESULT CALLBACK WindowProc(
  HWND hwnd,       // handle to window
  UINTuMsg,       // WM_LBUTTONDOWN
  WPARAMwParam,   // key indicator
  LPARAMlParam    // 鼠标的X,Y坐标
);








lParam参数只能?2位?/i>

?6位存放X坐标
        ?6位存放Y坐标

Return Values

If an application processes this message, it should return zero.


and  eax,ffff000 取高位?and eax,0FFFFh  取低?br />





催月?Jaclick) 2007-07-24 13:11 发表评论
]]>
Iczelion's Win32 Assembly Tutorial 4: l制文本http://www.tkk7.com/cuiyuelei/archive/2007/04/14/110689.html催月?Jaclick)催月?Jaclick)Sat, 14 Apr 2007 12:34:00 GMThttp://www.tkk7.com/cuiyuelei/archive/2007/04/14/110689.htmlhttp://www.tkk7.com/cuiyuelei/comments/110689.htmlhttp://www.tkk7.com/cuiyuelei/archive/2007/04/14/110689.html#Feedback0http://www.tkk7.com/cuiyuelei/comments/commentRss/110689.htmlhttp://www.tkk7.com/cuiyuelei/services/trackbacks/110689.html译QLxxQ校对:LuoYunBin's Win32 ASM PageQ?a >http://asm.yeah.net

点击此处下蝲全教E?/a>


本课中,我们学习如何在H口的客户区“绘制”字W串。我们还学习关于?font color="#0000ff">讑֤环境”的概念。点击这里下载实例here.

理论Q?/font>

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>

在?zhn)在客户区“绘制”字W串前,(zhn)必MWindows那里得到(zhn)客户区的大,实(zhn)无法像在DOS下那样随心所Ʋ地在屏q上M地方“绘制”,l制前?zhn)必须得到Windows的允许,然后Windows会告诉?zhn)客户区的大小Q字体,颜色和其它GUI对象的属性。?zhn)可以用这些来在客户区“绘制”?/p>

 什么是?font color="#0000ff">讑֤环境”(DCQ呢Q它其实是由Windows内部l护的一个数据结构。一个“设备环境”和一个特定的讑֤相连。像打印机和昄器。对于显C器来说,“设备环境”和一个个特定的窗口相q?/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

tut4.gif


分析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">EndPaint
Q?/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从而得客户区重绘


[
Iczelion's Win32 Assembly HomePage] 需要代理服务器Q但可通过以下镜像q行讉K
http://win32assembly.online.fr/                  http://users.daex.ufsc.br/~iczelion/   http://spiff.tripnet.se/~Iczelion


催月?Jaclick) 2007-04-14 20:34 发表评论
]]>
Iczelion's Win32 Assembly Tutorial 3: 一个简单的H口http://www.tkk7.com/cuiyuelei/archive/2007/03/13/103665.html催月?Jaclick)催月?Jaclick)Tue, 13 Mar 2007 15:37:00 GMThttp://www.tkk7.com/cuiyuelei/archive/2007/03/13/103665.htmlhttp://www.tkk7.com/cuiyuelei/comments/103665.htmlhttp://www.tkk7.com/cuiyuelei/archive/2007/03/13/103665.html#Feedback0http://www.tkk7.com/cuiyuelei/comments/commentRss/103665.htmlhttp://www.tkk7.com/cuiyuelei/services/trackbacks/103665.html译QLxxQ校对:LuoYunBin's Win32 ASM PageQ?a >http://asm.yeah.net

点击此处下蝲全教E?/a>


在本课中我们写一个Windows E序,它会在桌面显CZ个标准的H口.点击q里下蝲例程
here.

理论Q?/font>

WindowsE序?在写囑Ş用户界面旉要调用大量的标准Windows GUI函数。其实这对用户和E序员来说都有好?对于用户,面对的是同一套标准的H口,对这些窗口的操作都是一L,所以用不同的应用E序时无重新学习操?对程序员来说,q些GUI源代码都是经q了微Y的严格测?随时拿来可以用?当然至于具体地写E序对于E序员来说还是有隑ֺ?Z创徏ZH口的应用程?必须严格遵守规范.作到q一点ƈ不难,只要用模块化或OOP~程Ҏ(gu)卛_.

下面我就列出在桌面显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 />
内容Q?/font>

下面是我们简单的H口E序的源代码。在q入复杂的细节前Q我指出几点要点以化你的设计:

(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 指明库所在的路径卛_?

在其它地方运用头文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

WinMain proto :DWORD,:DWORD,:DWORD,:DWORD

.DATA                     ;初始化数?br />ClassName db "SimpleWinClass",0        ; WindowsH口cȝ名字
AppName db "Our First Window",0        ; H口的名?

.DATA?                ; 未初始化数据
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

WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
    LOCAL wc:WNDCLASSEX                                            ; 创徏局部变?br />    LOCAL msg:MSG
    LOCAL hwnd:HWND

    mov   wc.cbSize,SIZEOF WNDCLASSEX                   ; 填入wcl构的大?
    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                                 ; 更新客户?

    .WHILE TRUE                                                         ; q入消息循环
                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

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    .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

end start

window.png

分析Q?/font>                                                       

你一定会吃惊一个简单的H口E序竟然会有如此之多的代码,但是(zhn)必要知道上面的大多数代码都是模板而已Q模板的意思是指这些代码对差不多所有标?Windows E序来说都是相同的。在?Windows E序时?zhn)可以把这些代码拷来拷去,当然把这些重复的代码写到一个库中也挺好。其实真正要写的代码集中?WinMain 中。这和一?C ~译器一P无须要关心其它杂务,集中_֊?WinMain 函数。唯一不同的是 C ~译器要求?zhn)的源代码有必L一个函数叫 WinMain。否?QC 无法知道哪个函数和有关的前后代码链接。相对CQ汇~语a提供了较大的灉|性,它不要求一个叫 WinMain 的函数?/p>

 下面我们开始分析,(zhn)可得做好思想准备Q这可不是一件太L的活?

 .386
.model flat,stdcall
option casemap:none

WinMain proto :DWORD,:DWORD,:DWORD,:DWORD

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

(zhn)可以把前三行看成是"必须"的?br />.386告诉MASN我们要用80386指o集?br />. model flat,stdcall告诉MASM 我们用的内存d模式Q此处也可以加入stdcall告诉MASM我们所用的参数传递约定?br />       
接下来是函数 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>

和MASM 相比QTASM 则要单得多,(zhn)只要引入一个库Q即Qimport32.lib?/p>

 .DATA
    ClassName db "SimpleWinClass",0
    AppName  db "Our First Window",0

.DATA?
hInstance HINSTANCE ?
CommandLine LPSTR ?

接下来是"DATA"分段"?br />
?.DATA 中我们定义了两个?NULL l尾的字W串 (ASCIIZ)Q其?ClassName ?Windows cdQ?font color="#0000ff">AppName
是我们窗口的名字。这两个变量都是初始化了的?/p>

?.DATA? 中放了两个未q行初始化的变量Q其?hInstance 代表应用E序的句柄,CommandLine 保存从命令行传入的参数?/p>

HINSTACE ?LPSTR 是两个数据类型名Q它们在头文件中定义Q可以看做是 DWORD 的别名,之所以要q么重新定义仅是Z易记。?zhn)可以查?windows.inc 文gQ在 .DATA? 中的变量都是未经初始化的Q这也就是说在程序刚启动时它们的值是什么无关紧要,只不q占有了一块内存,以后可以再利用而已?/p>

 .CODE
 start:
     invoke GetModuleHandle, NULL
     mov    hInstance,eax
     invoke GetCommandLine
     mov    CommandLine,eax
     invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
     invoke ExitProcess,eax
     .....
end start

 .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 msg:MSG
    LOCAL hwnd:HWND

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
    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(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
  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

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,\
                                                ADDR ClassName,\
                                                ADDR AppName,\
                                                WS_OVERLAPPEDWINDOW,\
                                                CW_USEDEFAULT,\
                                                CW_USEDEFAULT,\
                                                CW_USEDEFAULT,\
                                                CW_USEDEFAULT,\
                                                NULL,\
                                                NULL,\
                                                hInst,\
                                                NULL

注册H口cdQ我们将调用CreateWindowEx来生实际的H口。请注意该函数有12个参数?/p>

CreateWindowExA proto dwExStyle:DWORD,\
   lpClassName:DWORD,\
   lpWindowName:DWORD,\
   dwStyle:DWORD,\
   X:DWORD,\
   Y:DWORD,\
   nWidth:DWORD,\
   nHeight:DWORD,\
   hWndParent:DWORD ,\
   hMenu:DWORD,\
   hInstance:DWORD,\
   lpParam: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
    invoke ShowWindow, hwnd,CmdShow
    invoke UpdateWindow, hwnd

调用CreateWindowEx成功后,会q回H口句柄于eax中。我们必M存该g备后用。我们刚刚生的H口不会自动昄Q所以必调?ShowWindow 来按照我们希望的方式来显CH口。接下来调用 UpdateWindow 来更新客户区?/p>

    .WHILE TRUE
                invoke GetMessage, ADDR msg,NULL,0,0
                .BREAK .IF (!eax)
                invoke TranslateMessage, ADDR msg
                invoke DispatchMessage, ADDR msg
   .ENDW

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
    ret
WinMain endp

如果消息循环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
        invoke PostQuitMessage,NULL
    .ELSE
        invoke DefWindowProc,hWnd,uMsg,wParam,lParam
        ret
    .ENDIF
    xor eax,eax
    ret
WndProc endp

上面可以说是关键部分。这也是我们?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
http://win32assembly.online.fr/                 http://users.daex.ufsc.br/~iczelion/  http://spiff.tripnet.se/~Iczelion



催月?Jaclick) 2007-03-13 23:37 发表评论
]]>
Iczelion's Win32 Assembly Tutorial 2: 消息?/title><link>http://www.tkk7.com/cuiyuelei/archive/2007/02/27/100883.html</link><dc:creator>催月?Jaclick)</dc:creator><author>催月?Jaclick)</author><pubDate>Tue, 27 Feb 2007 02:45:00 GMT</pubDate><guid>http://www.tkk7.com/cuiyuelei/archive/2007/02/27/100883.html</guid><wfw:comment>http://www.tkk7.com/cuiyuelei/comments/100883.html</wfw:comment><comments>http://www.tkk7.com/cuiyuelei/archive/2007/02/27/100883.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/cuiyuelei/comments/commentRss/100883.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/cuiyuelei/services/trackbacks/100883.html</trackback:ping><description><![CDATA[译QLxxQ校对:LuoYunBin's Win32 ASM PageQ?font color="#0000ff"><a target="_blank"><font face="Arial">http://asm.yeah.net<br /></font><br /></a><a href="/Files/cuiyuelei/Iczelion_Win32ASM.zip">点击此处下蝲全教E?/a><a target="_blank"><br /></a></font><hr /><br />在本课中Q我们将用汇~语a写一个WindowsE序Q程序运行时弹Z个消息框q显C?Win32 assembly is great!"。点击这里下载例E?a href="/Files/cuiyuelei/Tutorial2.zip"><font color="#0000ff">here</font></a>. <br /><br /><font color="#0000ff">理论Q?/font><br /><br />Windows 为编写应用程序提供了大量的资源。其中最重要的是<font color="#0000ff">Windows API</font> (Application Programming Interface)?font color="#0000ff">Windows API</font>是一大组功能强大的函敎ͼ它们本nL在Windows 中供Z随时调用。这些函数的大部分被包含在几个动态链接库(DLL)中,譬如Qkernel32.dll、user32.dll ?gdi32.dll?<font color="#0000ff">Kernel32.dll</font>中的函数主要处理内存理和进E调度;<font color="#0000ff">user32.dll </font>中的函数主要控制用户界面Q?font color="#0000ff">gdi32.dll </font>中的函数则负责图形方面的操作。除了上面主要的三个动态链接库Q?zhn)q可以调用包含在其他动态链接库中的函数Q当然?zhn)必须要有关于q些函数的够的资料?br /><br /><font color="#0000ff">动态链接库</font>Q顾名思义Q这些API的代码本wƈ不包含在Windows可执行文件中Q而是当要使用时才被加载。ؓ了让应用E序在运行时能找到这些函敎ͼ必M先把有关的重定位信息嵌入到应用程序的可执行文件中。这些信息存在于引入库中Q由链接器把相关信息从引入库中找出插入到可执行文件中。?zhn)必须指定正确的引入库Q因为只有正的引入库才会有正确的重定位信息?br /><br />当应用程序被加蝲时Windows会检查这些信息,q些信息包括动态链接库的名字和其中被调用的函数的名字。若查到q样的信息,Windows׃加蝲相应的动态链接库Qƈ且重定位调用的函数语句的入口地址Q以便在调用函数时控制权能{Ud函数内部?br /><br />如果从与字符集的相关性来分,API 共有两类Q一cL处理 ANSI 字符集的Q另一cL处理 UNICODE 字符集的。前一cd数名字的N带有一?A"字符的后~Q处理UNICODE的则带有一?W"字符的后~(我想"W"也许是代表宽字符的意思吧)?br /><br />我们比较熟?zhn)的ANSI字符串是?NULL l尾的一串字W数l,每一个ANSI字符是一个BYTE 宽。对于欧z语a体系QANSI 字符集已_了,但对于有成千上万个唯一字符的几U东方语a体系来说只有用 UNICODE 字符集了。每一?UNICODE 字符占有两个BYTE 宽,q样一来就可以在一个字W串中?5336个不同字W了?br /><br />Windows95仅支持ANSIQWindows98/ME只有极少的函数支持UNICODEQWindows NT则从底层支持UNICODE。但在更多的时候,你仅仅只需提供API函数名字而不需要后~Q即可因你的q_来决定和选择适当的函数?br /><br /><font color="#0000ff">例子Q?/font><br /><br />我先把框架程序放在下面,然后我们再向里面加东ѝ?br /><br /><b>.386<br />.model flat, stdcall</b><br /><b>.data</b><br /><b>.code</b><br /><b><font color="#0000ff">start:</font></b><br /><b>end </b><b><font color="#0000ff">start</font></b><br /><br />应用E序的执行是?END <label><font color="#0000ff">定义?/font><label><font color="#0000ff">标识W后的第一条语句开始的</font>。在上面的框架程序中是从START开始。程序逐条语句执行一直到遇到 JMPQJNEQJEQRET {蟩转指令。这些蟩转指令将把执行权转移到其他语句上Q若E序要退出WindowsQ则必须调用函数<font face="Georgia" color="#ff0000" size="4">ExitProcess</font>?<br /><br /><font color="#0000ff"><b>ExitProcess</b><b> proto uExitCode:DWORD</b></font><br /><br />上面一行是函数原型。函数原型会告诉~译器和链接器该函数的属性,q样在编译和链接Ӟ~译器和链接器就会作相关的类型检查?函数的原型定义如下:<br /><br /><font color="#0000ff"><b>FunctionName</b><b> PROTO </b><b>[ParameterName]</b><b>:DataType</b><b>,</b><b>[ParameterName]</b><b>:DataType, ...</b></font><br /><br />a之,是在函数名后加伪指令PROTOQ再跟一串由逗号盔R的数据类型链表。在前面的ExitProcess 定义中,该函数有一个DWORD cd的参数。当(zhn)用高层调用语句INVOKEӞ使用函数原型定义特别有用Q?zhn)可以单地认ؓ INVOKE 是一个有参数cd查的调用语句。譬如,假设(zhn)这样写Q?<br /><b><br />call</b><b> ExitProcess</b><b></b><br /><br />若?zhn)事先没把一个DWORDcd参数压入堆栈Q编译器和链接器都不会报错,但毫无疑问,在?zhn)的程序运行时引起崩溃。但是,当?zhn)q样写: <br /><b><br /><font color="#0000ff" size="4">invoke</font></b><font size="4"><b></b><b>ExitProcess</b><b></b><br /></font><br />q接器将报错提醒(zhn)忘记压入一?DWORD cd参数。所以我(zhn)用 INVOKE 指o而不是CALL去调用一个函数。INVOKE 的语法如下: <br /><b><br />INVOKE  <font size="4">expression</font></b><font size="4"><b> [</b><b>,arguments</b><b>]</b><br /></font><br /><font color="#0000ff"><font size="4">expression</font></font>既可以是一个函数名也可以是一个函数指针。参数由逗号隔开。大多数API函数的原型放在头文g中?如果(zhn)用的是<font size="4">hutchQh名)</font>的MASM32Q这些头文g在文件夹MASM32/include 下, q些头文件的扩展名ؓ INCQ函数名和DLL 中的函数名相同,譬如QKERNEL32.LIB 引出的函?ExitProcess 的函数原形声明于KERNEL32.INC中?<br /><br />(zhn)也可以自己声明函数原型。在我的教学评中都使用<font size="4">hutch</font>?font size="4">windows.inc</font>Q这些头文g(zhn)可以从<a >http://win32asm.cjb.net</a>Q需要代理)下蝲?<br /><br />好,我们现在回到ExitProcess 函数Q参数uExitCode 是?zhn)希望当(zhn)的应用程序结束时传递给Windows的。?zhn)可以q样写: <br /><b><br /><font color="#0000ff">invoke</font></b><font color="#0000ff"><b> ExitProcess</b><b>, 0</b></font><br /><br />把这一行放到开始标识符下,q个应用E序׃立即退出WindowsQ当然毫无疑问这个应用程序本w是一个完整的WindowsE序?<br /><b><br />.386</b><br /><b>.model </b><b>flat, stdcall</b><br /><font color="#0000ff"><b>option </b><b>casemap:none</b></font><br /><b><font color="#0000ff">include</font></b><b>\masm32\</b><b>include</b><b>\windows.inc</b><br /><b><font color="#0000ff">include</font></b><b>\masm32\</b><b>include</b><b>\kernel32.inc</b><br /><b><font color="#0000ff">includelib</font></b><b> \masm32\lib\kernel32.lib</b><br /><b>.data</b><br /><b>.code</b><br /><b>start:</b><br /><b>        <font color="#0000ff">invoke</font></b><b> ExitProcess</b><b>,0</b><br /><b>end </b><b>start</b><br /><b><br /><font color="#0000ff">option casemap</font></b><font color="#0000ff"><b>Q?/b><b>none</b><b></b></font>一句的意思是告诉 MASM 要区分标L大小写,譬如Qstart ?START 是不同的。请注意新的伪指?includeQ跟在其后的文g名所指定的文件在~译时将“插”在该处。在我们上面的程序段中,当MASM处理到语?include \masm\include\windows.inc Ӟ它就会打开文g夹\MASM32\include 中的文gwindows.incQ这和?zhn)把整个文仉_脓(chung)到?zhn)的源E序中的效果是一L。hutch ?windows.inc 包含?WIN32 ~程所需要的帔R和结构体的定义?但是它不包含函数原型的定义。尽hutch和我力包含所有的帔R和结构体的定义,但仍会有不少遗漏Qؓ此我们将不断加入新的内容。请随时注意我们主页Q下载最新的头文件?<br /><br />(zhn)的应用E序除了从windows.inc中得到相兛_量结构体的定义外Q还需要从其他的头文g中得到函数原型的声明Q这些头文g都放?\masm32\include 文g夹中?<br /><br />在我们上面的例子中调用了L?kernel32.dll 中的函数Q所以需要包含有q个函数原型声明的头文g kernel32.inc。如果用文本~辑器打开该文件?zhn)会发现里面全是?kernel32.dll中引出的函数的声明。如果?zhn)不包含kernel32.incQ?zhn)仍然可以调用QcallQExitProcessQ但不能够调用(invokeQExitProcessQ这会无法通过~译器和q接器的参数合法性检查)。所以若?invoke 去调用一个函敎ͼ(zhn)就必须事先声明Q当然不一定要包含我们的头文gQ?zhn)完全可以在调用该函数前在源代码的适当位置q行声名。包含头文g主要是ؓ了节省时_译者:当然q有正确性)<br />      <br />接下来我们来看看<b>includelib </b>伪指令,?include 不同Q它仅仅是告诉编译器(zhn)的E序引用了哪个库。当~译器处理到该指令时会在生成的目标文件中插入链接命o告诉链接器链入什么库。当然?zhn)q可以通过在链接器的命令行指定引入库名U的Ҏ(gu)来达到和用includelib指o相同的目的,但考虑到命令行仅能够传?28个字W而且要不厌其烦地在命令行敲字W,所以这U方法是非常不可取的?<br /><br />好了Q现在保存例子,取名为msgbox.asm。把 ml.exe 的\径放?PATH 环境变量中,键入下面一?q行~译Q?<br /><b><br /><font color="#0000ff">ml  /c  /coff  /Cp msgbox.asm</font></b><font color="#0000ff"><b></b><br /></font><b><br /><font color="#0000ff" size="4">/c</font></b>是告诉MASM只编译不链接。这主要是考虑到在链接前?zhn)可能q有其他工作要做?<br /><b><br /><font color="#0000ff" size="4">/coff</font></b> 告诉MASM产生的目标文件用coff格式。MASM的coff格式是COFFQCommon Object File FormatQ通用目标文g格式Q格式的一U变体。在UNIX下的COFF格式又有不同?<br /><b><br /><font color="#0000ff">/Cp</font></b> 告诉 MASM 不要更改用户定义的标识符的大写。若(zhn)用的是hutch的包含文件的话,?model 指o下加?"option casemap:none" 句,可达到同L效果?<br /><br />当?zhn)成功的编译了msgbox.asm 后,~译器会产生msgbox.obj 目标文gQ目标文件和可执行文件只一步之遥,目标文g中包含了以二q制形式存在的指令和数据Q比可执行文件相差的只是链接器加入的重定位信息?<br /><br />好,我们来链接目标文Ӟ <br /><b> </b><br /><font color="#0000ff"><b>link</b><b> /SUBSYSTEM:WINDOWS  /LIBPATH:c:\masm32\lib  msgbox.obj</b><br /></font><br /><font color="#0000ff"><b>/SUBSYSTEM:</b><b>WINDOWS</b></font>  告诉链接器可执行文g的运行^?<br /><b><br /><font color="#0000ff">/LIBPATH:</font></b><b><font color="#0000ff"><path to import library></font></b> 告诉链接器引入库的\径?br /><br />链接器做的工作就是根据引入库往目标文g中加入重定位信息Q最后生msgbox.exe可执行文件?既然得到了可执行文gQ我们来q行一下。好Q一、二、三QGOQ屏q上什么都没有。哦Q对了,我们除了调用?ExitProcess 函数外,什么都q没做呢Q但是别一Ҏ(gu)感都没有,因ؓ我们用汇~所写的是一个真正的Windows E序Q不信的话,查查(zhn)磁盘上?msgbox.exe文gQ在我的机器上它的大?,536字节呢?<br /><br />下面我们来做一点可以看的见摸的着的,我们在程序中加入一个对话框。该函数的原型如下: <br /><b><br /><font color="#0000ff">MessageBox</font></b><font color="#0000ff"><b> PROTO hwnd:DWORD, lpText:DWORD, lpCaption:DWORD, uType:DWORD</b><br /></font><b><br /><font color="#0000ff">hwnd</font></b><b></b>是父H口的句柄。句柄代表?zhn)引用的窗口的一个地址指针。它的值对(zhn)编Windows E序q不重要   <br />      Q译者注Q如果?zhn)x为高手则是必ȝQ,(zhn)只要知道它代表一个窗口。当(zhn)要对窗口做M操作Ӟ必须要引用该H口的指针?<br /><br /><b><font color="#0000ff">lpText</font></b><b></b>是指向?zhn)要显C的文本的指针。指向文本串的指针事实上是文本串的首地址。?br />  <br /><b><font color="#0000ff">lpCaption</font></b><b></b>是指向?zhn)要显C的对话框的标题文本串指针。?br />  <br /><b><font color="#0000ff">uType</font></b><b></b>是显C在对话框窗口上的小图标的类型?<br />  <br />我们修改 msgbox.asm 在其中加入MessageBox函数。  ?br /><b> <br />.386</b><b><br /></b><b>.model </b><b>flat,stdcall</b><b><br /></b><font color="#0000ff"><b>option</b><b> casemap:none</b></font><b><br /></b><b><font color="#0000ff">include </font></b><b>\masm32\include\windows.inc</b><b><br /></b><b><font color="#0000ff">include </font></b><b>\masm32\include\kernel32.inc</b><b><br /></b><b><font color="#0000ff">includelib</font></b><b> \masm32\lib\kernel32.lib</b><b><br /></b><b><font color="#0000ff">include </font></b><b>\masm32\include\user32.inc</b><b><br /></b><b><font color="#0000ff">includelib</font></b><b> \masm32\lib\user32.lib</b><b></b><br /><b>.data</b><b></b><b><br /></b><b>MsgBoxCaption</b><b>  db </b><b></b><b>"<font color="#ffa500">Iczelion Tutorial No.2</font>"</b><b>, 0</b><b></b><b><br /></b><b>MsgBoxText</b><b>       db </b><b>"<font color="#ffa500">Win32 Assembly is Great</font><font color="#ffa500">!</font>"</b><b>, 0</b><b></b><b></b><br /><b>.code</b><b></b><b><br /></b><b><font color="#ff0000">start:</font></b><b></b><b><br /></b><b>invoke </b><b><font color="#0000ff">MessageBox</font></b><b>, NULL,</b><b><font color="#0000ff">addr </font></b><b>MsgBoxText</b><b>,</b><b><font color="#0000ff">addr </font></b><b>MsgBoxCaption</b><b>,</b><b><font color="#0000ff"> MB_OK</font></b><b><br /></b><b>invoke </b><b><font color="#0000ff">ExitProcess</font></b><b>, NULL</b><b><br /></b><b>end </b><b><font color="#ff0000">start</font></b><font color="#ff0000"><b></b><br /></font><br />~译、链接上面的E序D,得到可执行文件。运行,H口上弹Z一个对话框Q上面有一行字QWin32 Assembly is GreatQ。想一惻I我们是用汇编写出来的Q所以我们有理由为编写了一个最单的WIN32E序感到高兴。(译者注Q如果明天我们能够像?DOS 下那h一行都用汇~写Q那我们有理׃ؓ自己感到自豪。)<br /><br /><img height="103" alt="msgbox.gif" src="http://www.tkk7.com/images/blogjava_net/cuiyuelei/msgbox.gif" width="266" border="0" /><br /><br />好,我们回过头来看看上面的源代码。我们在<font color="#0000ff">?DATA?/font>分段定义了两个NULLl尾的字W串。我们用了两个常量:NULL ?MB_OK。这些常量在<font size="4">windows.inc</font> 文g中有定义Q用常量得?zhn)的程序有较好的可L?<br /><br />addr操作W用来把标号的地址传递给被调用的函数Q它只能用在 invoke 语句中,譬如(zhn)不能用它来把标L地址赋给寄存器或变量Q如果想q样做则要用offset 操作W。在 offset ?addr 之间有如下区别: <br /><br />1. addr不可以处理向前引用,offset则能。所谓向前引用是指:标号的定义是在invoke 语句之后Q譬如在如下的例子: <br /><br />invoke MessageBox,NULL, addr MsgBoxText,addr MsgBoxCaption,MB_OK <br />...... <br />MsgBoxCaption  db "Iczelion Tutorial No.2",0 <br />MsgBoxText       db "Win32 Assembly is Great!",0<br /><br />如果(zhn)是用addr而不是offset的话Q那MASM׃报错?<br /><br />2. <font size="4"> addr</font>可以处理局部变量?font size="4">offset</font> 则不能。局部变量只是在q行时在堆栈中分配内存空间。?font color="#000000" size="4">offset</font> 则是在编译时q译器解释Q这昄不能?font size="4">offset</font>在运行时来分配内存空间。编译器?font size="4">addr</font> 的处理是先检查处理的是全局q是局部变量,若是全局变量则把其地址攑ֈ目标文g中,q一点和<font size="4">offset</font>相同Q若是局部变量,在执行<font size="4">invoke</font> 语句前生如下指令序列: <br /><br /><font color="#0000ff"><b>lea</b><b> eax, LocalVar</b></font><font color="#0000ff"><b><br /></b><b>push eax</b></font><br /><b> </b><br />因ؓ<font size="4">lea</font>指o能够在运行时军_标号的有效地址Q所以有了上q指令序列,可以保?<font size="4">invoke</font> 的正执行了?br /><b><br /><hr /><br />[ <b><a href="http://win32asm.cjb.net Iczelion" temp_href="http://win32asm.cjb.net Iczelion"><font color="#000080">Iczelion's Win32 Assembly HomePage</font></a></b><b>] 需要代理服务器Q但可通过以下镜像q行讉K<br /><a ><font color="#000080">http://win32assembly.online.fr/</font></a>  <a ><font color="#000080">http://users.daex.ufsc.br/~iczelion/</font></a>  <a ><font color="#000080">http://spiff.tripnet.se/~Iczelion</font></a></b></b></label></label><img src ="http://www.tkk7.com/cuiyuelei/aggbug/100883.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/cuiyuelei/" target="_blank">催月?Jaclick)</a> 2007-02-27 10:45 <a href="http://www.tkk7.com/cuiyuelei/archive/2007/02/27/100883.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Iczelion's Win32 Assembly Tutorial 1:基础知识http://www.tkk7.com/cuiyuelei/archive/2007/01/28/96397.html催月?Jaclick)催月?Jaclick)Sun, 28 Jan 2007 12:34:00 GMThttp://www.tkk7.com/cuiyuelei/archive/2007/01/28/96397.htmlhttp://www.tkk7.com/cuiyuelei/comments/96397.htmlhttp://www.tkk7.com/cuiyuelei/archive/2007/01/28/96397.html#Feedback0http://www.tkk7.com/cuiyuelei/comments/commentRss/96397.htmlhttp://www.tkk7.com/cuiyuelei/services/trackbacks/96397.html

译Q?/span> Lxx   校对Q?/span> LuoYunBin's Win32 ASM Page Q?/span> http://asm.yeah.net


点击此处下蝲全教E?/a>


我们先假设?zhn)已知道了如何使用MASM 。如果?zhn)q不知道的话Q请下蝲
MASM32 Q?q请仔细研读其中所附带的文档资料。好,如果(zhn)已准备qAQ我们这开始吧 !

原理Q?/b>

WIN32 E序q行在保护模式下的,保护模式的历史可以追溯到 80286 。而今 80286 已成Z历史。所以我们将只把_֊集中?80386 及后l的 X86 pd CPU ?Windows 把每一?Win32 应用E序攑ֈ分开的虚拟地址I间中去q行Q也是说每一个应用程序都拥有其相互独立的 4GB 地址I间Q当然这倒不是说它们都拥?4GB 的物理地址I间Q而只是说能够?4GB 的范围内d。操作系l将会在应用E序q行时完?4GB 的虚拟地址和物理内存地址间的转换。这p求编写应用程序时必须格守 Windows 的规范,否则极易引v内存的保护模式错误。而过ȝ Win16 内存模式下,所有的应用E序都运行于同一?4GB 地址I间Q它们可以彼?" ?" 到别的程序的内容Q这极易D一个应用程序破坏另一个应用程序甚x操作pȝ的数据或代码。 ?br />       
?6 位Windows下的把代码分?DATA Q?CODE {段的内存模式不同,WIN32 只有一U?font color="#0000ff">内存模式
Q即 FLAT 模式Q意思是 " q_ " 的内存模式,再没?64K 的段大小限制Q所有的 WIN32 的应用程序运行在一个连l、^坦、巨大的 4GB 的空间中。这同时也意味着(zhn)无dD寄存器打交道,(zhn)可以用L的段寄存器寻址L的地址I间Q这对于E序员来说是非常方便的,q也使得?32 位汇~语a和用 C 语言一hѝ ?br />       
在Win32 下编E,有许多重要的规则需要遵守。有一条很重要的是QWindows 在内部频J?ESI QEDI QEBP QEBX 寄存器,而且q不L这些寄存器的值是否被更改Q这样当(zhn)要使用q些寄存器时必须先保存它们的|待用完后再恢复它们,一个最显著的应用例子就是Windows 的CallBack 函数中?br />
下面的程序段是一个框Ӟ若?zhn)现在q不知道q些指o的确切意义的话,没关p,随后我就会给大家详细解释?

.386
.MODEL
Flat, STDCALL

.DATA
   
<
被初始化的数?>
    ......

.DATA?
   <
未初始化的数?>
   ......

.CONST
   <
帔R >
   ......

.CODE 
   <label>
    <
你的代码 >
   .....
    end <label>


框架p么简单,好,我现在就l?zhn)解释Q?

.386


q是一个汇~语a伪指令,他告诉编译器我们的程序是使用80386 指o集编写的。?zhn)q可以?.486?.586 Q但最安全的还是用 .386 。对于每一U?CPU 有两套几乎功能相同伪指o: .386/.386P ?486/.486P ?586/.586P ??P 的指令标明?zhn)的程序中可以?Ҏ(gu)U指?。特权指o是保留给操作pȝ的,如虚拟设备驱动程序。在大多数时_(zhn)的E序?无须q行 ?RING0 层,故用不带后缀 P 的伪指o已够了?

.MODEL
FLAT, STDCALL

.MODEL    是用来指定内存模式的伪指令,在Win32 下,只有一U内存模型,那就是FLAT q_模式?

STDCALL
告诉~译器参数的传递约定。参数的传递约定是指参C达时的顺?( 从左到右或从叛_  ?)  和由谁恢复堆栈指?( 调用者或被调?) 。在Win16 下有两种U定QC ?PASCAL ?

C
              U定规定参数传递顺序是 从右到左 Q即最双的参数最先压栈,p用者恢复堆栈指针。例 如:用函敊W?b>foo
(int first param, int second param, int third param)Q按CU定的汇~代码应该是q样的:

push  [third_param]              
; 把第三个参数压入?
push  [second_param]          
; 接着是第二个参数入栈
push  [first_param]               
; 然后是第一个参数入?
call   foo
add    sp, 12                           
; p用者恢复堆栈指?/font> 
       
PASCAL
U定?C U定正好相反Q它规定参数是从左向右传递,p调用者恢复堆栈。?br />
       
Win16 采用?PASCAL U定Q?因ؓ PASCAL U定产生的代码量要小。当不知道参数的个数ӞC U定特别有用。如在函?b>wsprintf ()中,wsprintf () 预先q不知道要传递几个参敎ͼ所以它不知道如何恢复堆栈?

STDCALL ?C U定?PASCAL U定的؜合体Q它规定参数的传递是从右到左Q恢复堆栈的工作交由被调用者?Win32 只用 STDCALL U定Q但除了一个特例,卻I wsprintf ()

.DATA
.DATA?

.CONST
.CODE
 


上面的四个伪指o?" 分段 "(SECTION) 伪指令。我们上面刚讲过 Win32 下没?" D?"(SEGMENT) 的概念,但是(zhn)可以把(zhn)的E序分成不同?" 分段 " Q?一?" 分段 " 的开始即是上一?" 分段 " 的结束?WIN32 中只有两U性质?" 分段 " Q?DATA ?CODE ?
其中 DATA" 分段 " 又分ZU:

.DATA   其中包括已初始化的数据?

.DATA?   其中包括未初始化的数据。比如有时?zhn)仅想预先分配一些内存但q不x定初始倹{用未初始化的数据的优Ҏ(gu)它不占据可执行文件的大小Q如Q若(zhn)要?.DATA? D中分配 10,000 字节的空_(zhn)的可执行文件的大小无须增加 10,000 字节Q而仅仅是要告诉编译器在装载可执行文g时分配所需字节?

.CONST
  其中包括帔R定义。这些常量在E序q行q程中是不能更改的?

.CODE
q是代码 " 分段 " ?

应用E序q不需要以上所有的三个 " 分段 " Q可以根据需要进行定义。?lt; 译者注Q实际上Q分Dƈ不是象在 Dos 下一?Qؓ不同的段分别指出不同的段寄存器,因ؓ Windows 下只有一?4GB 的段Q?Windows E序中的分段表现在当E序装蝲Ӟ赋予不同的分D不同的属性,比如说当你的E序加蝲Ӟ对于 Ring3 E序来说Q?b>.code D|不可写的Q?b>.data D|可写的,如果你尝试象?Dos 下一样写自己的代码部分,你会得到一个蓝屏错?>
 
<label>
end <label>
 


是用来唯一标识(zhn)的代码范围的标{,两个标签必须相同Q应用程序的所有可执行代码必修在两个标{之间?br />

[ Iczelion's Win32 Assembly HomePage] 需要代理服务器Q但可通过以下镜像q行讉K
http://win32assembly.online.fr/  http://users.daex.ufsc.br/~iczelion/  http://spiff.tripnet.se/~Iczelion


催月?Jaclick) 2007-01-28 20:34 发表评论
]]>
վ֩ģ壺 þþƷƷ| ѹһػƾþ| ɫݺݰվ| һҹaëƬƵ| һeһƬ߲| ɫëƬվ| С˵ͼƬ| ĻѸ | 9ȾƷѹۿƵ| Ļֻ| ðѾƷƵ | þþþþԻAV| ƵѲ| ԭƵ99| ŷպٲ| һһƬѲ| һһdvd߹ۿƵ | Դѹۿ| VƬ߹ۿ| avƬ߹ۿ| þѾƷƵ| ľþþƷ1 | av߿վ| 츾avҹ벻| ȫaһëƬ˰| һ˿wwwѸĻ| AVþþƷ| Ʒվa| ޾ƷۺϾþþþý | ĻƵվ| ӰӴȫ߲| VAĻ| þҹƵ| պӰ߹ۿַ| Ƶ߹ۿ| ŮƵ| һ߹ۿ| ŷ޹˾Ʒ| ޹˳ɾƷ| 51ƵƷȫ| ԻȫƵۿ |