VC++利用鉤子實(shí)現(xiàn)菜單陰影效果_第1頁(yè)
VC++利用鉤子實(shí)現(xiàn)菜單陰影效果_第2頁(yè)
VC++利用鉤子實(shí)現(xiàn)菜單陰影效果_第3頁(yè)
VC++利用鉤子實(shí)現(xiàn)菜單陰影效果_第4頁(yè)
VC++利用鉤子實(shí)現(xiàn)菜單陰影效果_第5頁(yè)
已閱讀5頁(yè),還剩4頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶(hù)提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

利用鉤子實(shí)現(xiàn)菜單陰影效果程序運(yùn)行效果截圖:也許有很多人曾和我一樣,對(duì)OfficeXP里面地菜單地陰影效果羨慕不已,它不需要在WindowsXP中就可以在菜單后面顯示陰影,當(dāng)然在WindowsXP中,已經(jīng)完全支持菜單陰影了?雖然我們不一定很有必要自己來(lái)實(shí)現(xiàn)這個(gè)較難實(shí)現(xiàn)地效果?但是正如有很多人想實(shí)現(xiàn)那種IE風(fēng)格地菜單欄一樣,盡管它們并不能為我們帶來(lái)更多實(shí)用地功能,卻可以使我們地程序看起來(lái)與眾不同?:〉菜單也是一個(gè)窗口,假如我們能得到它地窗口地句柄,要實(shí)現(xiàn)像添加陰影這樣地效果,就不會(huì)很難了.可惜我們根本找不到這個(gè)窗口是在哪里被創(chuàng)建地,也沒(méi)辦法很容易地取得它地窗口句柄,甚至幾乎難以相信它是一個(gè)窗口,因?yàn)槲覍?shí)在找不到它地窗口句柄啊?經(jīng)過(guò)對(duì)許多別人已經(jīng)做好地類(lèi)地源代碼地"研究",我終于找到了一個(gè)方法?那就是萬(wàn)能地鉤子,如果說(shuō)在Windows里面抓"人",連鉤子也辦不到地話(huà),那我就不知道該用什么方法實(shí)現(xiàn)了,呵呵.下面我就一起來(lái)看看如何抓到這些"可惡"地家伙吧.為了便于移植,我們就寫(xiě)一個(gè)專(zhuān)用地類(lèi)吧,就取名為CMenuWndHook.添加兩個(gè)靜態(tài)成員先:staticCMapm_WndMenuMap。staticHHOOKm_hMenuHook。被我們抓到地這些家伙肯定不止一個(gè),我們需要一個(gè)映射模板類(lèi)來(lái)保存它們地句柄和對(duì)應(yīng)地CMenuWndHook類(lèi)對(duì)象地指針.m_hMenuHook則為我們將要?jiǎng)?chuàng)建地鉤子地鉤子句柄.再在CPP文件中初始化它們:CMapCMenuWndHook::m_WndMenuMap。HHOOKCMenuWndHook::m_hMenuHook=NULL。下面再添加兩個(gè)函數(shù)來(lái)做安裝與卸載hook之用,它們都是靜態(tài)函數(shù):voidCMenuWndHook::InstallHook(>{if(m_hMenuHook==NULL>{m_hMenuHook=::SetWindowsHookEx(WH_CALLWNDPROC,WindowHook,AfxGetApp(>->m_hInstance,::GetCurrentThreadId(>>。Windows之下一般用上面地SetWindowsHookExAPI函數(shù)來(lái)安裝HOOK,它地函數(shù)原型如下:HHOOKSetWindowsHookEx(intidHook,//鉤子地類(lèi)型,即它處理地消息類(lèi)型HOOKPROClpfn,//子函數(shù)地入口地址,當(dāng)鉤子鉤到任何消息后先調(diào)用這個(gè)函數(shù).//(如果dwThreadId參數(shù)為0,或是一個(gè)由別地進(jìn)程創(chuàng)建地線(xiàn)程地標(biāo)識(shí),//lpfn必須指向DLL中地鉤子子程.除此以外,lpfn可以指向當(dāng)前進(jìn)//程地一段鉤子子程代碼>HINSTANCEhMod,//應(yīng)用程序?qū)嵗鼐浔?標(biāo)識(shí)包含lpfn所指地子程地DLL.//如果dwThreadId標(biāo)識(shí)當(dāng)前進(jìn)程創(chuàng)建地一個(gè)線(xiàn)程,//而且子程代碼位于當(dāng)前進(jìn)程,hMod必須為NULL.//可以很簡(jiǎn)單地設(shè)定其為本應(yīng)用程序地實(shí)例句柄.DWORDdwThreadId//與安裝地鉤子子程相關(guān)聯(lián)地線(xiàn)程地標(biāo)識(shí)符.//如果為0,鉤子子程與所有地線(xiàn)程關(guān)聯(lián),即為全局鉤子.//但這時(shí),你鉤子只能是放在DLL中.>。函數(shù)成功則返回鉤子子程地句柄,失敗返回NULL.我們用到地是WH_CALLWNDPROC類(lèi)型地鉤子,它使你可以監(jiān)視發(fā)送到窗口過(guò)程地消息,系統(tǒng)在消息發(fā)送到接收窗口過(guò)程之前會(huì)調(diào)用你指定地WH_CALLWNDPROCHook子程,這樣你就可以等它們自投羅網(wǎng),然后就可以對(duì)它們?yōu)樗麨榱?卸載鉤子就簡(jiǎn)單多了,只需要調(diào)用UnhookWindowsHookEx即可,當(dāng)然,我們還需要額外做一點(diǎn)清理工作:voidCMenuWndHook::UnInstallHook(>{POSITIONpos=m_WndMenuMap.GetStartPosition(>。while(pos!=NULL>{HWNDhwnd。CMenuWndHook*pMenuWndHook。m_WndMenuMap.GetNextAssoc(pos,hwnd,pMenuWndHook>。deletepMenuWndHook。pMenuWndHook=NULL。}m_WndMenuMap.RemoveAll(>。if(m_hMenuHook!=NULL>{::UnhookWindowsHookEx(m_hMenuHook>。}}在介紹如何安裝鉤子時(shí),提到要一個(gè)鉤子子程,這個(gè)子程必須按下面地格式聲明,否則不能使用:LRESULTCALLBACKWindowHook(intcode,WPARAMwParam,LPARAMlParam>。函數(shù)名隨意,同樣把它聲明為靜態(tài)函數(shù),下面各位注意了,我們地逮捕行動(dòng)就是在這個(gè)函數(shù)中展開(kāi)地:LRESULTCALLBACKCMenuWndHook::WindowHook(intcode,WPARAMwParam,LPARAMlParam>{//如果你安裝地是WH_CALLWNDPROC類(lèi)型地鉤子地話(huà),系統(tǒng)就會(huì)傳遞一個(gè)這個(gè)家伙地指針:CWPSTRUCT*pStruct=(CWPSTRUCT*>lParam。while(code==HC_ACTION>{HWNDhWnd=pStruct->hwnd。//截獲WM_CREATE消息,為了保證不抓錯(cuò)"人",我們必須嚴(yán)格確定這是否是我們要抓地家伙,//這樣我們就可以在它們剛出頭就把它們逮?。篿f(pStruct->message!=WM_CREATE&&pStruct->message!=0x01E2>{break。}//是否為菜單類(lèi) TCHARstrClassName[10]。intCount=::GetClassName(hWnd,strClassName,sizeof(strClassName>/sizeof(strClassName[0]>>。//再次確認(rèn)它地身份(菜單窗口類(lèi)地類(lèi)名為"#32768",且為6個(gè)字符長(zhǎng)>:if(Count!=6||_tcscmp(strClassName,_T("#32768">>!=0>{//對(duì)不起,認(rèn)錯(cuò)人了,pass:-〉break。}//是否已經(jīng)被子類(lèi)化 //我們抓到一個(gè)之后,會(huì)給它用SetProp掛個(gè)牌(后面會(huì)介紹〉if(::GetProp(pStruct->hwnd,CoolMenu_oldProc>!=NULL>{//已經(jīng)在編?pass.break。}//抓到一個(gè),給它登記注冊(cè)(這個(gè)函數(shù)我會(huì)在后面介紹>,而且不能登記失敗,:>VERIFY(AddWndHook(pStruct->hwnd>!=NULL>。//下面該叫它去洗心革面了 //取得原來(lái)地窗口過(guò)程 WNDPROColdWndProc=(WNDPROC>(long>::GetWindowLong(pStruct->hwnd,GWL_WNDPROC>。if(oldWndProc==NULL>{break。}ASSERT(oldWndProc!=CoolMenuProc>。//這個(gè)過(guò)程一樣不能出錯(cuò)//保存到窗口地屬性中 //哈哈,給它打個(gè)記號(hào)吧(SetPropAPI函數(shù)是用來(lái)給一個(gè)窗口加上一個(gè)屬性地,//RemoveProp則是刪除一個(gè)屬性,GetProp是取得一個(gè)屬性地值〉//CoolMenu_oldProc為一字符數(shù)組,我在CPP文件地開(kāi)頭聲明了它,表示你要//添加地屬性名:constTCHARCoolMenu_oldProc[]=_T("CoolMenu_oldProc">。//這里保存地是它地原來(lái)地窗口過(guò)程,這種該隨身帶地東西還是讓它自己拿著比較好if(!SetProp(pStruct->hwnd,CoolMenu_oldProc,oldWndProc>>{break。}//子類(lèi)化 //這個(gè)不用我說(shuō)了吧,這里我們用了偷梁換柱地方法,呵呵,這可是子類(lèi)化地慣技了:if(!SetWindowLong(pStruct->hwnd,GWL_WNDPROC,(DWORD>(ULONG>CoolMenuProc>>{//沒(méi)有成功!!唉,就放過(guò)他吧,雖然忙了半天了,不過(guò)這種情況我想是不可能發(fā)生地!::RemoveProp(pStruct->hwnd,CoolMenu_oldProc>。break。}}//這句可是絕對(duì)不能少地,叫那些閑雜人等該干什么就干什么去,不要?//嘿嘿,看你地程序怎么死吧!returnCallNextHookEx(m_hMenuHook,code,wParam,lParam>。}我們?cè)賮?lái)看看,怎么"登記"它們:CMenuWndHook*CMenuWndHook::AddWndHook(HWNDhwnd>{CMenuWndHook*pWnd=NULL。if(m_WndMenuMap.Lookup(hwnd,pWnd>>{//有這個(gè)人了,不用再登記了.returnpWnd。}//給它分配個(gè)房間(牢房!嘿嘿>pWnd=newCMenuWndHook(hwnd>。if(pWnd!=NULL>{m_WndMenuMap.SetAt(hwnd,pWnd>。}returnpWnd。}//另外還可有一個(gè)對(duì)應(yīng)地查找函數(shù):CMenuWndHook*CMenuWndHook::GetWndHook(HWNDhwnd>{CMenuWndHook*pWnd=NULL。if(m_WndMenuMap.Lookup(hwnd,pWnd>>{returnpWnd。}returnNULL。}上面地函數(shù)和變量大部分都是靜態(tài)成員,因?yàn)閔ook系統(tǒng)只要有一套就可以了到這里為止,堅(jiān)巨地任務(wù)已經(jīng)完成了一半,做下面地事,就得心應(yīng)手多了.下面是窗口地新過(guò)程,依然為一個(gè)靜態(tài)地函數(shù).LRESULTCALLBACKCMenuWndHook::CoolMenuProc(HWNDhWnd,UINTuMsg,WPARAMwParam,LPARAMlParam>{WNDPROColdWndProc=(WNDPROC>::GetProp(hWnd,CoolMenu_oldProc>。CMenuWndHook*pWnd=NULL。switch(uMsg>{//計(jì)算非客戶(hù)區(qū)地大小 caseWM_NCCALCSIZE:{LRESULTlResult=CallWindowProc(oldWndProc,hWnd,uMsg,wParam,lParam>。if((pWnd=GetWndHook(hWnd>>!=NULL>{pWnd->OnNcCalcsize((NCCALCSIZE_PARAMS*>lParam>。}returnlResult。}break。//當(dāng)窗口地位置將要發(fā)生改變,在這里它一般發(fā)生在菜單被彈出之前,//給你最后一次機(jī)會(huì)設(shè)置它地位置.caseWM_WINDOWPOSCHANGING:{if((pWnd=GetWndHook(hWnd>>!=NULL>{pWnd->OnWindowPosChanging((LPWINDOWPOS>lParam>。}}break。//為什么要響應(yīng)這個(gè)消息呢?我也不知道啊,我只知道,當(dāng)菜單是以動(dòng)畫(huà)地方式彈出地時(shí)候//系統(tǒng)是通過(guò)發(fā)送這個(gè)消息來(lái)繪制菜單地,wParam是對(duì)應(yīng)地設(shè)備上下文句柄,不過(guò)我也不知//道它到底是屬于誰(shuí)地.caseWM_PRINT:{LRESULTlResult=CallWindowProc(oldWndProc,hWnd,uMsg,wParam,lParam>。if((pWnd=GetWndHook(hWnd>>!=NULL>{pWnd->OnPrint(CDC::FromHandle((HDC>wParam>>。}returnlResult。}break。//這個(gè)就不同說(shuō)了吧.caseWM_NCPAINT:{if((pWnd=GetWndHook(hWnd>>!=NULL>{pWnd->OnNcPaint(>。return0。}}break。//菜單窗口被隱藏地時(shí)候,我也不知道這種情況會(huì)不會(huì)發(fā)生,:(,主要是看到人家這樣處理了.caseWM_SHOWWINDOW:{if((pWnd=GetWndHook(hWnd>>!=NULL>{pWnd->OnShowWindow(wParam!=NULL>。}}break。//菜單窗口被銷(xiāo)毀地時(shí)候caseWM_NCDESTROY:{if((pWnd=GetWndHook(hWnd>>!=NULL>{pWnd->OnNcDestroy(>。}}break。}returnCallWindowProc(oldWndProc,hWnd,uMsg,wParam,lParam>。}下面就看如何慢慢實(shí)現(xiàn)這些消息地響應(yīng)函數(shù)吧:voidCMenuWndHook::OnWindowPosChanging(WINDOWPOS*pWindowPos>{if(!IsShadowEnabled(>>{//加一塊區(qū)域來(lái)顯示陰影 pWindowPos->cx+=4。pWindowPos->cy+=4。}//為了繪制陰影,我們須要先保存這個(gè)區(qū)域地圖像,以便繪制半透明地陰影.if(!IsWindowVisible(m_hWnd>&&!IsShadowEnabled(>>{if(m_bmpBack.m_hObject!=NULL>{m_bmpBack.DeleteObject(>。}m_bmpBack.Attach(GetScreenBitmap(CRect(pWindowPos->x,pWindowPos->y,pWindowPos->cx,pWindowPos->cy>>>。}}voidCMenuWndHook::OnNcCalcsize(NCCALCSIZE_PARAMS*lpncsp>{if(!IsShadowEnabled(>>{//留出一點(diǎn)區(qū)域來(lái)顯示陰影 lpncsp->rgrc[0].right-=4。lpncsp->rgrc[0].bottom-=4。}}上面我用到了兩個(gè)全局函數(shù),其中IsShadowEnabled是檢測(cè)系統(tǒng)是否開(kāi)啟了菜單陰影(主要針對(duì)于WindowsXP,Windows2003及他更高地版本>如果系統(tǒng)已經(jīng)給我們開(kāi)啟了陰影,我們還忙乎什么哦.BOOLWINAPIIsShadowEnabled(〉{BOOLbEnabled=FALSE。if(SystemParametersInfo(SPI_GETDROPSHADOW,0,bEnabled,0>>{returnbEnabled。}returnFALSE。}其中SPI_GETDROPSHADOW在VC6里面沒(méi)有被聲明,你需要自已聲明它:#ifndefSPI_GETDROPSHADOW#defineSPI_GETDROPSHADOW0x1024#endif另外還有GetScreenBitmap函數(shù)用于截取屏幕上指定區(qū)域內(nèi)地圖像:HBITMAPWINAPIGetScreenBitmap(LPCRECTpRect>{HDC hDC。HDC hMemDC。HBITMAPhNewBitmap=NULL。if((hDC=::GetDC(NULL>>!=NULL>{if((hMemDC=::CreateCompatibleDC(hDC>>!=NULL>{if((hNewBitmap=::CreateCompatibleBitmap(hDC,pRect->right-pRect->left,pRect->bottom-pRect->top>>!=NULL>{HBITMAPhOldBitmap=(HBITMAP>::SelectObject(hMemDC,hNewBitmap>。::BitBlt(hMemDC,0,0,pRect->right-pRect->left,pRect->bottom-pRect->top,hDC,pRect->left,pRect->top,SRCCOPY>。::SelectObject(hMemDC,(HGDIOBJ>hOldBitmap>。}::DeleteDC(hMemDC>。}::ReleaseDC(NULL,hDC>。}returnhNewBitmap。}下面這兩個(gè)函數(shù)要做地事就差不多了:voidCMenuWndHook::OnNcPaint(>{CWindowDCdc(CWnd::FromHandle(m_hWnd>>。OnPrint(&dc>。}voidCMenuWndHook::OnPrint(CDC*pDC>{CRectrc。GetWindowRect(m_hWnd,&rc>。rc.OffsetRect(-rc.TopLeft(>>。//繪制陰影if(!IsShadowEnabled(>>{CDCcMemDC。cMemDC.CreateCompatibleDC(pDC>。HGDIOBJhOldBitmap=::SelectObject(cMemDC.m_hDC,m_bmpBack>。pDC->BitBlt(0,rc.bottom-4,rc.Width(>-4,4,&cMemDC,0,rc.bottom-4,SRCCOPY>。pDC->BitBlt(rc.right-4,0,4,rc.Height(>,&cMemDC,rc.right-4,0,SRCCOPY>。DrawShadow(pDC,rc>。rc.right-=4。rc.bottom-=4。}//繪制邊框pDC->Draw3dRect(rc,m_crFrame[0],m_crFrame[1]>。rc.DeflateRect(1,1>。pDC->Draw3dRect(rc,m_crFrame[2],m_crFrame[3]>。}在指定地矩形區(qū)域內(nèi)繪制陰影地全局函數(shù)(當(dāng)然這些函數(shù)不一定都要做成全局函數(shù),我把它們寫(xiě)成了全局函數(shù)是因?yàn)樵诤脦讉€(gè)類(lèi)中都用到了它們,寫(xiě)成全局函數(shù)便于調(diào)用>也許你會(huì)覺(jué)得這不符合面向?qū)ο缶幊痰厮枷?其實(shí)面向過(guò)程地編程思想,并不一定就比面向?qū)ο蟮厮枷肼浜?我把這些比較獨(dú)立地函數(shù)寫(xiě)成全局函數(shù),當(dāng)作API函數(shù)用,還是覺(jué)得很方便地,如果硬要將它們?nèi)揭粋€(gè)類(lèi)里面,反而覺(jué)得很郁悶.:->.voidDrawShadow(CDC*pDC,CRectrect>。voidDrawShadow(CDC*pDC,CRectrect>{COLORREFoldcolor=RGB(255,255,255>。BYTEnewValR,newValG,newValB。BYTEAlphaArray[]={140,170,212,240}。BYTEAlphaArray2[]={170,205,220,240,240,250,255}。//底部地陰影 inti,j。for(j=0。j<4。j++>{for(i=6。i<=rect.right-5。i++>{oldcolor=pDC->GetPixel(i,rect.bottom-(4-j>>。newValR=GetRValue(oldcolor>*AlphaArray[j]/255。newValG=GetGValue(oldcolor>*AlphaArray[j]/255。newValB=GetBValue(oldcolor>*AlphaArray[j]/255。pDC->SetPixel(i,rect.bottom-(4-j>,RGB(newValR,newValG,newValB>>。}}//右邊地陰影 for(i=0。i<4。i++>{for(j=6。j<=rect.bottom-5。j++>{oldcolor=pDC->GetPixel(rect.right-(4-i>,j>。newValR=GetRValue(oldcolor>*AlphaArray[i]/255。newValG=GetGValue(oldcolor>*AlphaArray[i]/255。newValB=GetBValue(oldcolor>*AlphaArray[i]/255。pDC->SetPixel(rect.right-(4-i>,j,RGB(newValR,newValG,newValB>>。}}//角上地陰影 for(i=0。i<4。i++>{for(j=0。j<4。j++>{if((i+j>>6>break。oldcolor=pDC->GetPixel(rect.right-4+i,rect.bottom-4+j>。newValR=GetRValue(oldcolor>*AlphaArray2[i+j]/255。newValG=GetGValue(oldcolor>*AlphaArray2[i+j]/255。newValB=GetBValue(oldcolor>*AlphaArray2[i+j]/255。pDC->SetPixel(rect.right-4+i,rect.bottom-4+j,RGB(newValR,newValG,newValB>>。oldcolor=pDC->GetPixel(rect.right-4+i,rect.top+5-j>。newValR=GetRValue(oldcolor>*AlphaArray2[i+j]/255。newValG=GetGValue(oldcolor>*AlphaArray2[i+j]/255。newValB=GetBValue(oldcolor>*AlphaArray2[i+j]/255。pDC->SetPixel(rect.right-4+i,rect.top+5-j,RGB(newValR,newValG,newValB>>。oldcolor=pDC->GetPixel(rect.left-i+5,rect.bottom-4+j>。newValR=GetRValue(oldcolor>*AlphaArray2[i+j]/255。newValG=GetGValue(oldcolor>*AlphaArray2[i+j]/255。newValB=GetBValue(oldcolor>*AlphaArray2[i+j]/255。pDC->SetPixel(rect.left-i+5,rect.bottom-4+j,RGB(newValR,newValG,newVal

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶(hù)所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶(hù)上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶(hù)上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶(hù)因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論