⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 lion-tut-c03.htm

📁 内有一些代码
💻 HTM
📖 第 1 页 / 共 3 页
字号:
  .BREAK .IF (!eax) <br>
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
  invoke TranslateMessage, ADDR msg <br>
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
  invoke DispatchMessage, ADDR msg <br>
  &nbsp;&nbsp; .ENDW <br>
  &nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp; eax,msg.wParam&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
  ; return exit code in eax <br>
  &nbsp;&nbsp;&nbsp; ret <br>
  WinMain endp </font>
<p><font color="#006666">WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM 
  <br>
  &nbsp;&nbsp;&nbsp; .IF uMsg==WM_DESTROY&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
  ; if the user closes our window <br>
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; invoke PostQuitMessage,NULL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
  ; quit our application <br>
  &nbsp;&nbsp;&nbsp; .ELSE <br>
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; invoke DefWindowProc,hWnd,uMsg,wParam,lParam&nbsp;&nbsp;&nbsp;&nbsp; 
  ; Default message processing <br>
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ret <br>
  &nbsp;&nbsp;&nbsp; .ENDIF <br>
  &nbsp;&nbsp;&nbsp; xor eax,eax <br>
  &nbsp;&nbsp;&nbsp; ret <br>
  WndProc endp </font>
<p><font color="#006666">end start </font>
<p><font color="#FF0000">分析:</font></p>
<p> 看到一个简单的 Windows 程序有这么多行,您是不是有点想撤? 但是您必须要知道的是上面的大多数代码都是模板而已,模板的意思即是指这些代码对差不多所有标准 
  Windows 程序来说都是相同的。在写 Windows 程序时您可以把这些代码拷来拷去,当然把这些重复的代码写到一个库中也挺好。其实真正要写的代码集中在 
  WinMain 中。这和一些 C 编译器一样,无须要关心其它杂务,集中精力于 WinMain 函数。唯一不同的是 C 编译器要求您的源代码有必须有一个函数叫 
  WinMain。否则 C 无法知道将哪个函数和有关的前后代码链接。相对C,汇编语言提供了较大的灵活性,它不强行要求一个叫 WinMain 的函数。</p>
<p> 下面我们开始分析,您可得做好思想准备,这可不是一件太轻松的活。</p>
<p> .386<br>
  .model flat,stdcall<br>
  option casemap:none<br>
  <br>
  WinMain proto :DWORD,:DWORD,:DWORD,:DWORD<br>
  <br>
  include \masm32\include\windows.inc<br>
  include \masm32\include\user32.inc<br>
  include \masm32\include\kernel32.inc<br>
  includelib \masm32\lib\user32.lib<br>
  includelib \masm32\lib\kernel32.lib <br>
  <br>
  您可以把前三行看成是"必须"的.<br>
  <br>
  .386告诉MASN我们要用80386指令集。<br>
  . model flat,stdcall告诉MASM 我们用的内存寻址模式,此处也可以加入stdcall告诉MASM我们所用的参数传递约定。</p>
<p> 接下来是函数 WinMain 的原型申明,因为我们稍后要用到该函数,故必须先声明。我们必须包含 window.inc 文件,因为其中包含大量要用到的常量和结构的定义,该文件是一个文本文件,您可以用任何文本编辑器打开它, 
  window.inc还没有包含所有的常量和结构定义,不过 hutch 和我一直在不断加入新的内容。如果暂时在 window.inc 找不到,您也可以自行加入。</p>
<p>我们的程序调用驻扎在 user32.dll (譬如:CreateWindowEx, RegisterWindowClassEx) 和 kernel32.dll 
  (ExitProcess)中的函数,所以必须链接这两个库。接下来我如果问:您需要把什么库链入您的程序呢 ? 答案是:先查到您要调用的函数在什么库中,然后包含进来。譬如:若您要调用的函数在 
  gdi32.dll 中,您就要包含gdi32.inc头文件。和 MASM 相比,TASM 则要简单得多,您只要引入一个库,即:import32.lib。&lt;译者注:但 
  Tasm5 麻烦的是 windows.inc 非常的不全面,而且如果在 Windows.inc 中包含全部的 API 定义会内存不够,所以每次你得把用到的 
  API 定义拷贝出来&gt;</p>
