增加LoadMenuIndirectEx函数

增加LoadMenuIndirectEx函数来装载MENUEX_TEMPLATE_ITEM菜单模板

使用LoadMenuIndirect的API函数可以装载MENUEX_TEMPLATE_HEADER扩展菜单模板,但不能使用字符串资源,所以用途不大。如果自己编写一个装载函数LoadMenuIndirectEx,就可以使菜单模板用于多语言编程中,可以节省很多的工程量。

原理如下:

扩展菜单模板中,其菜单项使用MENUEX_TEMPLATE_ITEM定义,该结构szText成员是菜单项的Unicode串;wFlags成员用来指示菜单项的属性,它有2个标志值,MFR_END指示这是该菜单条的最后一个菜单项,MFR_POPUP指示这是一个子菜单项。我给wFlags成员增加一个新的标志值MFR_TXTID,来指示szText成员是字符串资源ID。同时,不管wFlags成员是否有MFR_TXTID标志位,如果szText成员为空串,则均将菜单ID作为字符串资源ID来使用,所以如果菜单ID就是字符串资源ID,你可以不使用MFR_TXTID标志。

编写一个装载函数LoadMenuIndirectEx,并增加装载快捷菜单的功能,因为LoadMenuIndirect函数不能装载为快捷菜单。

编写LoadMenuIndirectEx函数的主要目的,是为了实现菜单的本地化。通常情况下,我们会使用菜单资源本地化的方法来实现菜单的本地化,这样就需要编辑多个菜单资源。如果使用菜单模板,则只需一个菜单模板就可以实现菜单本地化,当然还需要字符串表本地化,也就是说,只要本地化字符串表,就可以本地化一个程序中与字符串相关的所有项目。

注: 有关字符串表本地化,请参阅本站《字符串表资源(STRINGTABLE)和程序本地化》。有关菜单模板请查阅Windows API文档。

示例模板和完整代码如下:

1. 资源文件rsrc.rc的内容示例

以下资源文件的内容为定义字符串表,包含菜单中使用的字符串,也可以根据需要增加控件字符串等。

  #include "\masm32\include64\resource.h"

//=======================================
//菜单标题ID,也可作为菜单ID
//=======================================
//----------
//主菜单
//----------
 #define IDS_FILE       1600        //文件
 #define IDS_NEW        1601        //新建
 #define IDS_CLOSE      1611        //关闭
 #define IDS_ABOUT      1612        //关于
//---------
//编辑器
//---------
 #define IDS_EDIT         1616     //编辑
 #define IDS_UNDO         1619     //重做
 #define IDS_COPY         1621     //复制
 #define IDS_CUT          1622     //剪切
 #define IDS_PASTE        1623     //粘贴
 #define IDS_DEL          1624     //删除
 #define IDS_SELECTALL    1625     //全选
//=========================
// 字符串表定义
//=========================
STRINGTABLE
BEGIN
//主菜单
  IDS_FILE     "文 件"
  IDS_NEW      "新 建"
  IDS_CLOSE    "关 闭"
  IDS_ABOUT    "关 于"
//编辑器菜单
  IDS_EDIT        "编 辑"
  IDS_UNDO        "重 做"
  IDS_COPY        "复 制"
  IDS_CUT         "剪 切"
  IDS_PASTE       "粘 贴"
  IDS_DEL         "删 除"
  IDS_SELECTALL   "全 选"
END

2. 源代码

源代码共3个函数,功能如下:

Menu_GetTempItemInfoEx: 解释一个MENUEX_TEMPLATE_ITEM结构,并填写MENUITEMINFO结构。

Menu_LoadTemplateItemEx: 将模板中的全部菜单项添加到菜单句柄中。该函数是一个递归函数。

LoadMenuIndirectEx: 这是菜单装载的主函数。

