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

📄 lion-tut-c22.htm

📁 内有一些代码
💻 HTM
字号:
<HTML>
<head>
<link rel="stylesheet" href="../../asm.css">

<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>Iczelion's win32 asm tutorial</title>
</head>

<body bgcolor="#FFFFFF" background="../../images/back01.jpg">
<P align=center><font color="#0000FF" size="3"><b>第22课 超类化</b></font></P>
<HR SIZE=1>
<p><br>
  在这一讲我们将学习什么是超类化以及它有什么作用;同时你还会学到怎样在自己的窗口中用Tab键在控件中切换这一技巧。</p>
<p><font color="#FF0000">理论:</font></p>
<p>在你的程序生涯中你肯定遇到过这样的情况,你需要一系列的控件,但它们之间却只有一点点的不同。例如,你可能需要10个只接受数字的 Edit 控件,当然你可以通过多种方法来达到这个目的。</p>
<ol>
  <li>创建自己的类并用它实例化为那些控件</li>
  <li>创建那些 Edit 控件并把它们全部子类化</li>
  <li>超类化Edit 控件</li>
</ol>
<p>第一种方法太乏味了,因为你必须自己实现Edit 控件的每个功能,但这项工作不是轻松就能完成的。第二种方法好于第一种,但仍然要做许多工作,子类化几个Edit 
  控件还可以接受,但若要子类化十几二十个,这项工作简直就是一场恶梦。在这种情况下就应该使用超类化这个技巧,它是用于控制某一个特定窗口类的特殊方法。通过这种控制就可以修改窗口类的特性使之符合你的要求,然后再创建那一堆控件就可以了。</p>
<p>超类化有如下几个步骤:</p>
<ol>
  <li>通过调用 GetClassInfoEx 来获得想要进行超类化操作的窗口类的信息。函数GetClassInfoEx 需要一个指向 WNDCLASSEX 
    结构的指针,用于当成功返回时填入窗口类的信息。</li>
  <li>按需要修改 WNDCLASSEX 结构的成员,其中有两个成员必须修改:<br>
    hInstance 存放程序的实例句柄<br>
    lpszClassName 指向一个新类名的指针<br>
    不必修改成员 lpfnWndProc,但大多数情况下还是需要的。但要记住如果要使用函数 CallWindowProc 调用老窗口的过程,那就必须保存成员 
    lpfnWndProc 的原值。</li>
  <li>注册修改完的 WNDCLASSEX 结构,得到一个具有旧窗口类某些特性的新窗口类。</li>
  <li>用新窗口类创建窗口</li>
</ol>
<p>如果要创建具有相同特性的多个控件,超类化就比子类化要好。</p>
<p><font color="#FF0000">举例:</font></p>
<p>.386 <br>
  .model flat,stdcall <br>
  option casemap:none <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>
  WM_SUPERCLASS equ WM_USER+5 <br>
  WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD <br>
  EditWndProc PROTO :DWORD,:DWORD,:DWORD,:DWORD </p>
<p>.data <br>
  ClassName db &quot;SuperclassWinClass&quot;,0 <br>
  AppName db &quot;Superclassing Demo&quot;,0 <br>
  EditClass db &quot;EDIT&quot;,0 <br>
  OurClass db &quot;SUPEREDITCLASS&quot;,0 <br>
  Message db &quot;You pressed the Enter key in the text box!&quot;,0 </p>
<p>.data? <br>
  hInstance dd ? <br>
  hwndEdit dd 6 dup(?) ;存放6个窗口句柄的数组<br>
  OldWndProc dd ? ;原来的窗口过程</p>
<p>.code <br>
  start: <br>
  invoke GetModuleHandle, NULL <br>
  mov hInstance,eax <br>
  invoke WinMain, hInstance,NULL,NULL, SW_SHOWDEFAULT <br>
  invoke ExitProcess,eax </p>
<p>WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD 
  <br>
  LOCAL wc:WNDCLASSEX <br>
  LOCAL msg:MSG <br>
  LOCAL hwnd:HWND </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 hInst <br>
  pop wc.hInstance <br>
  mov wc.hbrBackground,COLOR_APPWORKSPACE <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 <br>
  invoke RegisterClassEx, addr wc <br>
  invoke CreateWindowEx,WS_EX_CLIENTEDGE+WS_EX_CONTROLPARENT,ADDR ClassName,ADDR 
  AppName,\ <br>
  WS_OVERLAPPED+WS_CAPTION+WS_SYSMENU+WS_MINIMIZEBOX+WS_MAXIMIZEBOX+WS_VISIBLE,CW_USEDEFAULT,\ 
  <br>
  CW_USEDEFAULT,350,220,NULL,NULL,\ <br>
  hInst,NULL <br>
  mov hwnd,eax </p>
<p> .while TRUE <br>
  invoke GetMessage, ADDR msg,NULL,0,0 <br>
  .BREAK .IF (!eax) <br>
  invoke TranslateMessage, ADDR msg <br>
  invoke DispatchMessage, ADDR msg <br>
  .endw <br>
  mov eax,msg.wParam <br>
  ret <br>
  WinMain endp </p>
