菜單枚舉記
2024-07-21 02:08:56
供稿:網友
菜單枚舉記
有一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 ystem/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
wid=10001
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