<p>.DATA<br>
  <br>
  ClassName db "SimpleWinClass",0 <br>
  AppName db "Our First Window",0<br>
  <br>
  .DATA?<br>
  <br>
  hInstance HINSTANCE ?<br>
  CommandLine LPSTR ?<br>
  <br>
  接下来是DATA"分段"。 在 .DATA 中我们定义了两个以 NULL 结尾的字符串 (ASCIIZ):其中 ClassName 是 Windows 
  类名,AppName 是我们窗口的名字。这两个变量都是初始化了的。未进行初始化的两个边量放在 .DATA? "分段"中,其中 hInstance 代表应用程序的句柄,CommandLine 
  保存从命令行传入的参数。HINSTACE 和 LPSTR 是两个数据类型名,它们在头文件中定义,可以看做是 DWORD 的别名,之所以要这么重新定仅是为了易记。您可以查看 
  windows.inc 文件,在 .DATA? 中的变量都是未经初始化的,这也就是说在程序刚启动时它们的值是什么无关紧要,只不过占有了一块内存,以后可以再利用而已。</p>
<p> .CODE<br>
  start:<br>
  invoke GetModuleHandle, NULL<br>
  mov hInstance,eax<br>
  invoke GetCommandLine<br>
  mov CommandLine,eax<br>
  invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT<br>
  invoke ExitProcess,eax<br>
  .....<br>
  end start</p>
<p> .DATA "分段"包含了您应用程序的所有代码,这些代码必须都在 .code 和 end <starting label>之间。至于 label 的命名只要遵从 
  Windows 规范而且保证唯一则具体叫什么倒是无所谓。我们程序的第一条语句是调用 GetModuleHandle 去查找我们应用程序的句柄。在Win32下,应用程序的句柄和模块的句柄是一样的。您可以把实例句柄看成是您的应用程序的 
  ID 号。我们在调用几个函数是都把它作为参数来进行传递,所以在一开始便得到并保存它就可以省许多的事。</p>
<p> 特别注意:WIN32下的实例句柄实际上是您应用程序在内存中的线性地址。</p>
<p>WIN32 中函数的函数如果有返回值,那它是通过 eax 寄存器来传递的。其他的值可以通过传递进来的参数地址进行返回。一个 WIN32 函数被调用时总会保存好段寄存器和 
  ebx,edi,esi和ebp 寄存器,而 ecx和edx 中的值总是不定的,不能在返回是应用。特别注意:从 Windows API 函数中返回后,eax,ecx,edx 
  中的值和调用前不一定相同。当函数返回时,返回值放在eax中。如果您应用程序中的函数提供给 Windows 调用时,也必须尊守这一点,即在函数入口处保存段寄存器和 
  ebx,esp,esi,edi 的值并在函数返回时恢复。如果不这样一来的话,您的应用程序很快会崩溃。从您的程序中提供给 Windows 调用的函数大体上有两种:Windows 
  窗口过程和 Callback 函数。</p>
<p>如果您的应用程序不处理命令行那么就无须调用 GetCommandLine,这里只是告诉您如果要调用应该怎么做。 </p>
<p>下面则是调用WinMain了。该函数共有4个参数:应用程序的实例句柄,该应用程序的前一实例句柄,命令行参数串指针和窗口如何显示。Win32 没有前一实例句柄的概念,所以第二个参数总为0。之所以保留它是为了和 
  Win16 兼容的考虑,在 Win16下,如果 hPrevInst 是 NULL,则该函数是第一次运行。特别注意:您不用必须申明一个名为 WinMain 
  函数,事实上在这方面您可以完全作主,您甚至无须有一个和 WinMain 等同的函数。您只要把 WinMain 中的代码拷到GetCommandLine 
  之后,其所实现的功能完全相同。在 WinMain 返回时,把返回码放到 eax 中。然后在应用程序结束时通过 ExitProcess 函数把该返回码传递给 
  Windows 。 </p>