<p>WndProc proc uses ebx edi hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM 
  <br>
  LOCAL wc:WNDCLASSEX <br>
  .if uMsg==WM_CREATE <br>
  mov wc.cbSize,sizeof WNDCLASSEX <br>
  invoke GetClassInfoEx,NULL,addr EditClass,addr wc <br>
  push wc.lpfnWndProc <br>
  pop OldWndProc <br>
  mov wc.lpfnWndProc, OFFSET EditWndProc <br>
  push hInstance <br>
  pop wc.hInstance <br>
  mov wc.lpszClassName,OFFSET OurClass <br>
  invoke RegisterClassEx, addr wc <br>
  xor ebx,ebx <br>
  mov edi,20 <br>
  .while ebx&lt;6 <br>
  invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR OurClass,NULL,\ <br>
  WS_CHILD+WS_VISIBLE+WS_BORDER,20,\ <br>
  edi,300,25,hWnd,ebx,\ <br>
  hInstance,NULL <br>
  mov dword ptr [hwndEdit+4*ebx],eax <br>
  add edi,25 <br>
  inc ebx <br>
  .endw <br>
  invoke SetFocus,hwndEdit <br>
  .elseif uMsg==WM_DESTROY <br>
  invoke PostQuitMessage,NULL <br>
  .else <br>
  invoke DefWindowProc,hWnd,uMsg,wParam,lParam <br>
  ret <br>
  .endif <br>
  xor eax,eax <br>
  ret <br>
  WndProc endp </p>
<p>EditWndProc PROC hEdit:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD <br>
  .if uMsg==WM_CHAR <br>
  mov eax,wParam <br>
  .if (al&gt;=&quot;0&quot; &amp;&amp; al&lt;=&quot;9&quot;) || (al&gt;=&quot;A&quot; 
  &amp;&amp; al&lt;=&quot;F&quot;) || (al&gt;=&quot;a&quot; &amp;&amp; al&lt;=&quot;f&quot;) 
  || al==VK_BACK <br>
  ;处理字符0~9,A~F,a~f,这几个十六进制数<br>
  .if al&gt;=&quot;a&quot; &amp;&amp; al&lt;=&quot;f&quot; <br>
  sub al,20h <br>
  如果是字符a~f,则把它们变为大写<br>
  .endif <br>
  invoke CallWindowProc,OldWndProc,hEdit,uMsg,eax,lParam <br>
  ret <br>
  .endif <br>
  .elseif uMsg==WM_KEYDOWN <br>
  mov eax,wParam <br>
  .if al==VK_RETURN <br>
  invoke MessageBox,hEdit,addr Message,addr AppName,MB_OK+MB_ICONINFORMATION <br>
  invoke SetFocus,hEdit <br>
  .elseif al==VK_TAB <br>
  invoke GetKeyState,VK_SHIFT <br>
  test eax,80000000 <br>
  .if ZERO? <br>
  invoke GetWindow,hEdit,GW_HWNDNEXT <br>
  .if eax==NULL <br>
  invoke GetWindow,hEdit,GW_HWNDFIRST <br>
  .endif <br>
  .else <br>
  invoke GetWindow,hEdit,GW_HWNDPREV <br>
  .if eax==NULL <br>
  invoke GetWindow,hEdit,GW_HWNDLAST <br>
  .endif <br>
  .endif <br>
  invoke SetFocus,eax <br>
  xor eax,eax <br>
  ret <br>
  .else <br>
  invoke CallWindowProc,OldWndProc,hEdit,uMsg,wParam,lParam <br>
  ret <br>
  .endif <br>
  .else <br>
  invoke CallWindowProc,OldWndProc,hEdit,uMsg,wParam,lParam <br>
  ret <br>
  .endif <br>
  xor eax,eax <br>
  ret <br>
  EditWndProc endp <br>
  end start <br>
</p>
<p><font color="#FF0000">分析</font> </p>
<p>这个程序创建了一个在其客户区有六个被修改的 Edit 控件的简单窗口,这些 Edit控件只接受十六进制的数字。实际上,这个例子是通过修改窗口了类化的例子得来的。这个程序开始和其它程序一样,有趣的部分出现在主窗口被创建的时候:</p>
<p>.if uMsg==WM_CREATE <br>
  mov wc.cbSize,sizeof WNDCLASSEX <br>
  invoke GetClassInfoEx,NULL,addr EditClass,addr wc </p>
<p> 必须用想进行超类化操作的类数据填充 WNDCLASSEX 结构,在我们的例子中就是类 Edit ,记住在调用函数 GetClassInfoEx 之前必须填写成员 
  cbSize,否则函数调用 GetClassInfoEx不会在 WNDCLASSEX 结构中填入正确的返回值。成功返回后,变量 wc中保存的就是想要创建一个新类所需要的所有信息。</p>
