Chinaunix首页 | 论坛 | 博客
  • 博客访问: 126283
  • 博文数量: 37
  • 博客积分: 1490
  • 博客等级: 上尉
  • 技术积分: 323
  • 用 户 组: 普通用户
  • 注册时间: 2009-12-01 16:38
文章分类

全部博文(37)

文章存档

2011年(1)

2010年(23)

2009年(13)

我的朋友

分类: WINDOWS

2009-12-18 17:07:50

  在这一课中,我们将学习什么是超类化以及它能用来做什么。你也将学习到如何在你自己的窗口中为控件提供Tab键导航。(切换窗口)

在你的程序员生涯中,你必定遭遇过这种情形,这里有你需要的一系列控件,但它们的行为又稍微有些不同。例如,你可能需要10仅接收数字的编辑控件。这里有几种方法能完成目标:
          1.创建你自己的类并将控件实例化。
          2.创建这些编辑控件然后将它们全部子类化。
          3.超类化这个编辑控件。
第一种方法是非常乏味的。你必须自己实现编辑控件的每一个功能。艰难的任务不是能被轻松完成的。
第二种方法比第一种方法好很多但是仍要做很多工作。如果你子类化的控件仅有几个,那么这种方法仍可行。但是当你子类化一打(十二个)左右的控件时它将是一个恶梦。在这种场合,超类化才是你应该使用的技术。

超类化用于操纵特别窗口类控件的方法。用操纵控件,我的意思是你能修改这个窗口类的性质来让它于你的目的想匹配,然后你能用它创建一捆的控件。
         超类化的步骤 概要如下:
         调用GetClassInfoEx函数来获得你想超类化的那个窗口的信息。GetClassInfoEx函数需要一个指向WNDCLADDEX结构体的指针,如果调用成功,这个结构体将被成功返回的信息填满。
         修改你想改变的WNDCLASSEX结构的成员。无论如何,这里有两个成员是你必须修改。
                 hInstance 你必须把你们的应用程序实例句柄放到这个成员中。
                 LpszClassName  你必须为它提供一个包含新类名的字符指针。
          你并不需要修改lpfnWndProc成员,但是在大多数情况下,你还是修改一下。仅需要记住的是如果你要调用的函数是CallWindowProc函数,你要保存lpfnWndProc的初始值。
          注册已修改过的WNDCLASSEX结构。你将有一个新的窗口类, 这个新窗口类将有几个特征和老窗口类的相同。
          从这个窗口类中创建窗口。

如果你想创建具有相同特征值的多个控件,超类化比子类化好得多。
Example:
.386
.model flat,stdcall
option casemap:none
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
WM_SUPERCLASS equ WM_USER+5
WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
EditWndProc PROTO :DWORD,:DWORD,:DWORD,:DWORD
.data
ClassName  db "SuperclassWinClass",0
AppName    db "Superclassing Demo",0
EditClass  db "EDIT",0
OurClass db "SUPEREDITCLASS",0
Message  db "You pressed the Enter key in the text box!",0
.data?
hInstance dd ?
hwndEdit dd 6 dup(?)
OldWndProc dd ?
.code
start:
    invoke GetModuleHandle, NULL
    mov    hInstance,eax
    invoke WinMain, hInstance,NULL,NULL, 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_APPWORKSPACE
    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
    invoke CreateWindowEx,WS_EX_CLIENTEDGE+WS_EX_CONTROLPARENT,ADDR ClassName,ADDR AppName,\
        WS_OVERLAPPED+WS_CAPTION+WS_SYSMENU+WS_MINIMIZEBOX+WS_MAXIMIZEBOX+WS_VISIBLE,CW_USEDEFAULT,\
           CW_USEDEFAULT,350,220,NULL,NULL,\
           hInst,NULL
    mov hwnd,eax
    .while TRUE
        invoke GetMessage, ADDR msg,NULL,0,0
        .BREAK .IF (!eax)
        invoke TranslateMessage, ADDR msg
        invoke DispatchMessage, ADDR msg
    .endw
     mov eax,msg.wParam
    ret