<p><b>WinMain proc Inst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD</b> 
</p>
<p> 上面是WinMain的定义。注意跟在 proc 指令后的parameter:type形式的参数,它们是由调用者传给 WinMain 的,我们引用是直接用参数名即可。至于压栈和退栈时的平衡堆栈工作由 
  MASM 在编译时加入相关的前序和后序汇编指令来进行。 LOCAL wc:WNDCLASSEX LOCAL msg:MSG LOCAL hwnd:HWND 
  LOCAL 伪指令为局部变量在栈中分配内存空间,所有的 LOCAL 指令必须紧跟在 PROC 之后。LOCAL 后跟声明的变量,其形式是 变量名:变量类型<variable type>。譬如 
  LOCAL wc:WNDCLASSEX 即是告诉 MASM 为名字叫 wc 的局部边量在栈中分配长度为 WNDCLASSEX 结构体长度的内存空间,然后我们在用该局部变量是无须考虑堆栈的问题,考虑到 
  DOS 下的汇编,这不能不说是一种恩赐。不过这就要求这样申明的局部变量在函数结束时释放栈空间,(也即不能在函数体外被引用),另一个缺点是您因不能初始化您的局部变量,不得不在稍后另外再对其赋值。</p>
<p> mov wc.cbSize,SIZEOF WNDCLASSEX<br>
  mov wc.style, CS_HREDRAW or CS_VREDRAW<br>
  mov wc.lpfnWndProc, OFFSET WndProc<br>
  mov wc.cbClsExtra,NULL<br>
  mov wc.cbWndExtra,NULL<br>
  push hInstance<br>
  pop wc.hInstance<br>
  mov wc.hbrBackground,COLOR_WINDOW+1 <br>
  mov wc.lpszMenuName,NULL<br>
  mov wc.lpszClassName,OFFSET ClassName <br>
  invoke LoadIcon,NULL,IDI_APPLICATION<br>
  mov wc.hIcon,eax<br>
  mov wc.hIconSm,eax<br>
  invoke LoadCursor,NULL,IDC_ARROW<br>
  mov wc.hCursor,eax invoke <br>
  RegisterClassEx, addr w <br>
  <br>
  上面几行从概念上说确实是非常地简单。只要几行指令就可以实现。其中的主要概念就是窗口类(window class),一个窗口类就是一个有关窗口的规范,这个规范定义了几个主要的窗口的元素,如:图标、光标、背景色、和负责处理该窗口的函数。您产生一个窗口时就必须要有这样的一个窗口类。如果您要产生不止一个同种类型的窗口时,最好的方法就是把这个窗口类存储起来,这种方法可以节约许多的内存空间。也许今天您不会太感觉到,可是想想以前 
  PC 大多数只有 1M 内存时,这么做是非常有必要的。如果您要定义自己的创建窗口类就必须:在一个 WINDCLASS 或 WINDOWCLASSEXE 
  结构体中指明您窗口的组成元素,然后调用 RegisterClass 或 RegisterClassEx ,再根据该窗口类产生窗口。对不同特色的窗口必须定义不同的窗口类。 
  WINDOWS有几个预定义的窗口类,譬如:按钮、编辑框等。要产生该种风格的窗口无须预先再定义窗口类了,只要包预定义类的类名作为参数调用 CreateWindowEx 
  即可。</p>
<p> WNDCLASSEX 中最重要的成员莫过于lpfnWndProc了。前缀 lpfn 表示该成员是一个指向函数的长指针。在 Win32中由于内存模式是 
  FLAT 型,所以没有 near 或 far 的区别。每一个窗口类必须有一个窗口过程,当 Windows 把属于特定窗口的消息发送给该窗口时,该窗口的窗口类负责处理所有的消息,如键盘消息或鼠标消息。由于窗口过程差不多智能地处理了所有的窗口消息循环,所以您只要在其中加入消息处理过程即可。下面我将要讲解 
  WNDCLASSEX 的每一个成员</p>
<p>WNDCLASSEX STRUCT DWORD <br>
  &nbsp; cbSize&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
  DWORD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ? <br>
  &nbsp; style&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
  DWORD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ? <br>
  &nbsp; lpfnWndProc&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DWORD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
  ? <br>
  &nbsp; cbClsExtra&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DWORD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
  ? <br>
  &nbsp; cbWndExtra&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DWORD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
  ? <br>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -