新书推介:《语义网技术体系》
作者:瞿裕忠,胡伟,程龚
   XML论坛     W3CHINA.ORG讨论区     计算机科学论坛     SOAChina论坛     Blog     开放翻译计划     新浪微博  
 
  • 首页
  • 登录
  • 注册
  • 软件下载
  • 资料下载
  • 核心成员
  • 帮助
  •   Add to Google

    >> 本版讨论高级C/C++编程、代码重构(Refactoring)、极限编程(XP)、泛型编程等话题
    [返回] 中文XML论坛 - 专业的XML技术讨论区计算机技术与应用『 C/C++编程思想 』 → QQ悬挂窗口实现【附源码】 查看新帖用户列表

      发表一个新主题  发表一个新投票  回复主题  (订阅本版) 您是本帖的第 10930 个阅读者浏览上一篇主题  刷新本主题   树形显示贴子 浏览下一篇主题
     * 贴子主题: QQ悬挂窗口实现【附源码】 举报  打印  推荐  IE收藏夹 
       本主题类别:     
     卷积内核 帅哥哟,离线,有人找我吗?
      
      
      威望:8
      头衔:总统
      等级:博士二年级(版主)
      文章:3942
      积分:27590
      门派:XML.ORG.CN
      注册:2004/7/21

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给卷积内核发送一个短消息 把卷积内核加入好友 查看卷积内核的个人资料 搜索卷积内核在『 C/C++编程思想 』的所有贴子 访问卷积内核的主页 引用回复这个贴子 回复这个贴子 查看卷积内核的博客楼主
    发贴心情 QQ悬挂窗口实现【附源码】

    上过QQ的朋友们都知道,当QQ窗口位于桌面的左边界、右边界或顶部的时候,QQ会自动隐藏起来;而一旦鼠标再次接触到上述边界的时候,QQ窗口又会自动展开。QQ的这种特效在一定程度上大大的节约了桌面资源,给使用者带来的方便。
    QQ悬挂窗口主要特点就是结合窗口以及鼠标的位置,并通过鼠标事件来调整窗口的显示方式。其中,窗口以及鼠标的位置可以通过GetWindowRect和GetCursorPos这两个函数来获取,故如何获取鼠标事件成为QQ悬挂窗口实现的关键。
    对于一个窗口来说,按鼠标事件的触发位置,鼠标事件可以分为三类:
    1. 客户区鼠标消息:鼠标在窗口的客户区移动时产生的消息,此消息是标准的鼠标消息,MFC中通过WM_MOUSEMOVE这个事件解决了这个问题。
    2. 非客户区鼠标消息:鼠标在非客户区以外(标题栏、框架等)移动时产生的消息,此消息是标准的鼠标消息,MFC中通过WM_NCMOUSEMOVE这个事件解决了这个问题。
    3. 窗口以外的鼠标消息:鼠标不在本窗口移动时产生的消息,此消息不是标准的鼠标消息,在MFC中也找不到这样的事件。那该如何捕获这样的鼠标消息呢?
    窗口以外的鼠标消息必然是发生在其他窗口上的,此鼠标消息是发往其他窗口的消息队列中,由其他窗口的消息队列所维护。
    不过,我们可以通过设置全局鼠标钩子来监视鼠标的位置,并触发鼠标消息。如果将鼠标钩子设置在窗口内部设置的话,那此鼠标钩子仅能够监视到上述鼠标事件的前两类事件,而不能够监视到本窗口以外的鼠标消息,并不是真正的全局鼠标钩子。如果将鼠标钩子设置在DLL中,那么鼠标在整个屏幕上所发生的事件都会被这个鼠标过程所监察到,即可以捕获其他窗口的鼠标消息并将此鼠标消息发往本窗口的所属线程的消息队列中。在本窗口中,必须将本窗口的线程ID传到DLL中,使DLL能够将其他鼠标事件发到指定线程的消息队列中。具体实现如下:


    //------------------------------------------------------------------------------------
    // Function: SetHook - Creates mouse hook (Exported), called by CAppBarMngr
    // Arguments: _id - Calling thread ID, used to send message to it
    // _width - Width of window
    // _left - True if window is left side docked, false if not
    // Returns: False if it is already hooked
    // True if hook has been created
    //------------------------------------------------------------------------------------
    BOOL SetHook(DWORD _id, int _width, BOOL _left){     if (s_ThreadID)     return FALSE; // Already hooked!
         s_Width = _width;     s_Left = _left;     g_Hook = ::SetWindowsHookEx(WH_MOUSE, (HOOKPROC)MouseProc, g_Instance, 0);     s_ThreadID = _id;     return TRUE; // Hook has been created correctly
    }
      //-------------------------------------------------------------------------------------
      // Function: MouseProc - Callback function for mouse hook
      // Arguments: nCode - action code, according to MS documentation, must return
      // inmediatly if less than 0
      // wParam - not used
      // lParam - not used
      // Returns: result from next hook in chain
      //-------------------------------------------------------------------------------------
     static LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam)   {       static LRESULT lResult; // Made static to accelerate processing       static POINT pt; // idem
          if (nCode<0 && g_Hook)            {            ::CallNextHookEx(g_Hook, nCode, wParam, lParam); // Call next hook in chain            return 0;           }
       if (s_ThreadID)            {            // Obtain absolute screen coordinates            ::GetCursorPos(&pt);            static POINT ptOld;
         //只有当鼠标发生移动时候发生鼠标事件,没有想到鼠标不移动也会产生此鼠标过程,          //真让我大吃一惊,必须得防止鼠标消息乱发。          if(ptOld.x!=pt.x && ptOld.y!=pt.y)           {            ::PostThreadMessage(s_ThreadID, WM_USER+1000, 0, 0);             ptOld.x = pt.x;            ptOld.y = pt.y;            }      }  return ::CallNextHookEx(g_Hook, nCode, wParam, lParam); // Call next hook in chain   }
      //-------------------------------------------------------------------------------------
      // Function: UnSetHook - Removes hook from chain
      // Arguments: none
      // Returns: False if not hook pending to delete (no thread ID defined)
      // True if hook has been removed. Also returns true if there is not hook
      // handler, this can occur if Init failed when called in second instance
      //-------------------------------------------------------------------------------------
      BOOL UnSetHook()
      {
        if (!s_ThreadID) {    return FALSE; // There is no hook pending to close  }
      if (g_Hook) { // Check if hook handler is valid  ::UnhookWindowsHookEx(g_Hook); // Unhook is done here  s_ThreadID = 0; // Remove thread id to avoid continue sending  g_Hook = NULL; // Remove hook handler to avoid to use it again  }
     return TRUE; // Hook has been removed
    }
    鼠标消息一旦发到本窗口线程的消息队列后,本窗口过程在鼠标消息未被翻译之前从消息队列中取出消息,并进行处理。故得重载PreTranslateMessage这个虚函数。逻辑判断过程如下:


    BOOL CHookTestDlg::PreTranslateMessage(MSG* pMsg)   {
      // TODO: Add your specialized code here and/or call the base class  static int i=0;  int nSrcWidth = ::GetSystemMetrics(SM_CXSCREEN);  int nSrcHeight = ::GetSystemMetrics(SM_CYSCREEN);  switch(pMsg->message)  {  case WM_USER+1000:  {  POINT pt;  CRect rcWindow;  ::GetCursorPos(&pt);  GetWindowRect(&rcWindow);
      if(pt.x<1 && (pt.y>rcWindow.top && pt.y<rcWindow.bottom))  {    if(rcWindow.left<1 && rcWindow.Width()<1)     {      SliderWindow(LEFT, true);     }  else if(rcWindow.left<1 && rcWindow.Width()>99)     {
         }  else if(rcWindow.left>1 && rcWindow.Width()>99)     {    
         }  else     {
         }   }  else if(pt.y<rcWindow.top || pt.y>rcWindow.bottom)   {     if(rcWindow.left<1 && rcWindow.Width()<1)      {
          }     else if(rcWindow.left<1 && rcWindow.Width()>99)      {        SliderWindow(LEFT, false);      }     else if(rcWindow.left>1 && rcWindow.Width()>99)      {     
          }     else      {
          }
      }  else if(pt.x>0 && pt.x<100)  {    if(rcWindow.left<1 && rcWindow.Width()<1 && (pt.y>rcWindow.top && pt.y<rcWindow.bottom))     {
         }    else if(rcWindow.left<1 && rcWindow.Width()>99)      {      //SliderWindow(LEFT, true);      }    else if(rcWindow.left>1 && rcWindow.Width()>99)     {
         }    else     {
         }  }  else  {    if(rcWindow.left<1 && rcWindow.Width()<1)     {
          }    else if(rcWindow.left<1 && rcWindow.Width()>99 && (pt.y>rcWindow.top && pt.y<rcWindow.bottom))     {     SliderWindow(LEFT, false);     }    else if(rcWindow.left>1 && rcWindow.Width()>99)     {  
          }    else     {
          }
       }
    }  break;  default:  break; }  return CDialog::PreTranslateMessage(pMsg);}
       void CHookTestDlg::SliderWindow(int nPos, bool bShow){  CRect rc;  GetWindowRect(rc);  int nSrcWidth = ::GetSystemMetrics(SM_CXSCREEN);  switch(nPos)  {  case LEFT:  if(bShow)    {    for(int i=0; i<=10; i++)      {        SetWindowPos(&CWnd::wndTopMost, 0, rc.top, i*10, rc.Height(), SWP_SHOWWINDOW);        Sleep(20);       }     }  else    {     for(int i=0; i<=10; i++)      {        SetWindowPos(&CWnd::wndTopMost, 0, rc.top, 100-10*i, rc.Height(), SWP_SHOWWINDOW);        Sleep(20);       }   }   break;   case RIGHT:   break;   case TOP:   break;   case BOTTOM:   break;   default:   break;  }
    }

    朋友们,以后若想捕获其他窗口的鼠标事件的时候可以采用这个方法,大家也可以明白MFC中的标准鼠标消息的底层是怎么实现的,大家是否有眼前一亮的感觉呢?最后提出几个问题:
    1、 在安装完上述鼠标钩子后,MFC的标准鼠标消息WM_MOUSEMOVE、WM_LBUTTONDOWN等还有作用吗?
    2、 通过MFC的ON_MESSAGE将WM_USER+1000这个与指定的处理过程相关联来处理鼠标消息(当然此时不需要重载PreTranslateMessage),这样做可以吗?如:ON_MESSAGE(WM_USER+1000, MouseProc)。




    [此贴子已经被作者于2008-7-17 15:52:18编辑过]

       收藏   分享  
    顶(0)
      




    ----------------------------------------------
    事业是国家的,荣誉是单位的,成绩是领导的,工资是老婆的,财产是孩子的,错误是自己的。

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2008/7/16 9:03:00
     
     beckson 帅哥哟,离线,有人找我吗?
      
      
      等级:大一新生
      文章:2
      积分:58
      门派:XML.ORG.CN
      注册:2009/4/15

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给beckson发送一个短消息 把beckson加入好友 查看beckson的个人资料 搜索beckson在『 C/C++编程思想 』的所有贴子 引用回复这个贴子 回复这个贴子 查看beckson的博客2
    发贴心情 
    ~~
    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2009/4/28 9:17:00
     
     xzcxin061 帅哥哟,离线,有人找我吗?
      
      
      等级:大一新生
      文章:1
      积分:54
      门派:XML.ORG.CN
      注册:2009/6/6

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给xzcxin061发送一个短消息 把xzcxin061加入好友 查看xzcxin061的个人资料 搜索xzcxin061在『 C/C++编程思想 』的所有贴子 引用回复这个贴子 回复这个贴子 查看xzcxin061的博客3
    发贴心情 
    感谢楼主,学习用,参考下
    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2009/6/6 15:21:00
     
     GoogleAdSense
      
      
      等级:大一新生
      文章:1
      积分:50
      门派:无门无派
      院校:未填写
      注册:2007-01-01
    给Google AdSense发送一个短消息 把Google AdSense加入好友 查看Google AdSense的个人资料 搜索Google AdSense在『 C/C++编程思想 』的所有贴子 访问Google AdSense的主页 引用回复这个贴子 回复这个贴子 查看Google AdSense的博客广告
    2024/5/3 6:51:47

    本主题贴数3,分页: [1]

    管理选项修改tag | 锁定 | 解锁 | 提升 | 删除 | 移动 | 固顶 | 总固顶 | 奖励 | 惩罚 | 发布公告
    W3C Contributing Supporter! W 3 C h i n a ( since 2003 ) 旗 下 站 点
    苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
    93.750ms