;----------
;主菜单
;----------
IDS_FILE     EQU  1600        ;文件
IDS_NEW      EQU  1601        ;新建
IDS_CLOSE    EQU  1611        ;关闭
IDS_ABOUT    EQU  1612        ;关于
;---------
;编辑菜单
;---------
IDS_EDIT        EQU 1616     ;编辑
IDS_UNDO        EQU 1619     ;重做
IDS_COPY        EQU 1621     ;复制
IDS_CUT         EQU 1622     ;剪切
IDS_PASTE       EQU 1623     ;粘贴
IDS_DEL         EQU 1624     ;删除
IDS_SELECTALL   EQU 1625     ;全选

;========================================================
;MENUEX_TEMPLATE_ITEM中的wFlags标志
;增加一个MFR_TXTID,作用如下:
;(1)如果wFlags标志无MFR_TXTID,则szText成员为字符串。
;   此时如果szText值为零,则将菜单ID作为字符串资源ID。
;(2)如果wFlags标志有MFR_TXTID,则szText成员为字符串资源ID。
;   此时如果szText值为零,则将菜单ID作为字符串资源ID。
;========================================================
 MFR_END   EQU 80h  ;一个菜单条中的最后一个菜单项。
 MFR_POPUP EQU 01h  ;子菜单标题项。
 MFR_TXTID EQU 100h ;szText成员指示标志

.data
;-----------------
; 扩展菜单模板
;-----------------
 align 4
;---MENUEX_TEMPLATE_HEADER结构---
Menu_Temp1 \
  dw 1          ;扩展菜单模板版本号。此成员必须为 1。
  dw 4          ;首个 MENUEX_TEMPLATE_ITEM 结构的偏移
  dd 100        ;菜单栏的帮助标识符(还不知道有什么用处,随便给一个值)。
;---以下紧跟菜单项(每项一个MENUEX_TEMPLATE_ITEM结构)---
  dd MFT_STRING  ;dwType
  dd MFS_ENABLED ;dwState
  dd IDS_FILE    ;uId
  dw MFR_POPUP or MFR_TXTID ;wFlags
  dw 0           ;szText
  dd 0   ;HELPID (只有MFR_POPUP类有该项)
;--------------
align 4
  dd MFT_STRING
  dd MFS_ENABLED
  dd IDS_NEW
  dw MFR_TXTID
  dw IDS_NEW    ;使用字符串资源ID
;--------------
align 4
  dd MFT_STRING
  dd MFS_ENABLED
  dd IDS_CLOSE
  dw MFR_END or MFR_TXTID    ;该子菜单条的最后一项
  dw 0 ; 默认使用菜单ID作为字符串资源ID
;--------------
align 4
  dd MFT_STRING
  dd MFS_ENABLED
  dd IDS_EDIT
  dw MFR_POPUP or MFR_TXTID
  dw 0
  dd 0   ;HELPID (只有MFR_POPUP类有该项)
;-----------------
align 4
  dd MFT_STRING
  dd MFS_ENABLED
  dd IDS_UNDO
  dw MFR_TXTID
  dw 0
;-----------------
align 4
  dd MFT_STRING
  dd MFS_GRAYED
  dd IDS_COPY
  dw 0           ;不设置MFR_TXTID
  dw 0           ;szText为空,自动以菜单项ID作为字符串资源ID
;-----------------
align 4
  dd MFT_STRING
  dd MFS_ENABLED
  dd IDS_CUT
  dw MFR_TXTID
  dw 0
;-----------------
align 4
  dd MFT_STRING
  dd MFS_ENABLED
  dd IDS_PASTE
  dw MFR_TXTID
  dw 0
;-----------------
align 4
  dd MFT_STRING
  dd MFS_ENABLED
  dd IDS_DEL
  dw MFR_TXTID
  dw 0
;-----------------
align 4
  dd MFT_SEPARATOR  ;分隔线
  dd 0 
  dd 0
  dw 0
  dw 0
