VC++利用钩子实现菜单阴影效果_第1页
VC++利用钩子实现菜单阴影效果_第2页
VC++利用钩子实现菜单阴影效果_第3页
VC++利用钩子实现菜单阴影效果_第4页
VC++利用钩子实现菜单阴影效果_第5页
已阅读5页,还剩4页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

利用钩子实现菜单阴影效果程序运行效果截图:也许有很多人曾和我一样,对OfficeXP里面地菜单地阴影效果羡慕不已,它不需要在WindowsXP中就可以在菜单后面显示阴影,当然在WindowsXP中,已经完全支持菜单阴影了•虽然我们不一定很有必要自己来实现这个较难实现地效果•但是正如有很多人想实现那种IE风格地菜单栏一样,尽管它们并不能为我们带来更多实用地功能,却可以使我们地程序看起来与众不同•:〉菜单也是一个窗口,假如我们能得到它地窗口地句柄,要实现像添加阴影这样地效果,就不会很难了.可惜我们根本找不到这个窗口是在哪里被创建地,也没办法很容易地取得它地窗口句柄,甚至几乎难以相信它是一个窗口,因为我实在找不到它地窗口句柄啊•经过对许多别人已经做好地类地源代码地"研究",我终于找到了一个方法•那就是万能地钩子,如果说在Windows里面抓"人",连钩子也办不到地话,那我就不知道该用什么方法实现了,呵呵.下面我就一起来看看如何抓到这些"可恶"地家伙吧.为了便于移植,我们就写一个专用地类吧,就取名为CMenuWndHook.添加两个静态成员先:staticCMapm_WndMenuMap。staticHHOOKm_hMenuHook。被我们抓到地这些家伙肯定不止一个,我们需要一个映射模板类来保存它们地句柄和对应地CMenuWndHook类对象地指针.m_hMenuHook则为我们将要创建地钩子地钩子句柄.再在CPP文件中初始化它们:CMapCMenuWndHook::m_WndMenuMap。HHOOKCMenuWndHook::m_hMenuHook=NULL。下面再添加两个函数来做安装与卸载hook之用,它们都是静态函数:voidCMenuWndHook::InstallHook(>{if(m_hMenuHook==NULL>{m_hMenuHook=::SetWindowsHookEx(WH_CALLWNDPROC,WindowHook,AfxGetApp(>->m_hInstance,::GetCurrentThreadId(>>。Windows之下一般用上面地SetWindowsHookExAPI函数来安装HOOK,它地函数原型如下:HHOOKSetWindowsHookEx(intidHook,//钩子地类型,即它处理地消息类型HOOKPROClpfn,//子函数地入口地址,当钩子钩到任何消息后先调用这个函数.//(如果dwThreadId参数为0,或是一个由别地进程创建地线程地标识,//lpfn必须指向DLL中地钩子子程.除此以外,lpfn可以指向当前进//程地一段钩子子程代码>HINSTANCEhMod,//应用程序实例地句柄.标识包含lpfn所指地子程地DLL.//如果dwThreadId标识当前进程创建地一个线程,//而且子程代码位于当前进程,hMod必须为NULL.//可以很简单地设定其为本应用程序地实例句柄.DWORDdwThreadId//与安装地钩子子程相关联地线程地标识符.//如果为0,钩子子程与所有地线程关联,即为全局钩子.//但这时,你钩子只能是放在DLL中.>。函数成功则返回钩子子程地句柄,失败返回NULL.我们用到地是WH_CALLWNDPROC类型地钩子,它使你可以监视发送到窗口过程地消息,系统在消息发送到接收窗口过程之前会调用你指定地WH_CALLWNDPROCHook子程,这样你就可以等它们自投罗网,然后就可以对它们为所欲为了.卸载钩子就简单多了,只需要调用UnhookWindowsHookEx即可,当然,我们还需要额外做一点清理工作: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>。}}在介绍如何安装钩子时,提到要一个钩子子程,这个子程必须按下面地格式声明,否则不能使用:LRESULTCALLBACKWindowHook(intcode,WPARAMwParam,LPARAMlParam>。函数名随意,同样把它声明为静态函数,下面各位注意了,我们地逮捕行动就是在这个函数中展开地:LRESULTCALLBACKCMenuWndHook::WindowHook(intcode,WPARAMwParam,LPARAMlParam>{//如果你安装地是WH_CALLWNDPROC类型地钩子地话,系统就会传递一个这个家伙地指针:CWPSTRUCT*pStruct=(CWPSTRUCT*>lParam。while(code==HC_ACTION>{HWNDhWnd=pStruct->hwnd。//截获WM_CREATE消息,为了保证不抓错"人",我们必须严格确定这是否是我们要抓地家伙,//这样我们就可以在它们刚出头就把它们逮住:if(pStruct->message!=WM_CREATE&&pStruct->message!=0x01E2>{break。}//是否为菜单类 TCHARstrClassName[10]。intCount=::GetClassName(hWnd,strClassName,sizeof(strClassName>/sizeof(strClassName[0]>>。//再次确认它地身份(菜单窗口类地类名为"#32768",且为6个字符长>:if(Count!=6||_tcscmp(strClassName,_T("#32768">>!=0>{//对不起,认错人了,pass:-〉break。}//是否已经被子类化 //我们抓到一个之后,会给它用SetProp挂个牌(后面会介绍〉if(::GetProp(pStruct->hwnd,CoolMenu_oldProc>!=NULL>{//已经在编?pass.break。}//抓到一个,给它登记注册(这个函数我会在后面介绍>,而且不能登记失败,:>VERIFY(AddWndHook(pStruct->hwnd>!=NULL>。//下面该叫它去洗心革面了 //取得原来地窗口过程 WNDPROColdWndProc=(WNDPROC>(long>::GetWindowLong(pStruct->hwnd,GWL_WNDPROC>。if(oldWndProc==NULL>{break。}ASSERT(oldWndProc!=CoolMenuProc>。//这个过程一样不能出错//保存到窗口地属性中 //哈哈,给它打个记号吧(SetPropAPI函数是用来给一个窗口加上一个属性地,//RemoveProp则是删除一个属性,GetProp是取得一个属性地值〉//CoolMenu_oldProc为一字符数组,我在CPP文件地开头声明了它,表示你要//添加地属性名:constTCHARCoolMenu_oldProc[]=_T("CoolMenu_oldProc">。//这里保存地是它地原来地窗口过程,这种该随身带地东西还是让它自己拿着比较好if(!SetProp(pStruct->hwnd,CoolMenu_oldProc,oldWndProc>>{break。}//子类化 //这个不用我说了吧,这里我们用了偷梁换柱地方法,呵呵,这可是子类化地惯技了:if(!SetWindowLong(pStruct->hwnd,GWL_WNDPROC,(DWORD>(ULONG>CoolMenuProc>>{//没有成功!!唉,就放过他吧,虽然忙了半天了,不过这种情况我想是不可能发生地!::RemoveProp(pStruct->hwnd,CoolMenu_oldProc>。break。}}//这句可是绝对不能少地,叫那些闲杂人等该干什么就干什么去,不要?//嘿嘿,看你地程序怎么死吧!returnCallNextHookEx(m_hMenuHook,code,wParam,lParam>。}我们再来看看,怎么"登记"它们:CMenuWndHook*CMenuWndHook::AddWndHook(HWNDhwnd>{CMenuWndHook*pWnd=NULL。if(m_WndMenuMap.Lookup(hwnd,pWnd>>{//有这个人了,不用再登记了.returnpWnd。}//给它分配个房间(牢房!嘿嘿>pWnd=newCMenuWndHook(hwnd>。if(pWnd!=NULL>{m_WndMenuMap.SetAt(hwnd,pWnd>。}returnpWnd。}//另外还可有一个对应地查找函数:CMenuWndHook*CMenuWndHook::GetWndHook(HWNDhwnd>{CMenuWndHook*pWnd=NULL。if(m_WndMenuMap.Lookup(hwnd,pWnd>>{returnpWnd。}returnNULL。}上面地函数和变量大部分都是静态成员,因为hook系统只要有一套就可以了到这里为止,坚巨地任务已经完成了一半,做下面地事,就得心应手多了.下面是窗口地新过程,依然为一个静态地函数.LRESULTCALLBACKCMenuWndHook::CoolMenuProc(HWNDhWnd,UINTuMsg,WPARAMwParam,LPARAMlParam>{WNDPROColdWndProc=(WNDPROC>::GetProp(hWnd,CoolMenu_oldProc>。CMenuWndHook*pWnd=NULL。switch(uMsg>{//计算非客户区地大小 caseWM_NCCALCSIZE:{LRESULTlResult=CallWindowProc(oldWndProc,hWnd,uMsg,wParam,lParam>。if((pWnd=GetWndHook(hWnd>>!=NULL>{pWnd->OnNcCalcsize((NCCALCSIZE_PARAMS*>lParam>。}returnlResult。}break。//当窗口地位置将要发生改变,在这里它一般发生在菜单被弹出之前,//给你最后一次机会设置它地位置.caseWM_WINDOWPOSCHANGING:{if((pWnd=GetWndHook(hWnd>>!=NULL>{pWnd->OnWindowPosChanging((LPWINDOWPOS>lParam>。}}break。//为什么要响应这个消息呢?我也不知道啊,我只知道,当菜单是以动画地方式弹出地时候//系统是通过发送这个消息来绘制菜单地,wParam是对应地设备上下文句柄,不过我也不知//道它到底是属于谁地.caseWM_PRINT:{LRESULTlResult=CallWindowProc(oldWndProc,hWnd,uMsg,wParam,lParam>。if((pWnd=GetWndHook(hWnd>>!=NULL>{pWnd->OnPrint(CDC::FromHandle((HDC>wParam>>。}returnlResult。}break。//这个就不同说了吧.caseWM_NCPAINT:{if((pWnd=GetWndHook(hWnd>>!=NULL>{pWnd->OnNcPaint(>。return0。}}break。//菜单窗口被隐藏地时候,我也不知道这种情况会不会发生,:(,主要是看到人家这样处理了.caseWM_SHOWWINDOW:{if((pWnd=GetWndHook(hWnd>>!=NULL>{pWnd->OnShowWindow(wParam!=NULL>。}}break。//菜单窗口被销毁地时候caseWM_NCDESTROY:{if((pWnd=GetWndHook(hWnd>>!=NULL>{pWnd->OnNcDestroy(>。}}break。}returnCallWindowProc(oldWndProc,hWnd,uMsg,wParam,lParam>。}下面就看如何慢慢实现这些消息地响应函数吧:voidCMenuWndHook::OnWindowPosChanging(WINDOWPOS*pWindowPos>{if(!IsShadowEnabled(>>{//加一块区域来显示阴影 pWindowPos->cx+=4。pWindowPos->cy+=4。}//为了绘制阴影,我们须要先保存这个区域地图像,以便绘制半透明地阴影.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(>>{//留出一点区域来显示阴影 lpncsp->rgrc[0].right-=4。lpncsp->rgrc[0].bottom-=4。}}上面我用到了两个全局函数,其中IsShadowEnabled是检测系统是否开启了菜单阴影(主要针对于WindowsXP,Windows2003及他更高地版本>如果系统已经给我们开启了阴影,我们还忙乎什么哦.BOOLWINAPIIsShadowEnabled(〉{BOOLbEnabled=FALSE。if(SystemParametersInfo(SPI_GETDROPSHADOW,0,bEnabled,0>>{returnbEnabled。}returnFALSE。}其中SPI_GETDROPSHADOW在VC6里面没有被声明,你需要自已声明它:#ifndefSPI_GETDROPSHADOW#defineSPI_GETDROPSHADOW0x1024#endif另外还有GetScreenBitmap函数用于截取屏幕上指定区域内地图像: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。}下面这两个函数要做地事就差不多了: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]>。}在指定地矩形区域内绘制阴影地全局函数(当然这些函数不一定都要做成全局函数,我把它们写成了全局函数是因为在好几个类中都用到了它们,写成全局函数便于调用>也许你会觉得这不符合面向对象编程地思想,其实面向过程地编程思想,并不一定就比面向对象地思想落后,我把这些比较独立地函数写成全局函数,当作API函数用,还是觉得很方便地,如果硬要将它们塞到一个类里面,反而觉得很郁闷.:->.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. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论