<p> push wc.lpfnWndProc <br>
  pop OldWndProc <br>
  mov wc.lpfnWndProc, OFFSET EditWndProc <br>
  push hInstance <br>
  pop wc.hInstance <br>
  mov wc.lpszClassName,OFFSET OurClass </p>
<p> 现在必须修改变量 wc 的一些属性:第一个要修改的就是指向窗口过程的指针。因为在新窗口过程中函数 CallWindowProx 要用到老窗口过程,因此得把它保存到一个变量中以便使用。这个技巧和在子类化中用到的一样,只不过不是调用 
  SetWindowLong 而是直接修改 WNDCLASSEX 结构罢了。接下来必须得为这个新类取个名字。</p>
<p> invoke RegisterClassEx, addr wc </p>
<p>当所有这些都完成时,注册这个新类就会得到一个具有旧类某些特征的新类了。</p>
<p> xor ebx,ebx <br>
  mov edi,20 <br>
  .while ebx&lt;6 <br>
  invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR OurClass,NULL,\ <br>
  WS_CHILD+WS_VISIBLE+WS_BORDER,20,\ <br>
  edi,300,25,hWnd,ebx,\ <br>
  hInstance,NULL <br>
  mov dword ptr [hwndEdit+4*ebx],eax <br>
  add edi,25 <br>
  inc ebx <br>
  .endw <br>
  invoke SetFocus,hwndEdit </p>
<p> 注册完新类就可以创建基于它的窗口了:<br>
  在上面的程序片断中,用寄存器 ebx 来保存已创建的窗口数目,用寄存器 edi 来保存窗口左上角的 y 坐标。创建一个新窗口时,把它的句柄保存在一个双字的数组中,当创建完所有的窗口后,设定输入焦点为所创建的第一个窗口。</p>
<p>这时已经有6个只能接受十六进制数字的 edit 窗口控件了,替换的窗口过程处理了字符过滤,这实际上和在子类化中的例子是一样的。但不必做子类化那些窗口的额外工作了。 
</p>
<p>在此程序中,通过使用 Tabs 键来在各个 Edit 控件中切换来使得这个程序更加有趣。一般来说,如果使用对话框,对话框管理器会处理好所有这些问题,即:<br>
  按下 Tabs 输入焦点切换到下一个控件窗口中,按下 Shift-Tabs 输入焦点切换到上一个控件窗口中;但一个简单的窗口不具有这个功能,必须子类化它们以处理 
  Tabs 键。在这个例子中,不必一个一个去子类化已经进行过超类化操作的这些控件,可以使用一种集中控制切换策略。</p>
<p> .elseif al==VK_TAB <br>
  invoke GetKeyState,VK_SHIFT <br>
  test eax,80000000 <br>
  .if ZERO? <br>
  invoke GetWindow,hEdit,GW_HWNDNEXT <br>
  .if eax==NULL <br>
  invoke GetWindow,hEdit,GW_HWNDFIRST <br>
  .endif <br>
  .else <br>
  invoke GetWindow,hEdit,GW_HWNDPREV <br>
  .if eax==NULL <br>
  invoke GetWindow,hEdit,GW_HWNDLAST <br>
  .endif <br>
  .endif <br>
  invoke SetFocus,eax <br>
  xor eax,eax <br>
  ret </p>
<p> 上面是摘自于 EditWndClass 过程的程序片断,它检查用户是否按下了 Tabs 键,若是就调用函数 GetKeyState 来检查 SHIFT 
  键是否也被同时按下了。函数 GetKeyState 在寄存器 eax 中设立一个返回值,用于判断某个特定的键是否被按下了,若按下了,则把 eax 的的最高位置1,否则把最高位清0。所以只要用 
  80000000h 来测试返回值就行了,若最高位是1则说明用户按下了 SHIFT-Tabs,这需要单独处理;否则说明只按下 Tabs 键,调用函数 GetWindow 
  来获得 hEdit 所指向窗口的下一个窗口句柄,若该函数返回 NULL ,说明这是当前窗口是窗口链中最后一个窗口了,应该通过以参数 GW_HWNDFIRST 
  调用函数 GetWindow 来卷回到窗口链中的第一个窗口控件。SHIFT-Tabs 的处理过程和这正好相反。<br>
</p>
<HR SIZE=1>
<DIV align=center> <font face="宋体"> 
  <SCRIPT language=JavaScript1.1 src="../lion-tut-c13.files/textclick"></SCRIPT>
  <BR>
  </font></DIV>
<font face="宋体"><!-- 10:1 文本广告交换 --> </font> 
<DIV align=center> <font face="宋体"> 
  <SCRIPT language=JavaScript1.1 src="../lion-tut-c13.files/c21.htm"></SCRIPT>
  <!-- 10:1 文本广告交换 --></font></DIV>
<HR SIZE=1>
<DIV align=center><font face="宋体">翻译:ZhangJun,校对:LuoYunBin's Win32 ASM Page, <A 
href="http://asm.yeah.net/">http://asm.yeah.net</A></font></DIV>
</BODY></HTML>

⌨️ 快捷键说明

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