WinMain endp
WndProc proc uses ebx edi hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    LOCAL wc:WNDCLASSEX
    .if uMsg==WM_CREATE
        mov wc.cbSize,sizeof WNDCLASSEX
        invoke GetClassInfoEx,NULL,addr EditClass,addr wc
        push wc.lpfnWndProc
        pop OldWndProc
        mov wc.lpfnWndProc, OFFSET EditWndProc
        push hInstance
        pop wc.hInstance
        mov wc.lpszClassName,OFFSET OurClass
        invoke RegisterClassEx, addr wc
        xor ebx,ebx
        mov edi,20
        .while ebx<6
            invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR OurClass,NULL,\
                 WS_CHILD+WS_VISIBLE+WS_BORDER,20,\
                 edi,300,25,hWnd,ebx,\
                 hInstance,NULL
            mov dword ptr [hwndEdit+4*ebx],eax
            add edi,25
            inc ebx
        .endw
        invoke SetFocus,hwndEdit
    .elseif uMsg==WM_DESTROY
        invoke PostQuitMessage,NULL
    .else
        invoke DefWindowProc,hWnd,uMsg,wParam,lParam
        ret
    .endif
    xor eax,eax
    ret
WndProc endp
EditWndProc PROC hEdit:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
    .if uMsg==WM_CHAR
        mov eax,wParam
        .if (al>="0" && al<="9") || (al>="A" && al<="F") || (al>="a" && al<="f") || al==VK_BACK
            .if al>="a" && al<="f"
               sub al,20h
            .endif
            invoke CallWindowProc,OldWndProc,hEdit,uMsg,eax,lParam
            ret
        .endif
    .elseif uMsg==WM_KEYDOWN
        mov eax,wParam
        .if al==VK_RETURN
            invoke MessageBox,hEdit,addr Message,addr AppName,MB_OK+MB_ICONINFORMATION
            invoke SetFocus,hEdit
        .elseif al==VK_TAB
            invoke GetKeyState,VK_SHIFT
            test eax,80000000
            .if ZERO?
                invoke GetWindow,hEdit,GW_HWNDNEXT
                .if eax==NULL
                    invoke GetWindow,hEdit,GW_HWNDFIRST
                .endif
            .else
                invoke GetWindow,hEdit,GW_HWNDPREV
                .if eax==NULL
                    invoke GetWindow,hEdit,GW_HWNDLAST
                .endif
            .endif
            invoke SetFocus,eax
            xor eax,eax
            ret
        .else
            invoke CallWindowProc,OldWndProc,hEdit,uMsg,wParam,lParam
            ret
        .endif
    .else
        invoke CallWindowProc,OldWndProc,hEdit,uMsg,wParam,lParam
        ret
    .endif
    xor eax,eax
    ret
EditWndProc endp
end start
 
分析:
程序将创建一个简单的窗口,在窗口的客户区有6个修改过的编辑控件。这些编辑控件仅接收16进制数。实际上,我是修改了子类化的例子做到超类化的。程序正常开始,有趣的部分是在主窗口被创建的时候:
    .if uMsg==WM_CREATE
         mov wc.cbSize,sizeof WNDCLASSEX
        invoke GetClassInfoEx,NULL,addr EditClass,addr wc
首先,我们必须用我们想超类化的那个类的数据成员来填充WNDCLASSEX结构。在我们的例子中,它就是EDIT类。记住,在你调用GetClassInfoEx函数之前,你必须设置WNDCLASSEX结构的cbSize成员的值,否则,WNDCLASSEX结构将不能被完全填充。在GetClassInfoEx返回之后,变量 wc中保存的就是想要创建一个新类所需要的所有信息。
       push wc.lpfnWndProc
        pop OldWndProc
        mov wc.lpfnWndProc, OFFSET EditWndProc
        push hInstance
        pop wc.hInstance
        mov wc.lpszClassName,OFFSET OurClass