;-----------------
align 4
  dd MFT_STRING
  dd MFS_ENABLED
  dd IDS_SELECTALL
  dw MFR_END or MFR_TXTID
  dw 0
;-----------------
align 4
  dd MFT_STRING
  dd MFS_ENABLED
  dd IDS_ABOUT
  dw MFR_END
  WSTR  ,"About" ;这里使用Unicode字符串
;==========================

.code
;====================================================
;获取一个菜单项模板数据---含有自定义标志的扩展模板
;入: pTemp=当前菜单项模板MENUEX_TEMPLATE_ITEM结构地址
;    pInfo=MENUITEMINFO结构地址。用于接收信息
;    pFlag=DWORD变量地址。用于接收wFlags成员
;    pHelpId=DWORD变量地址。用于接收dwHelpId成员
;           =0: 不用
;出: RAX=被处理的结构的字节长度
;    .hSubMenu=子菜单句柄(如果为MFR_POPUP类型)
;====================================================
Menu_GetTempItemInfoEx proc pTemp:QWORD,pInfo:QWORD,\
                          pFlag:QWORD,pHelpId:QWORD
 LOCAL ss_rbx:QWORD
 LOCAL ss_rsi:QWORD
 LOCAL ss_flag:QWORD
 mov ss_rbx,rbx
 mov ss_rsi,rsi
 mov rsi,rcx  ;pTemp
 mov rbx,rdx   ;pInfo
 mov [rbx.MENUITEMINFO].cbSize,SIZEOF MENUITEMINFO
 mov [rbx.MENUITEMINFO].fMask,MIIM_STATE
 lodsd  ;dwType
 mov [rbx.MENUITEMINFO].fType,eax
 lodsd  ;dwState
 mov [rbx.MENUITEMINFO].fState,eax
 lodsd  ;uId
 mov [rbx.MENUITEMINFO].wID,eax
 xor rax,rax
 lodsw  ; wFlags
 mov ss_flag,rax
 mov [r8],eax
 test ax,MFR_POPUP
 jnz ss_sub
 test [rbx.MENUITEMINFO].fType,MFT_SEPARATOR
 jnz ss_sp
 or [rbx.MENUITEMINFO].fMask,MIIM_ID
 jmp ss_str
;---分隔条---
ss_sp:
 add rsi,2  ;移过空串
 mov [rbx.MENUITEMINFO].fMask,MIIM_TYPE
 mov [rbx.MENUITEMINFO].fType,MFT_SEPARATOR
 mov [rbx.MENUITEMINFO].dwTypeData,0
 jmp ss_ok
;---子菜单---
ss_sub:
 or [rbx.MENUITEMINFO].fMask,MIIM_SUBMENU
 call CreatePopupMenu
 mov [rbx.MENUITEMINFO].hSubMenu,rax
;---菜单项目串---
ss_str:
 or [rbx.MENUITEMINFO].fMask,MIIM_STRING
 mov [rbx.MENUITEMINFO].dwTypeData,rsi
 mov rax,2
 test ss_flag,MFR_TXTID
 jnz ss_2
 invoke lstrlenW,rsi
 inc rax
 shl rax,1
ss_2:
 add rsi,rax
 test ss_flag,MFR_POPUP
 jz ss_ok
 lodsd              ;dwHelpId
 mov rdx,pHelpId
 test rdx,rdx
 jz ss_ok
 mov [rdx],eax
ss_ok:
 mov rax,rsi
 sub rax,pTemp
 mov rbx,ss_rbx
 mov rsi,ss_rsi
 ret
