菜单枚举记
有一MDI应用程序,现假设要枚举出其能够得到系统命令响应所有菜单项的内容:如命令ID,资源名称,所在(子)菜单的句柄等,并对之进行操作。本文对此略述一二,未达意处,望各位海涵之外,以我之深度,唯与诸位共勉而已。
1、 我的迷惑
这几天修炼English Help,悟得些许正果,希望得与世人共超,尚不知是否有效,但为之而已。言归正传:
菜单项可以是菜单,反之亦然,此时菜单被称为子菜单;如果菜单项不是菜单,则它便是’纯菜单项’。以前我以为菜单中每一项都是菜单,我的思维中因此不再有菜单项的定义。可能这是个菜鸟级问题,但也可能您尚不甚解,那您不妨纸上谈兵一番,将最简单的菜单画上一画,再琢磨琢磨。
2、 菜单枚举算法
A、 菜单抽象结构之一(树型抽象):
现在要访问到这棵’树’的所有叶结点并处理之,方法如B所述。
B、 枚举算法(深度遍历,C++语法)
1. )求得当前结点的子结点数c;
2. )循环查询当前结点的I (0<=I<c)子结点信息:
若该子结点I无下级子结点,则子结点I为叶结点,按需处理后,作I=I+1,重复此步骤;
否则子结点I为分枝结点,置下级子结点为当前结点,转向1;
3.)遍历完毕。
int FindMenuItem(HMENU hmenu)//参数hmenu为分支结点资源标识
{
int i,c;
long res;
char* s=new char[33];
MENUITEMINFO mii;
mii.cbSize=sizeof(MENUITEMINFO);
mii.fMask=MIIM_DATA|MIIM_ID|MIIM_SUBMENU|MIIM_TYPE|MIIM_STATE|MIIM_CHECKMARKS;
mii.dwTypeData=s;
c=GetMenuItemCount(hmenu);//获取当前菜单所有的菜单项数目
for(i=0;i<c;i++)
{
mii.cch=32;//注意哦!
res=GetMenuItemInfo(hmenu,i,true,&mii);
if (res==0 ) {delete s;return -1;} //如果获取菜单信息失败
if (mii.hSubMenu==NULL)//
{
//若该菜单项非子菜单,则在此作相应处理
}
else
{
//若该菜单项为子菜单,则如此处理
res=FindMenuItem(mii.hSubMenu);//直接递归调用
if(res==-1){delete s;return -1;}
}
}
delete s;
return 0;
}
int EnumMenuEndItems(HWND hwnd)//开始遍历窗口菜单项。参数hwnd为菜单所在的窗体。
{
HMENU hmenu=GetMenu(hwnd);
if(hmenu==NULL) return –1;
return FindMenuItem(hmenu);
}
注: 此处代码仅仅描述算法。若应用到它处,根据不同的需要,Function的返回值和结点处理代码理所当然要发生改变。
附录2为上述代码的PB文本,两者略有不同,以资参考。
下面对上述代码中用到的Windows api 及 struct稍作解说。
3、 在的所用开发工具中Declare 如下Windows API及相关Struct (标准格式):
HMENU GetMenu( HWND hWnd);//返回值:hWnd所关联的窗口的菜单句柄
HMENU GetSubMenu(HMENU hMenu,int nPos);//返回值:hMenu所关联的菜单中第nPos项的子菜单句柄
int GetMenuItemCount(HMENU hMenu ); //返回值:hMenu所关联的菜单中的菜单项数目
BOOL WINAPI GetMenuItemInfo (
HMENU hMenu,
UINT uItem,
BOOL fByPosition, //该参数确定uItem为当前菜单项的命令标识还是在所属菜单中的排序位置号
LPMENUITEMINFO lpmii);
BOOL DrawMenuBar(HWND hWnd ); //重绘hWnd所关联的窗口的菜单
typedef struct tagMENUITEMINFO {
UINT cbSize; //本结构体物理大小,以byte为单位,其值实际上为48
UINT fMask; //确定想要查询或设置菜单项的哪能些内容
UINT fType; //菜单格式类型
UINT fState;// 菜单状态:Enabled、Disabled or Grayed
UINT wID; //命令标识符
HMENU hSubMenu; //子菜单句柄,若无子菜单则为NULL
HBITMAP hbmpChecked; //根据这两个域值,可以自定义菜单项被选定时的标记,
HBITMAP hbmpUnchecked; //而不一定要是’√’或空白
DWORD dwItemData;//由English翻译为:应用程序定义的与菜单项相关的值(我也不明白)
LPTSTR dwTypeData; //菜单资源内容指针,指向String or BitMap or SEPARATOR资源
UINT cch; //若菜单项为MFT_STRING类型,则此项为dwTypeData长度
HBITMAP hbmpItem;//在调试程序时发现有此一项
} MENUITEMINFO, FAR *LPMENUITEMINFO;
参考资料: MSDN98(MicroSoft) MS SDK Help Files\Win32 Developer’s References(Inprise/Borland)
(附录1为上述API在PB中的Declare文本)
与Menu操作相关的Windows API Functions还比较多,可适度参考,动态库:USER32.DLL。
4、 其它:
a、.我在PB7中调用这些Windows功能,但在调试至GetMenuItemInfo时,单步运行却停滞不前,约
30小时后,我进行了如下操作:
a1、打开一Dos窗口,运行TDump c:\windows\system\user32.dll>user32.txt(Tdump.exe为Inprise公司的应用程序)
a2、回到Windows中,以记事本打开user32.txt,发现其中并无GetMenuItemInfo,但有GetMenuItemInfoA,若在VB中调用过WIN API,则可猜测此为何物。
a3、将GetMenuItemInfo改写(或Alias)成GetMenuItemInfoA,便OK 了!
b、菜单项命令标识符作用
在调用GetMenuItemInfoA之后,其值保存在MENUITEMINFO::wID中。
设现在获得一菜单项信息存储于MENUITEMINFO型结构体mii中,其部分内容为:
mii.cbsize=48
mii.dwTypeData=”打开文件”
mii.wID=10001
mii.fState=Enabled
mii.fType=MFT_STRING
对应函数引用标识 |
命令标识符wID |
OpenFile() |
10001 |
CloseFile() |
10002 |
C、在上述GetMenuItemInfoA调用时,需对MENUITEMINFO的如下域赋值:
MENUITEMINFO::cbSize
MENUITEMINFO::fMask
MENUITEMINFO::dwTypeData
MENUITEMINFO:: cch
其中MENUITEMINFO::fMask可为如下常数值的组合(在相关资料中很难查到这些常数数值)
MIIM_STATE (= 1) MIIM_ID (= 2)
MIIM_SUBMENU (= 4) MIIM_CHECKMARKS (= 8)
MIIM_TYPE (=16) MIIM_DATA (=32)
附:
1、 相关API及结构体在PB中的Declare
Function Long GetMenu(long hWnd ) LIBRARY "USER32.DLL"
Function Long GetSubMenu(long hMenu,long nPos) LIBRARY "USER32.DLL"
Function Long GetMenuItemCount(long hMenu) LIBRARY "USER32.DLL"
Function Long EnableMenuItem(long hMenu,Ulong uIDEnableItem, Ulong uEnable)LIBRARY "USER32.DLL"
Function Long GetMenuItemInfoA(long hMenu, Ulong uItem, long fByPosition,ref MENUITEMINFO mii)LIBRARY "USER32.DLL"
Function Long DrawMenuBar(long hwnd) LIBRARY "USER32.DLL"
global type menuiteminfo from structure
unsignedlong cbsize
unsignedlong fmask
unsignedlong ftype
unsignedlong fstate
unsignedlong wid
long hsubmenu
long hbmpchecked
long hbmpunchecked
long dwitemdata
string dwtypedata
unsignedlong cch
long hbmpitem
end type
2、PB代码,根据需要,不同于C++代码(节选,有修改)
Public Function Int EnumMenuEndItems (long hwnd,int ins_upd)
long hm
constant int ins=0
if hwnd=0 or isnull(hwnd) then return 0
hm=GetMenu(handle(wmdi))
if ins_upd=ins then
delete from sec_fun using trans;
IF TRANS.SQLCODE=-1 THEN goto error
end if
if ismenu(hm)=0 then return -1
if fun(hm,ins_upd) =-1 then goto error
IF TRANS.SQLCODE=-1 THEN goto error
COMMIT USING TRANS;
delete from sec_auth where id_fun not in (select id_fun from sec_fun) using trans;//对
if trans.sqlcode=-1 and ins_upd=ins then messagebox("","某些操作可能不再存在,但用户仍然拥有该项操作权限,请手动删除!")
return 0
error:
ROLLBACK USING TRANS;
RETURN –1
End Fuction
Private Function Int FindMenuItem (HMENU hmenu,int ins_upd )
//ins=0
//upd=1
int i,c
long hm,res
menuiteminfo mii
mii.cbsize=48
mii.fmask=54
MII.FTYPE=0
c=getmenuitemcount(hmenu)
for i=0 to c -1
mii.dwtypedata=" "
mii.cch=63
if not isvalid(mii) then return -1
res=GetMenuItemInfoA(hmenu,i,255,mii)
if res=0 then continue
IF MII.HSUBMENU=0 THEN
IF NOT ISNULL(MII.DWTYPEDATA) THEN
choose case ins_upd
case 0
insert into sec_fun(id_fun,des_fun,HANDLE) values (:mii.wid,:mii.dwTypeData,:hmenu) USING TRANS;//保存’纯’菜单项的信息
IF TRANS.SQLCODE=-1 THEN return -1
case 1
update sec_fun set HANDLE=:hmenu where id_fun=:mii.wid USING TRANS;
IF TRANS.SQLCODE=-1 THEN return -2
end choose
ELSE
CONTINUE
END IF
ELSE
res= FindMenuItem (MII.HSUBMENU,ins_upd)
if res<>0 then return res
END IF
NEXT
return 0
End Function