现在我们修改wc的一些成员。第一个成员是指向窗口过程的指针。因为在我们自己的窗口过程中要用到原来的窗口过程,我们必须把它保存在一个变量中,那么我们在调用CallWindowProc时就能够使用它。除了没有直接调用SetWindowLong函数来修改WNDCLADSDEX结构外,这个技术和子类化是一样的。接下来的两个成员必须被改变,否则你就不能注册你的新窗口类,它们是hInstance和lpsClassName。你必须用你拥有的程序实例替代原来的实例值并且你必须为这个新类选择一个新类名。
        invoke RegisterClassEx, addr wc
当所有的这些都准备好时,注册这个新的类。这个新窗口类将有几个特征和老窗口类的相同。
        xor ebx,ebx
        mov edi,20
        .while ebx<6
            invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR OurClass,NULL,\
                 WS_CHILD+WS_VISIBLE+WS_BORDER,20,\
                 edi,300,25,hWnd,ebx,\
                 hInstance,NULL
            mov dword ptr [hwndEdit+4*ebx],eax
            add edi,25
            inc ebx
        .endw
        invoke SetFocus,hwndEdit

现在,我们注册了这个窗口类,我们就能基于它创建窗口了。在上面的代码片段中,我用ebx作为创建了多少个窗口的计数器。Edi保存窗口左上角的y坐标值。当一个窗口被创建时,它的句柄就被储存在一个DWORD的数组中。当所有的窗口都被创建时,设置输入焦点给第一个窗口。到这里,你得到了6个仅接收16进制数的编辑控件。取代的窗口过程处理了字符过滤。实际上,它和在子类化例子中的窗口过程一致。如你所见,你不必要去做窗口子类化的那些额外的工作了。
我插入用TAB键处理控件切换的代码片段来让这个例子更有趣。通常,你把一个控件放置在一个对话框上,对话框管理器为你处理定位键,所以你能用TAB键定位到下一个控件或者是用shift-tab键返回到上一个控件。唉,如果你把控件放置在一个窗口上时,这样的特性是无法使用的。你必须子类化它们来让你自己处理tab 键。在我们的例子中,我们不需要一个一个的子类化这些控件,因为我们已经超类化了它们。所以我们为它们提供了一个“中央控制导航管理器”
 
        .elseif al==VK_TAB
            invoke GetKeyState,VK_SHIFT
            test eax,80000000
            .if ZERO?
                invoke GetWindow,hEdit,GW_HWNDNEXT
                .if eax==NULL
                    invoke GetWindow,hEdit,GW_HWNDFIRST
                .endif
            .else
                invoke GetWindow,hEdit,GW_HWNDPREV
                .if eax==NULL
                    invoke GetWindow,hEdit,GW_HWNDLAST
                .endif
            .endif
            invoke SetFocus,eax
            xor eax,eax
            ret
上面的是摘自于 EditWndClass 过程的程序片断,它检查用户是否按下了TAB键,如果是,它调用GetKeyState来检查SHIFT键是否也被按下。GetKeyState在eax中返回的值用于确定指定的键是否被按下。如果这个键被按下,eax的高字节被设置,如果没有按下,高字节被清理。所以我们用80000000来测试返回值。若最高位是1则说明用户按下了 SHIFT-Tabs,这需要单独处理

如果用户仅按下TAB键,我们调用GetWindow来获取下一个控件的句柄。我们使用GW_HWNDNEXT标志来告诉GetWindow函数获取的窗口句柄是和当前hEdit句柄链上的下一个句柄。如果这个函数返回值为NULL,说明在当前窗口句柄链中没有其它的句柄了,所以当前的hEdit是这个链上的最后一个句柄。我们在调用GetWindow函数时设置GW_HWNDFIRST来绕回到第一个控件上。类似于tab键,shift键处理过程正好相反。
 
阅读(910) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~