• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            luqingfei@C++

            為中華之崛起而崛起!
            兼聽則明,偏聽則暗。

            Win32匯編--使用資源--菜單和加速鍵

             

            使用資源 —— 菜單和加速鍵

             

            主菜單,頂層菜單

            彈出式菜單,子菜單

            右鍵彈出式菜單

            系統彈出式菜單

             

            菜單中的菜單項有好幾種,從資源定義的角度來看,分隔用的橫線也是一個菜單項,除橫線外其他菜單項可以供用戶選擇,也可以設置為“禁止”或“灰化”狀態暫時停用。

             

            菜單項上的圓點表示選中標記是互斥的,對鉤表示是不互斥的。

             

            加速鍵就是菜單項的快捷鍵。表示當窗口是激活的時候,不必打開菜單,直接按快捷鍵就相當于選擇了菜單項。

             

            菜單和加速鍵的資源定義

            資源腳本文件舉例:

            //Menu.rc

            #include <resource.h>

            #define ICO_MAIN                     0x1000           //圖標

            #define IDM_MAIN                    0x2000           //菜單

            #define IDA_MAIN                     0x2000           //加速鍵

            #define IDM_OPEN                    0x4101

            #define IDM_OPTION                0x4102

            #define IDM_EXIT                            0x4103

            #define IDM_SETFONT              0x4201

            #define IDM_SETCOLOR            0x4202

            #define IDM_INACT                   0x4203

            #define IDM_GRAY                    0x4204

            #define IDM_BIG                        0x4205

            #define IDM_SMALL                  0x4206

            #define IDM_LIST                      0x4207

            #define IDM_DETALL                 0x4208

            #define IDM_TOOLBAR              0x4209

            #define IDM_TOOLBARTEXT     0x4210

            #define IDM_INPUTBAR             0x4211

            #define IDM_STATUSBAR           0x4212

            #define IDM_HELP                     0x4301

            #define IDM_ABOUT                  0x4302

            //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

            ICO_MAIN           ICON             “Main.ico”

            //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

            IDM_MAIN           menu             discardable

            BEGIN

                                 popup “文件(&F)”

                                 BEGIN

                                               menuitem              打開文件(&O)…”, IDM_OPEN

                                               menuitem              關閉文件(&C)…”, IDM_OPTION

                                               menuitem              separator

                                               menuitem              退出(&X)”, IDM_EXIT

                                 END

                                 popup “查看(&V)”

                                 BEGIN

                                               menuitem              字體(&F)…\tAlt+F”, IDM_SETFONT

                                               menuitem              背景色(&B) …\tCtrl+Alt+B”, IDM_SETCOLOR

                                               menuitem              separator

                                               menuitem              被禁用的菜單項”, IDM_INACT, INACTIVE

                                               menuitem              被灰化的菜單項”, IDM_GRAY, GRAYED

                                               menuitem              separator

                                               menuitem              大圖標(&G)”, IDM_BIG

                                               menuitem              小圖標(&M)”, IDM_SMALL

                                               menuitem              列表(&L)”, IDM_LIST

                                               menuitem              詳細資料(&D)”, IDM_DETAIL

                                               menuitem              separator

                                               menuitem              工具欄(&T)”

                                               BEGIN

                                                             menuitem              標準按鈕(&S)”, IDM_TOOLBAR

                                                             menuitem              文字標簽(&C)”, IDM_TOOLBARTEXT

                                                             menuitem              命令欄(&I)”, IDM_INPUTBAR

                                               END

                                               menuitem              狀態欄(&U)”, IDM_STATUSBAR

                                 END

                                 popup “幫助(&H)” HELP

                                 BEGIN

                                               menuitem              幫助主題(&H)\F1”, IDM_HELP

                                               menuitem              separator

                                               menuitem              關于本程序(&A)…”, IDM_ABOUT

                                 END

            END

            //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

            IDA_MAIN           accelerators

            BEGIN

                                 VK_F1, IDM_HELP, VIRTKEY

                                 “B”,        IDM_SETCOLOR, VIRTKEY, CONTROL, ALT

                                 “F”,        IDM_SETFONT, VIRTKEY, ALT

            END

            //編譯上述文件使用的makefile文件如下:

            NAME = Menu

            OBJS = $(NAME).obj

            RES = $(NAME).res

             

            LINK_FLAG = /subsystem:windows

            ML_FLAG = /c /coff

             

            $(NAME).exe: $(OBJ) $(RES)

                                 Link $(LINK_FLAG) $(OBJS) $(RES)

             

            .asm .obj:

                                 ml $(ML_FLAG) $<

             

            .rc .res:

                                 rc $<

             

            clean:

                                 del *.obj

                                 del *.res

             

            為了編譯資源文件,makefile中多了一個資源編譯的隱含規則:

            .rc .res:

                          rc $<

            同時在exe文件的依賴文件中增加了Menu.res文件。

             

            rc文件中,各種語句使用的是C語言語法格式,因為資源編譯器Rc.exe根本上就是Visual C++附帶的,所以在定義等值語句的時候用的是#define,包含語句使用#include<文件名>,用到16進制數值的時候并不是用匯編的語法在后面加h,而是用前面加0x的方法,如1234h寫為0x1234,注釋也要用前面加//的方法。

             

            在腳本文件的頭部,首先要把MASM32軟件包中的resource.h文件包含進來,這個文件中包括了資源定義中很多的預定義值,如窗口屬性與加速鍵的鍵值等。資源在程序中的引用往往用一個數值來表示,稱為資源的ID值,但在定義的時候直接使用數值不是很直觀,所以往往用#define語句將數值定義為容易記憶的字符串。

             

            菜單的定義

            在資源腳本文件中菜單的定義格式是:

            菜單ID          MENU           [DISCARDABLE]

            BEGIN

                          菜單項定義

                         

            END

            “菜單ID   MENU   [DISCARDABLE]”語句用來指定菜單的ID值和內存屬性,菜單ID可以是16位的整數,范圍是1~65535,在Menu.rc文件中,定義的菜單ID2000h,但菜單ID也可以字符串表示,如下面的定義:

            MainMenu menu

            begin

                          menuitem…

            end

             

            表示菜單的ID是字符串型的“MainMenu”,但這樣定義的話,在程序中引用的時候就要用字符串指針代替16進制的菜單ID值,顯得相當不便,所以在實際應用中通常使用16進制數值當做菜單ID。

             

            數值型ID的范圍限制在1~65535之間的原因是字符串在內存中的線性地址總是大于10000hAPI函數檢測參數時發現小于10000h時就可以把它認為是數值型的,大于10000h時就當做字符串指針處理。

             

            menu關鍵字后面的DISCARDABLE是菜單的內存屬性,表示菜單在不再使用的時候可以暫時從內存中釋放以節省內存,這是一個可選屬性。菜單項的定義語句必須包含在beginend關鍵字之內,這兩個關鍵字也可以用花括號{}代替。

            菜單項目的定義方法有3類:

                   MENUITEM 菜單文字,命令ID[,選項列表]        (用法1

                MENUITEM SEPARATOR                                       (用法2

             POPUP 菜單文字[,選項列表]                               (用法3

                   BEGIN

                                 item-definitions

                                

                   END

             

            用法1定義的是普通菜單項。

            組成部分有:

            菜單文字——顯示在菜單項中的字符串。如果需要字符串中某個字母帶下橫線,那么可以在字母前面加&符號,帶下橫線的字母可以被系統自動當做快捷鍵。另外,如果要把加速鍵的提示信息顯示在菜單項的右邊,如“字體”菜單項中的“Alt+F”字符,可以在兩者中間加\t,表示插入一個Tab字符),寫為“字體(&F)…\tAlt+F”,這樣Tab后面的字符在顯示的時候會右對齊。

             

            命令ID——用來分辨不同的菜單項,當菜單項被選中的時候,Windows會向窗口過程發送WM_COMMAND消息,消息的參數就是這個命令ID。用命令ID可以分辨用戶究竟選中了哪個菜單項,所以不同的菜單項應該定義不同的ID值,除非想讓兩個菜單項的功能相同。

             

            選項——用來定義菜單項的各種屬性,它可以是下列數值:

                   CHECKED——表示打上選定標志(對鉤)。

                   GRAYED——表示菜單項是灰化的。

                   INACTIVE——表示菜單是禁用的。

                   MENUBREAKMENUBARBREAK——表示將這個菜單項和以后的菜單項列到新的列中。

             

            用法2定義的是菜單項之間的分隔線,顯然,分隔線是不需要字符串和選項的。

             

            用法3定義的是彈出式菜單,頂層菜單是由多個彈出式子菜單組成的,所以在Menu.rc文件中,主菜單是由“文件”、“查看”、“幫助”3個順序定義的彈出式菜單組成的,彈出式菜單的定義也可以嵌套,如“查看”菜單中的“工具欄”又是一個彈出式菜單,在嵌套的時候要注意像寫C的源程序一樣把beginend(或者{})正確地配對。popup菜單的選項列表可以是以下的值:

                   GRAYED——表示菜單項是灰化的。

                   INACTIVE——表示菜單項是禁用的。

                   HELP——表示本項和以后的菜單項是右對齊的。

             

            popup菜單項選中的時候會自動將彈出式菜單彈出來,不需要向程序發送消息,所以在定義的參數中不需要命令ID。

             

            有些選項是可以同時定義,如果要指定超過一個的選項,中間要用逗號隔開,但是也有些小小的限制:GRAYEDINACTIVE不能同時使用,MENUBREAKMENUBARBREAK也是不能同時使用的。

             

             

            加速鍵的定義

            和菜單的定義相比,加速鍵的定義要簡單得多,具體的語法如下:

            加速鍵ID    ACCELERATORS

            BEGIN

                          鍵名,命令ID [,類型] [,選項]

                         

            END

             

            加速鍵ID同樣可以是一個字符串或者是1~65535之間的數字,整個定義內容也是用beginend(或花括號)包含起來,中間是多個加速鍵的定義項目,每個鍵占據一行,各字段的含義如下所示。

            鍵名——表示加速鍵對應的按鈕,可以有3種方式定義:

                   ^字母”:表示Ctrl鍵加上字母鍵。

                   “字母”:表示字母,這時類型必須指明是VIRTKEY

                   數值:表示ASCII碼為該數值的字母,這時類型必須指明為ASCII。

             

            命令ID——按下加速鍵后,Windows向程序發送的命令ID。如果想把加速鍵和菜單項關聯起來,這里就是要關聯期間項的命令ID。

            類型——用來指定鍵的定義方式,可以是VIRTKEYASCII,分別用來表示“鍵名”字段定義的是虛擬鍵還是ASCII碼。

            選項——可以是Alt,ControlShift中的單個或多個,如果指定多個,則中間用逗號隔開,表示加速鍵是按鍵加上這些控制鍵的組合鍵。這些選項只能在類型是VIRTKEY的情況下才能使用。

             

            在鍵名的定義中,系統按鍵如F1,F2,BackSpaceEsc等都是用虛擬鍵的方法定義的,Resource.h中已經包括所有的預定義,它們是以VK_開頭的一引起值,如VK_BACKVK_TAB,VK_RETURN,VK_ESCAPEVK_DELETE,VK_F1VK_F2等,讀者可以查看Resource.h文件,下面是加速鍵定義的一些例子:

             

            下面是加速鍵定義的一些例子:

            “^C”,      ID                 ;Ctrl+C

            “K”,        ID                 ;Shift+K

            “k”,        ID,ALT          ;Alt+k

            98,          ID,ASCII              ;b(字符bASCII碼為98

            66,          ID,ASCII              ;BShift b

            “g”,        ID                 ;g

            VK_F1,   ID,VIRTKEY ;F1

            VK_F2,   ID,VIRTKEY, CONTROL     ;Ctrl + F1

            VK_F3,   ID,VIRTKEY,ALT,SHIFT      ;Alt + Shift + F2

             

            在一個資源腳本文件中,可以定義多個菜單和多個加速鍵表,當然也有其他各式各樣的資源,有位圖、圖標與對話框等,這就涉及為這些資源取ID值的問題,取值的時候要掌握的原則是:

            1)對于同類別的多個資源,資源ID必須為不同的值,如定義了兩個菜單,那么它們的ID就必須用不同的數值表示,否則將無法分辨。

            2)對于不同類別的資源,資源ID在數值上可以是相同的,如可以將菜單和加速鍵的ID都定義為1,同時也可以有ID1的位圖或圖標等,Windows并不會把它們搞混。

             

             

            使用菜單和加速鍵

            在完成資源文件所示的編寫后,來看看如何在程序中使用菜單和加速鍵,這里先列出程序的功能說明,讀者可以先嘗試一下,以便在以下的程序分析中有所印象。程序功能如下:

            ·程序在用戶選擇了任何一個菜單項以后,會彈出一個對話框,將接收到的菜單命令ID顯示出來。

            ·選擇“大圖標”、“小圖標”、“列表”和“詳細資料”菜單項后,選中的菜單項前面會出現一個圓點選中標記,4個菜單項的選擇是互斥的。

            ·在窗口的客戶區單擊鼠標右鍵會彈出和“查看”菜單一致的彈出式菜單。

            ·在標題欄圖標上單擊鼠標左鍵,會彈出系統菜單,注意上面比默認的菜單多了兩項:“幫助主題”和“關于本程序”。

             

            接下來,將逐步分析這些功能是如何實現的。下面是Menu.asm源代碼,代碼是在以前的FirstWindow程序的基礎上改寫的,這是編寫Win32匯編程序的一個常用方法——拷貝一個模板程序再進行修改會節省很多的時間。

             

                            .386

                            .model flat,stdcall

                            option casemap:none

            ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

            ; Include 文件定義

            ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

            include         windows.inc

            include         user32.inc

            includelib      user32.lib

            include         kernel32.inc

            includelib      kernel32.lib

            ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

            ; Equ 等值定義

            ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

            ICO_MAIN        equ     1000h   ;圖標

            IDM_MAIN        equ     2000h   ;菜單

            IDA_MAIN        equ     2000h   ;加速鍵

             

            IDM_OPEN        equ     4101h

            IDM_OPTION      equ     4102h

            IDM_EXIT        equ     4103h

             

            IDM_SETFONT     equ     4201h

            IDM_SETCOLOR    equ     4202h

            IDM_INACT       equ     4203h

            IDM_GRAY        equ     4204h

            IDM_BIG         equ     4205h

            IDM_SMALL       equ     4206h

            IDM_LIST        equ     4207h

            IDM_DETAIL      equ     4208h

            IDM_TOOLBAR     equ     4209h

            IDM_TOOLBARTEXT equ     4210h

            IDM_INPUTBAR    equ     4211h

            IDM_STATUSBAR   equ     4212h

             

            IDM_HELP        equ     4301h

            IDM_ABOUT       equ     4302h

            ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

            ; 數據段

            ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

                            .data?

            hInstance       dd      ?

            hWinMain        dd      ?

            hMenu           dd      ?

            hSubMenu        dd      ?

             

                            .const

            szClassName     db      'Menu Example',0

            szCaptionMain   db      'Menu',0

            szMenuHelp      db      '幫助主題(&H)',0

            szMenuAbout     db      '關于本程序(&A)...',0

            szCaption       db      '菜單選擇',0

            szFormat        db      '您選擇了菜單命令:%08x',0

            ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

            ; 代碼段

            ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

                            .code

            ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

            ; 彈出窗口,顯示菜單命令ID

            ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

            _DisplayMenuItem    proc    _dwCommandID

                                local @szBuffer[256]:byte

                                   

                            pushad

                            invoke wsprintf, addr @szBuffer, addr szFormat, _dwCommandID

                            invoke MessageBox, hWinMain, addr @szBuffer, offset szCaption, MB_OK

                            popad

                            ret

            _DisplayMenuItem    endp

            ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

            ; 退出

            ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

            _Quit           proc

                            invoke DestroyWindow, hWinMain

                            invoke PostQuitMessage, NULL

                            ret

            _Quit           endp

            ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

            ; 窗口過程

            ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

            _ProcWinMain    proc uses ebx edi esi, hWnd, uMsg, wParam, lParam

                            local @stPos:POINT

                            local @hSysMenu

             

                            mov eax,uMsg

            ;*************************************************************************

                            .if     eax == WM_CREATE

                                    invoke     GetSubMenu, hMenu, 1

                                    mov     hSubMenu, eax

            ;***************************************************************************

            ; 在系統菜單中添加菜單項

            ;***************************************************************************

                                    invoke GetSystemMenu, hWnd, FALSE

                                    mov     @hSysMenu, eax

                                    invoke AppendMenu, @hSysMenu, MF_SEPARATOR, 0, NULL

                                    invoke AppendMenu, @hSysMenu, 0, IDM_HELP, offset szMenuHelp

                                    invoke AppendMenu, @hSysMenu, 0, IDM_ABOUT, offset szMenuAbout

            ;***************************************************************************

            ; 處理菜單及加速鍵消息

            ;***************************************************************************

                            .elseif eax == WM_COMMAND

                                    invoke _DisplayMenuItem, wParam

                                    mov     eax, wParam

                                    movzx   eax, ax

                                    .if     eax == IDM_EXIT

                                            call    _Quit

                                    .elseif eax >= IDM_TOOLBAR && eax <= IDM_STATUSBAR

                                            mov ebx, eax

                                            invoke GetMenuState, hMenu, ebx, MF_BYCOMMAND

                                            .if     eax == MF_CHECKED

                                                    mov eax, MF_UNCHECKED

                                            .else

                                                    mov eax, MF_CHECKED

                                            .endif

                                            invoke CheckMenuItem, hMenu, ebx, eax

                                    .elseif eax >= IDM_BIG && eax <= IDM_DETAIL

                                            invoke CheckMenuRadioItem, hMenu, IDM_BIG, IDM_DETAIL, eax, MF_BYCOMMAND

                                    .endif

            ;***************************************************************************

            ; 處理系統菜單消息

            ;***************************************************************************

                            .elseif eax == WM_SYSCOMMAND

                                    mov     eax, wParam

                                    movzx   eax, ax

                                    .if     eax == IDM_HELP || eax == IDM_ABOUT

                                            invoke _DisplayMenuItem, wParam

                                    .else

                                            invoke DefWindowProc, hWnd, uMsg, wParam, lParam

                                            ret

                                    .endif

            ;***************************************************************************

            ; 單擊鼠標右鍵時彈出一個POPUP菜單

            ;***************************************************************************

                            .elseif eax == WM_RBUTTONDOWN

                                    invoke GetCursorPos, addr @stPos

                                    invoke TrackPopupMenu, hSubMenu, TPM_LEFTALIGN, @stPos.x, @stPos.y, NULL, hWnd, NULL

            ;***************************************************************************

                            .elseif eax == WM_CLOSE

                                    call    _Quit

            ;***************************************************************************

                            .else

                                    invoke     DefWindowProc,hWnd,uMsg,wParam,lParam

                                    ret

                            .endif

            ;***************************************************************************

                            xor     eax,eax

                            ret

            _ProcWinMain    endp

            ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

            _WinMain        proc

                            local   @stWndClass:WNDCLASSEX

                            local   @stMsg:MSG

                            local   @hAccelerator

             

                            invoke     GetModuleHandle,NULL

                            mov     hInstance,eax

                            invoke LoadMenu, hInstance, IDM_MAIN   ;獲取菜單句柄

                            mov     hMenu, eax                      ;把菜單句柄存儲于hMenu變量

                            invoke LoadAccelerators, hInstance,IDA_MAIN

                            mov     @hAccelerator, eax

                            

                            invoke     RtlZeroMemory,addr @stWndClass,sizeof @stWndClass

            ;**************************************************************************

            ; 注冊窗口類

            ;**************************************************************************

                            invoke LoadIcon, hInstance, ICO_MAIN

                            mov     @stWndClass.hIcon, eax

                            mov     @stWndClass.hIconSm, eax

                            invoke LoadCursor, 0, IDC_ARROW

                            mov     @stWndClass.hCursor, eax

                            push    hInstance

                            pop     @stWndClass.hInstance

                            mov     @stWndClass.cbSize, sizeof WNDCLASSEX

                            mov     @stWndClass.style, CS_HREDRAW or CS_VREDRAW

                            mov     @stWndClass.lpfnWndProc, offset _ProcWinMain

                            mov     @stWndClass.hbrBackground,COLOR_WINDOW + 1

                            mov     @stWndClass.lpszClassName, offset szClassName

                            invoke     RegisterClassEx, addr @stWndClass

            ;***************************************************************************

            ; 建立并顯示窗口

            ;***************************************************************************

                            invoke     CreateWindowEx, WS_EX_CLIENTEDGE, \

                                    offset szClassName, offset szCaptionMain, \

                                    WS_OVERLAPPEDWINDOW, \

                                    100, 100, 400, 300, \

                                    NULL, hMenu, hInstance, NULL

                            mov     hWinMain,eax

                            invoke     ShowWindow,hWinMain,SW_SHOWNORMAL

                            invoke     UpdateWindow,hWinMain

            ;**************************************************************************

            ; 消息循環

            ;**************************************************************************

                            .while TRUE

                                    invoke     GetMessage, addr @stMsg, NULL, 0, 0

                                    .break     .if eax == 0

                                    invoke TranslateAccelerator, hWinMain, @hAccelerator, addr @stMsg

                                    .if     eax == 0

                                            invoke TranslateMessage, addr @stMsg

                                            invoke DispatchMessage, addr @stMsg

                                    .endif

                            .endw

                            ret

            _WinMain        endp

            ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

            start:

                            call    _WinMain

                            invoke ExitProcess, NULL

            ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

                            end start

             

            1、加載菜單

            在窗口中加載菜單的方法有兩個:一是在注冊窗口類的時候指定類的默認菜單;二是在建立窗口的時候在參數中指定菜單句柄。Menu.asm程序中用的是第2種方法:

                   invoke CreateWindowEx, WS_EX_CLIENTEDGE, \

                                 offset szClassName, offset szCaptionMain, \

                                 WS_OVERLAPPEDWINDOW,\

                                 100, 100, 400, 300, \

                                 NULL, hMenu, hInstance, NULL

            在參數中指出了hMenu。不管用哪種方法,首先都必須使用LoadMenu函數來獲取菜單句柄hMenu,如下面的語句:

                   invoke     LoadMenu, hInstance, IDM_MAIN

                   mov hMenu, eax

            這個函數的第1個參數是用GetModuleHandle獲取的實例句柄,第2個參數指定需要裝入的菜單資源ID,函數返回菜單句柄。在得到菜單句柄以后,我們先把它放入hMenu變量保存起來以便后用。

             

            當資源文件中用字符串為名稱定義菜單而不是用數值的時候,例如:

                   MainMenu      menu             //定義菜單名為字符串“MainMenu

                   begin

                                

                   end

            那么在程序中就必須用字符串指針代替菜單ID做參數:

                   szMenu   “MainMenu”,0              ;在數據段中定義菜單名稱字符串

                                

                                 invoke     LoadMenu, hInstance, addr szMenu            ;在程序中裝載

                                 mov        hMenu, eax

            注:用字符串為名稱定義資源,在資源裝載函數LoadXXXX中用戶字符串指針做參數裝入,這實際上是一個通用的方法,不僅適用于菜單資源,對于其他類別的資源也是適用的。

             

             

            2、加載加速鍵

            和菜單一樣,加速鍵在使用前也要裝入,參數同樣是在資源腳本文件中定義的加速鍵ID,程序中對應的語句是:

                   invoke     LoadAccelerators, hInstance, IDA_MAIN

                   mov        @hAccelerator, eax

            其實我們自己在程序中也可以很方便地實現加速鍵功能,方法是:在WM_KEYDOWN消息中判斷鍵盤消息并按照自定義的邏輯進行處理,使用加速鍵實際上是讓Windows替我們完成這個功能,Windows實現的方法正是在消息循環中檢查WM_KEYDOWNWM_SYSKEYDOWN消息。下面是使用加速鍵時消息循環的代碼,請注意粗體字部分:

            .while      TRUE

                          invoke     GetMessage, addr @stMsg, NULL, 0, 0

                          .break     .if    eax == 0

                          invoke     TranslateAccelerator, hWinMain, @hAccelerator, addr @stMsg

                          .if           eax == 0

                                        invoke     TranslateMessage, addr @stMsg

                                        invoke     DispatchMessage, addr @stMsg

                          .endif

            .endw

             

            TranslateAccelerator函數是實現加速鍵功能的核心,它的參數為目標窗口、加速鍵句柄和GetMessage取得的消息結構。該函數檢查消息結構中的消息,如果遇到WM_KEYDOWNWM_SYSKEYDOWN消息則檢測加速鍵資源,看按鍵是否符合某個加速鍵,符合的話則向目標窗口發送WM_COMMANDWM_SYSCOMMAND消息,并返回TRUE,不符合的話不進行任何處理并返回FALSE。

             

            由于加速鍵的鍵碼并不是用戶真正想輸入窗口的,比如用戶在寫字板中輸入文字,按Ctrl+C鍵是為了“拷貝”,而不是想把Ctrl+C鍵對應的字符輸入文檔,所以這個Ctrl+C的鍵碼在完成加速鍵的使命后就應該丟棄,也就是說符合加速鍵的鍵盤消息不應該再發送給窗口,TranslateMessageDispatchMessage函數前的邏輯判斷就是這樣的意圖:只有TranslateAccelerator沒有轉換的消息(返回值eax0)才繼續處理。

             

            另外,TranslateAccelerator的參數中有個“目標窗口”,例子中是程序的主窗口hWinMain,為什么要設置這樣一個參數而不像DispatchMessage函數一樣使用MSG結構中的hwnd呢?這是因為鍵盤消息可以產生于不同窗口中——既可能是主窗口,也可能是其他子窗口,如果用@stMsg.hwnd做目標窗口,就必須在所有子窗口的窗口過程中都設置處理加速鍵消息的代碼,這顯然是一種浪費,所以一般把所有的加速鍵消息都發送到主窗口,然后集中在主窗口的窗口過程中處理WM_COMMAND消息,這樣有利精簡代碼。

             

            3、菜單和加速鍵消息

            當用戶選擇了一個菜單項的時候,Windows向菜單所屬的窗口發送WM_COMMAND消息;而用戶按下了一個加速鍵的時候,WindowsTranslateAccelerator函數指定的目標窗口發送WM_COMMAND消息。一般這兩種情況對應的窗口都是主窗口,所以可以在主窗口中的窗口過程中集中處理WM_COMMAND消息,而不必考慮它究竟是菜單引發的還是加速鍵引發的。

             

            WM_COMMAND消息的兩個參數是這樣定義的:

            wParam的高位 = wNotifyCode         ;通知碼

            wParam的低位 = wID                      ;命令ID

            lParam =hwndCtl                               ;發送WM_COMMAND的子窗口句柄

            除了菜單和加速鍵,WM_COMMAND消息也可以由其他子窗口引發,如主窗口中的按鈕或工具欄等,lParam參數指定了引發消息的子窗口句柄,對于菜單和加速鍵引發的WM_COMMAND消息,lParam的值為零。wParam參數的低16位是命令ID,也就是資源腳本文件中菜單項的命令ID或加速鍵的命令ID,高16位是通知碼,菜單消息的通知碼是0,加速鍵消息的通知碼為1。

             

            在需要處理菜單和加速鍵消息的窗口過程中,一般需要增加一個WM_COMMAND分支來處理對應的消息,這個分支的一般結構為:

                   .elseif             eax == WM_COMMAND     ;eax中為wMsg

                                        mov        eax, wParam

                                        movzx     eax, ax

                                        .if           eax == 命令ID1

                                                     

                                        .elseif      eax == 命令ID2

                                                     

                                        .endif

            其中movzx eax, ax指令將16位的ax擴展到32位的eax,相當于將eax的高16位清零,作用就是當消息由加速鍵引起時,將高16位中的1忽略,這樣下面的分支就可以同時處理菜單和加速鍵消息,當然讀者也可以去掉這一句,這樣下面的比較語句中就要使用ax而不是eax。

             

            在例子程序中,mov eax, wParam前面還有一句invoke _DisplayMenuItem, wParam, 作用是在處理WM_COMMAND消息前將wParam的值通過一個對話框顯示出來,讀者可以和資源腳本文件中定義的命令ID值對比一下,在正常使用的程序中可以去掉這一句。

             

            讀者可以發現,資源文件中定義的“字體”菜單項的ID0x4201,當選中“字體”菜單項的時候,對話框中顯示的wParam數值正是00004201,而按下加速鍵Alt+F的時候,顯示出來的值卻是00014201了,它們的區別就是高16位中的通知碼不同。

             

            4、菜單項的修改

            在程序的運行中也可以動態修改菜單項,包括添加、刪除和修改操作,這些操作是通過幾個API函數來完成的:

                   invoke     AppendMenu, hMenu, uFlags, uIDNewItem, lpNewItem           ;添加菜單項

                   invoke     InsertMenu, hMenu, uPosition, uFlags, uIDNewItem, lpNewItem     ;插入菜單項

                   invoke     ModifyMenu, hMenu, uPosition, uFlags, uIDNewItem, lpNewItem   ;修改菜單項

                   invoke     DeleteMenu, hMenu, uPosition, uFlags        ;刪除菜單項

                   invoke     RemoveMenu, hMenu, uPosition, uFlags      ;刪除菜單項

             

            其中AppendMenu用來在一個菜單的最后添加菜單項,InsertMenu則在中間插入菜單項,ModifyMenu可以修改一個菜單項的文字,DeleteMenuRemoveMenu則可以刪除一個菜單項。

             

            這些函數中的參數都雷同的,hMenu參數指要操作的菜單句柄;uPosition用來定位要操作的菜單項,定位的方法有兩種:用命令ID定義或用位置索引,用哪一種方法取決于后面的uFlags參數,當uFlagsMF_BYCOMMAND時,uPosition為菜單項的命令ID,而當uFlagsMF_BYPOSITION的時候,uPosition表示菜單項的位置索引,索引是從0開始的,也就是說第一個菜單項的索引值為0。

             

            AppendMenu函數總是在菜單的最后添加新的菜單項,所以不需要uPosition參數。

            對于AppendMenuInsertMenu,會有一個新的菜單項產生,uIDNewItem就表示這個新菜單項的命令ID,lpNewItem指向新菜單項的文字字符串,ModifyMenu函數可以修改一個菜單項的命令ID或文字字符串,所以也有uIDNewItemlpNewItem參數。而用來刪除菜單項的DeleteMenuRemoveMenu顯然用不著uIDNewItemlpNewItem參數。

             

            uFlags參數除了指定MF_BYCOMMAND還是MF_BYPOSITION外,還可以組合指定菜單項的其他屬性,如MF_CHECKED,MF_DISABLED, MF_ENABLED, MF_GRAYED, MF_MENUBARBREAK, MF_MENUBREAK, MF_SEPARATORMF_UNCHECKED等,從其字面上就可以看出這些屬性的含義。

             

            DeleteMenuRemoveMenu的不同之外在于對popup菜單項的處理。當它們用于popup屬性的菜單項時,DeleteMenu不僅刪除菜單項,而且將這個popup菜單項的所有子項目全部刪除,這樣,這個popup菜單就不能在別的地方繼續使用;而RemoveMenu僅從菜單中移去這個popup菜單項,整個popup菜單在內存中還是存在的。以Menu.asm程序為例,掃鼠標右鍵彈出的菜單實際上是主菜單中的“查看”菜單項,假如用DeleteMenu刪除主菜單中的“查看”項目,那么按右鍵也就彈不出菜單了,而用RemoveMenu刪除主菜單中的“查看”項目,按鼠標右鍵依然可以彈出菜單。對于非popup屬性的菜單項,DeleteMenuRemoveMenu的效果是同樣的。

             

            5、使用系統菜單

            系統菜單指按下了標題欄圖標后彈出的菜單,和窗口菜單不同,選中系統菜單的菜單項后,Windows向窗口發送的是WM_SYSCOMMAND消息而非WM_COMMAND消息。默認的系統菜單中已經有“還原”、“移動”、“大小”、“最大化”、“最小化”和“關閉”等菜單項,這些菜單項的命令ID已經預定義為SC_RESTORE,SC_MOVE,SC_SIZESC_MAXIMIZE,SC_MINIMIZESC_CLOSE等,如果讀者要自己處理它們,可以在WM_SYSCOMMAND消息中建立一個比較分支對它們進行處理,一般在程序中并不自己處理WM_SYSCOMMAND消息,而是交給DefWindowProc處理。

             

            如何在系統菜單中添加自己的菜單項呢?方法就是使用上面介紹的AppendMenu(當然也可以用InsertMenu),在添加前必須用GetSystemMenu函數首先獲取系統菜單的句柄。例子程序在窗口初始化的時候在系統菜單尾添加了一個分隔線和兩個菜單項:“幫助主題”和“關于本程序”:

                   .if    eax == WM_CREATE

                         

                          invoke     GetSystemMenu, hWnd, FALSE

                          mov        @hSysMenu, eax

                          invoke     AppendMenu, @hSysMenu, MF_SEPARATOR, 0, NULL

                          invoke     AppendMenu, @hSysMenu, 0, IDM_HELP, offset szMenuHelp

                          invoke     AppendMenu, @hSysMenu, 0, IDM_ABOUT, offset szMenuAbout

            在窗口過程中處理系統菜單消息的分支結構為:

                   .elseif      eax == WM_SYSCOMMAND

                                 mov        eax, wParam

                                 .if           ax == 自定義ID1

                                              

                                 .else

                                               invoke     DefWindowProc, hWnd, uMsg, wParam, lParam

                                               ret

                                 .endif

            和處理WM_COMMAND消息不同的是,在WM_SYSCOMMAND消息中處理了自定義的菜單命令ID后,必須把其他命令ID交給DefWindowProc處理,并直接把返回值返回給Windows,不然的話會發現窗口不能移動,不能關閉,不能最小化……因為它相當于屏幕了所有SC_RESTORE, SC_MOVE, SC_SIZE, SC_MAXIMIZE, SC_MINIMIZESC_CLOSE等消息的處理。

             

            6、右鍵彈出菜單

            例子程序的一個功能是當用戶在窗口客戶區按下鼠標右鍵的時候彈出一個菜單,這個功能是用TrackPopupMenu函數實現的。TrackPopupMenu函數的用法:

                   invoke     TrackPopupMenu, hMenu, uFlags, x, y, nReserved, hWnd, lpRect

            這個函數本身很簡單,執行后在參數指定的x,y位置彈出一個屬于hWnd窗口(也就是說WM_COMMAND消息發到這個窗口)的菜單,菜單句柄是hMenu。函數中的坐標是以整個屏幕左上角為基準的,所以彈出菜單的位置不一定在窗口的客戶區內,它可以是屏幕的任何一個地方。

             

            uFlags參數指定一些和位置相關的選項,它可以是PM_CENTERALIGN,TPM_LEFTALIGNTPM_RIGHTALIGN三者之一,表示(x, y)坐標是代表彈出菜單位置的中間、左上角還是右上角,一般的習慣是使用TPM_LEFTALIGN,這樣菜單會在鼠標點擊處的右邊彈出。uFlags中同時還可以指定用鼠標左鍵還是右鍵選定菜單項,定義值可以是TPM_LEFTBUTTONTPM_RIGHTBUTTON,如果選擇TPM_RIGHTBUTTON的話,對在菜單項上面按鼠標左鍵是沒有反應的。

             

            lpRect指向一個RECT結構,用來指定一個區域,當菜單彈出后,在這個區域外單擊鼠標,菜單才會消失,如果這個參數指定為NULL的話,在菜單之外單擊鼠標,菜單就會消失。

             

            在使用TrackPopupMenu之前,有幾個準備工作是要做的:為了在客戶區中按下鼠標右鍵彈出菜單,我們當然要處理鼠標右鍵消息,也就是說在WM_RBUTTIONDOWN消息中調用TrackPopupMenu函數,一般的習慣是在鼠標按下的地方彈出菜單,所以還要首先獲取鼠標光標的位置,然后在此位置彈出菜單。

             

            要獲取鼠標位置,可以用GetCursorPos函數:

                   invoke GetCusorPos, lpPoint

            參數lpPoint指向一個POINT數據結構,這個結構只有兩個字段:

            POINT STRUCT

            x     DWORD       ?

            y     DWORD        ?

            POINT ENDS

             

            該結構用來表示一個點的(x, y)坐標,GetCursorPos將當前的鼠標位置返回到這個結構中,程序中的相關代碼是:

                   local        @stPos:POINT             ;首先定義一個POINT結構

                  

                   invoke     GetCursorPos, addr @stPos         ;獲取鼠標位置

                   invoke     TrackPopupMenu, hSubMenu, \

                                 TPM_LEFTALIGN, @stPos.x, @stPos.y, NULL, hWnd, NULL

            GetCursorPos獲取的鼠標位置是一個POINT結構,但TrackPopupMenu輸入坐標的方法是用x,y兩個參數,而不是一個POINT結構,所以要用結構中的兩個字段@stPos.x@stPos.y分別輸入。

             

            使用TrackPopupMenu時要注意的是,彈出的菜單句柄必須是popup類型的,而在資源文件中定義并且可以用LoadMenu函數裝入的菜單并不是popup類型的,popup菜單(如例子中的“文件”與“查看”等)只能在第二層中才能定義,在程序中用GetSubMenu得到的第二層子菜單的句柄才是popup類型的。GetSubMenu函數的用法是:

                   invoke     GetSubMenu, hMenu, nPos

                   .if           eax

                                 mov hSubMenu, eax

                   .endif

            nPos參數指定要獲取的菜單的位置索引,GetSubMenu的返回值是獲取的子菜單句柄。例子用invoke GetSubMenu, hMenu, 1取得第二個子菜單(“文件”子菜單為0,“查看”子菜單為1,……)的句柄,然后在TrackPopupMenu中使用,這個菜單句柄就是主菜單中的“查看”菜單,所以按鼠標右鍵彈出的菜單和下拉菜單中的“查看”菜單是一模一樣的。

             

             

            7、菜單狀態的檢測和設置

            在程序中經常要對菜單項的狀態進行設置,如剪貼板中沒有數據時,“粘貼”菜單項應該灰化,窗口中沒有被選中的字符時,“拷貝”菜單項也應該灰化,這樣可以給使用者一個善意的提醒。同樣,對菜單的狀態也常常需要檢測,如查看菜單項的狀態是否處于灰化狀態或選中狀態以便進行下一步操作等。

             

            對菜單項狀態的檢測可以用GetMenuState函數來完成,用法是:

                   invoke     GetMenuState, hMenu, uId, uFlags

            參數hMenu是菜單的句柄,uId用來定位要檢測的菜單項,當uFlagsMF_BYCOMMAND的時候,uId用菜單項的命令ID指定,當uFlagsMF_BYPOSITION的時候,uId的值是位置索引,函數執行后的返回值為-1時表示失敗,否則會是MF_CHECKED, MF_DISABLED, MF_GRAYED, MF_HILITE, MF_MENUBARBREAK, MF_MENUBREAKMF_SEPARATOR的組合值,它們分別表示菜單項的狀態是選中、禁止、灰化、高亮顯示以及3種分隔線,讀者可以用test指令測試相應的數據位來分辨菜單項處于哪種狀態,一般的測試代碼如下:

                   invoke    GetMenuStage, hMenu, IDM_XXX, MF_BYCOMMAND

                   .if           eax & MF_CHECKED

                                 ;表示IDM_XXX菜單項現在是選中狀態

                   .endif

             

            同樣,讀者也可以用eax & MF_DISABLEDeax & MF_GRAYED等條件測試其他狀態。

             

            設置菜單項的狀態可以用下列3個函數來實現不同的功能:

                   invoke     EnableMenuItem, hMenu, uIDEnableItem, uEnable

                   invoke     CheckMenuItem, hMenu, uIDCheckItem, uCheck

                   invoke     CheckMenuRadioItem, hMenu, idFirst, idLast, idCheck, uFlags

             

            EnableMenuItem函數將菜單項在禁用、可用和灰化狀態之間切換,uEnable可以取值為MF_DISABLED, MF_ENABLEDMF_GRAYED,分別代表這3種狀態。

             

            CheckMenuItem函數將菜單項在非互斥的選定狀態和非選定狀態之間切換(即前面是否有對鉤),uCheck的取值可以是MF_CHECKEDMF_UNCHECKED,代表選定或非選定狀態。

             

            CheckMenuRadioItem將菜單項在互斥的選定狀態和非選定狀態之間切換(即前面是否有圓點標志),由于互斥的菜單項在一個范圍內只有一個是可以選定的,當選定另一個的時候,原來的選定應該撤銷,idFirstidLast就指定了這個互斥范圍。函數在選定idCheck指定的菜單項的同時將自動清除idFirstidLast范圍內的其他選定。所以uFlags中無需指定狀態,只需指定MF_BYCOMMANDMF_BYPOSITION定位方式。

             

            在這些函數的參數中,uIDEnableItem, uIDCheckItem, idFirst, idLastidCheck用來定位菜單項,同樣,參數的取值可以是菜單項的命令ID或位置索引,可以在狀態參數(uEnable, uCheck, uFlags)中組合定義MF_BYCOMMANDMF_BYPOSITION來決定使用哪種方法。

             

            在例子程序中,當選中IDM_TOOLBARIDM_STATUSBAR之間的菜單項的時候,程序先用invoke GetMenuState, hMenu, ebx, MF_BYCOMMAND獲取當前的狀態,檢查是否選定,并將選定狀態反轉后用CheckMenuItem重新設置:

                   .elseif             eax >= IDM_TOOLBAR && eax <= IDM_STATUSBAR

                                        mov ebx, eax

                                        invoke     GetMenuState, hMenu, ebx, MF_BYCOMMAND

                                        .if           eax == MF_CHECKED

                                                      mov        eax, MF_UNCHECKED

                                        .else

                                                      mov        eax, MF_CHECKED

                                        .endif

                                        invoke     CheckMenuItem, hMenu, ebx, eax

            當選中IDM_BIGIDM_DETAIL之間的菜單項的時候,程序用CheckMenuRadioItem將原告IDM_BIGIDM_DETAIL范圍內的互斥選定撤銷并將當前選定的菜單項加圓點標記。

            .elseif      eax >= IDM_BIG && eax <= IDM_DETAIL

                          invoke     CheckMenuRadioItem, hMenu, IDM_BIG, IDM_DETAIL, \

                                        eax, MF_BYCOMMAND

             

            最后,修改菜單狀態的時機是什么時候呢?在程序中似乎不應該隨時去檢測狀態并設置,這顯然是很浪費資源的。Windows考慮到了這一點:在菜單將要激活的時候,也就是用戶在菜單上按動鼠標的時候,Windows在菜單彈出之前會向窗口過程發送WM_INITMENU消息,我們可以從容不迫地在這里進行各種檢測,并設置對應的菜單項。

             

            注意:讀者可以注意到,在狀態參數中指定MF_COMMANDMF_POSITION將決定位置參數用命令ID還是位置索引表示,這個規則在所有的菜單函數中都是適用的,MF_BYCOMMAND是默認值(它的定義值是0),如果兩者都不定義的話,位置參數代表的就是命令ID

             

            8、其他菜單函數

            除了前面介紹的一些函數之外,還有一些不太常用的菜單函數,在這里作一個簡單的介紹。

             

            菜單不一定非要在資源文件中定義,在程序中也可以用代碼來建立菜單,不過比較麻煩一點,方法是先用CreateMenu建立一個菜單,CreateMenu函數沒有參數,調用后返回一個沒有任何菜單項的菜單句柄,接下來就可以用AppendMenu在上面一條條地添加菜單項了。

             

            同樣,CreatePopupMenu也可以建立一個沒有任何菜單項的菜單句柄,但它建立的是popup類型的菜單句柄,可以在TrackPopupMenu中直接使用。

             

            如果要獲取一個窗口當前使用的菜單句柄,那么可以使用GetMenu函數:

                   invoke     GetMenu, hWnd

                   mov        hMenu, eax

            一個菜單的總項數可以用GetMenuItemCount函數獲?。?/span>

                   invoke     GetMenuItemCount, hMenu

            不過GetMenuItemCount函數的返回值不包括子菜單展開以后的項數,而是指最上層菜單的項數,比如在例子程序中對hMenu統計的結果是3,因為Menu.rc中定論的最上層的菜單項是“文件”、“查看”、和“幫助”,總共3個,如果要統計全部展開后的項數,那么只好用GetSubMenu一層層地統計下去了。

             

            對菜單中各個菜單項當前的文字和命令ID也是可以查詢的,方法是用GetMenuStringGetMenuItemID,讀者可以參考命令手冊。

             

            建立窗口時指定了菜單句柄后并不是不能改變的,我們常常見到一些編輯軟件,沒有打開文件之前菜單只有寥寥幾項,一打開文件以后功能菜單就全部出來了,實際上這是用SetMenu函數完成的:

                   invoke     SetMenu, hWnd, hMenu

             

            可以在資源文件中預定義幾個不同的菜單,在使用的時候根據不同情況用SetMenu設置不同的菜單句柄。

             

            使用菜單后,要涉及清除的總是,和窗口相連的菜單句柄在窗口摧毀的時候會由Windows自動釋放,不需要手工釋放,但沒有和窗口相連的菜單就要由程序自己來釋放了,方法是使用DestroyMenu函數,比如沒有和窗口相連而僅用TrackPopupMenu彈出的菜單句柄:

                   invoke     DestroyMenu, hMenu

             

             

            posted on 2010-08-31 09:43 luqingfei 閱讀(3559) 評論(1)  編輯 收藏 引用 所屬分類: Win32匯編程語言序設計

            評論

            # re: Win32匯編--使用資源--菜單和加速鍵 2011-03-29 09:38 coreBugZJ

            初學的匯編(用NASM),仰慕?。?nbsp; 回復  更多評論   

            導航

            <2010年7月>
            27282930123
            45678910
            11121314151617
            18192021222324
            25262728293031
            1234567

            統計

            留言簿(6)

            隨筆分類(109)

            隨筆檔案(105)

            Blogers

            Game

            Life

            NodeJs

            Python

            Useful Webs

            大牛

            搜索

            積分與排名

            最新評論

            閱讀排行榜

            評論排行榜

            亚洲国产另类久久久精品| 国产亚洲婷婷香蕉久久精品| 婷婷综合久久狠狠色99h| 久久夜色tv网站| 久久天天躁狠狠躁夜夜不卡| 亚洲国产一成久久精品国产成人综合 | 久久青青草原亚洲av无码app| 久久久久久夜精品精品免费啦| 亚洲国产成人久久精品动漫| 无码国内精品久久人妻麻豆按摩| 无码人妻久久一区二区三区| 国产精品狼人久久久久影院| 99久久做夜夜爱天天做精品| 色综合久久天天综合| 精品久久久久成人码免费动漫| 久久国产乱子伦精品免费强| 亚洲国产成人久久一区久久| 久久综合久久综合久久综合| 国产精品亚洲综合久久| 办公室久久精品| 69国产成人综合久久精品| 2021最新久久久视精品爱| 91久久精品电影| 久久精品国产亚洲欧美| 久久久噜噜噜久久中文福利| 婷婷久久综合| 激情久久久久久久久久| 麻豆一区二区99久久久久| 久久se这里只有精品| 久久久久夜夜夜精品国产| 久久香综合精品久久伊人| 亚洲欧美日韩久久精品| 久久一本综合| 久久精品国产一区二区电影| 亚洲国产精品久久久久久| 2021精品国产综合久久| 国产精品99久久99久久久| 久久青青草原亚洲av无码app | 久久午夜夜伦鲁鲁片免费无码影视| 久久99国产精品久久99| 国内精品久久国产大陆|