Menu_GetTempItemInfoEx endp
;====================================================
;菜单模板项目装载---含有自定义标志的扩展模板
;入: uhMenu=菜单句柄
;    item=新菜单项的插入位置,其意义由zFlag指定。
;    zFlag=FALSE: item为插入处的菜单项目ID
;          TURE: item为插入的菜单项位置(0...)
;    pTemp=首个MENUEX_TEMPLATE_ITEM结构
;    hInst=字符串资源所在的模块实例句柄
;         =0: 为本进程模块
;出: RAX=指向下一个MENUEX_TEMPLATE_ITEM结构(递归使用)
;====================================================
Menu_LoadTemplateItemEx proc uhMenu:QWORD,item:DWORD,\
                    zFlag:DWORD,pTemp:QWORD,hInst:QWORD
 LOCAL ss_mi:MENUITEMINFO
 LOCAL ss_buf[MAX_PATH]:WORD
 LOCAL ss_rsi:QWORD
 LOCAL ss_Flag:DWORD
 mov ss_rsi,rsi ;保护
 mov rsi,pTemp
 cmp hInst,0
 jnz ss_lp1
 invoke GetModuleHandle,NULL
 mov hInst,rax
ss_lp1:
;---4字节对齐---
 add rsi,3
 and rsi,NOT 3
;---取MENUEX_TEMPLATE_ITEM结构数据---
 invoke Menu_GetTempItemInfoEx,rsi,ADDR ss_mi,ADDR ss_Flag,0
 add rsi,rax
;---处理字符串---
 test ss_mi.fMask,MIIM_STRING
 jz ss_add
 mov edx,ss_mi.wID
 mov rax,ss_mi.dwTypeData
 mov ax,[rax]
 test ax,ax
 jz ss_ldstr  ;文本项为空,菜单ID作为字符串资源ID
 mov dx,ax
 test ss_Flag,MFR_TXTID
 jz ss_add
ss_ldstr:
 lea r8,ss_buf
 mov ss_mi.dwTypeData,r8
 invoke LoadStringW,hInst,edx,r8,MAX_PATH
ss_add:
;---添加项目---
 invoke InsertMenuItemW,uhMenu,item,zFlag,ADDR ss_mi
 test eax,eax
 jz ss_out
 cmp zFlag,0
 jz ss_5
 inc item
ss_5:
 test ss_mi.fMask,MIIM_SUBMENU
 jz ss_nt
 invoke Menu_LoadTemplateItemEx,ss_mi.hSubMenu,0,TRUE,rsi,hInst ;递归
 mov rsi,rax
ss_nt:
 test ss_Flag,MFR_END
 jz ss_lp1
ss_out:
 mov rax,rsi   ;递归使用
 mov rsi,ss_rsi
 ret
Menu_LoadTemplateItemEx endp
;================================================
;菜单模板装载---含有自定义标志的扩展模板
;入: pTemp=指向MENUEX_TEMPLATE_HEADER结构
;          即菜单扩展模板。
;    zFlag=标志:
;         MF_POPUP: 装入为快捷菜单
;    hInst=字符串资源所在的模块实例句柄
;         =0: 为本进程模块
;出: RAX=菜单句柄
;================================================
LoadMenuIndirectEx proc pTemp:QWORD,zFlag:DWORD,\
                    hInst:QWORD
 LOCAL ss_hMenu:QWORD
 cmp hInst,0
 jnz ss_be
 invoke GetModuleHandle,NULL
 mov hInst,rax
ss_be:
 test zFlag,MF_POPUP
 jnz ss_popup
 invoke CreateMenu
 jmp ss_2
ss_popup:
 invoke CreatePopupMenu
ss_2:
 mov ss_hMenu,rax
 xor rax,rax
 mov r10,pTemp
 add r10,2
 mov ax,[r10]
 add r10,2
 add r10,rax  ;指向首个MENUEX_TEMPLATE_ITEM结构
 invoke Menu_LoadTemplateItemEx,ss_hMenu,0,TRUE,r10,hInst
 mov rax,ss_hMenu
 ret
LoadMenuIndirectEx endp

3. 调用例

用以下方法装载菜单模板,并关联到窗口hWin:

 invoke LoadMenuIndirectEx,ADDR Menu_Temp1,0,0
 invoke SetMenu,hWin,rax
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值