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

    >> 本版讨论高级C/C++编程、代码重构(Refactoring)、极限编程(XP)、泛型编程等话题
    [返回] 中文XML论坛 - 专业的XML技术讨论区计算机技术与应用『 C/C++编程思想 』 → 用Visual C++设计窗体探测器 查看新帖用户列表

      发表一个新主题  发表一个新投票  回复主题  (订阅本版) 您是本帖的第 4052 个阅读者浏览上一篇主题  刷新本主题   平板显示贴子 浏览下一篇主题
     * 贴子主题: 用Visual C++设计窗体探测器 举报  打印  推荐  IE收藏夹 
       本主题类别:     
     卷积内核 帅哥哟,离线,有人找我吗?
      
      
      威望:8
      头衔:总统
      等级:博士二年级(版主)
      文章:3942
      积分:27590
      门派:XML.ORG.CN
      注册:2004/7/21

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给卷积内核发送一个短消息 把卷积内核加入好友 查看卷积内核的个人资料 搜索卷积内核在『 C/C++编程思想 』的所有贴子 访问卷积内核的主页 引用回复这个贴子 回复这个贴子 查看卷积内核的博客楼主
    发贴心情 用Visual C++设计窗体探测器



    最近心血来潮,对spy深感兴趣,便准备进行一次间谍行动,目标很简单,利用EnableWindow函数激活不可用或无效的控件按钮,当然,这窗体探测器是必不可少了,原以为很简单,但实际行动起来,却遇到不少麻烦,现将经过记录下来,供有兴趣的朋友参考。

      我们大都见过spyxx中的窗体探测器,当鼠标在窗体探测器上按下左键时,更改鼠标样式,同时捕获鼠标,探测鼠标下的窗体直到鼠标左键松开。这样我们可以写出代码框架了:

    case WM_LBUTTONDOWN://鼠标左键按下,检测拖动还是探测
    {
     MSG msg;
     //在窗体探测器中按下鼠标
     if(在窗体探测器内==TRUE)//替换光标,探测拖动
     {
      //更改鼠标样式
      SetCursor(...);
      //捕获鼠标
      SetCapture(hWnd);
      hWndNow=NULL;//当前窗体设为空//此为全局变量
      //获取鼠标移动消息
      while(GetMessage(&msg,NULL,WM_MOUSEFIRST,WM_MOUSELAST))
      {
       if((msg.message==WM_MOUSEMOVE)||(msg.message==WM_NCMOUSEMOVE))
       {
        HWND hWndPoint;
        //设置光标
        SetCursor(LoadCursor(hInst, (LPCTSTR)CUR_WindowsPY));
        GetCursorPos(&Point);
        //探测当前鼠标点
        if(hWndPoint=WindowFromPoint(Point))
        {
         if(hWndNow!=hWndPoint)//目标已改变
         {
          if(hWndNow)
          {
           //清除旧目标上的黑框
           XorBorder(hWndNow);
          }
          //并且不属于本线程
          if(GetWindowThreadProcessId(hWndPoint,NULL)!=GetCurrentThreadId())
          {
           //将当前窗体画一黑边框
           hWndNow=hWndPoint;
           XorBorder(hWndNow);
          }
          else
          {
           hWndNow=NULL;
          }
          //显示窗体信息
          ShowWindowMessage(hWnd,hWndNow);
         }
        }
        else
        {
         hWndNow=NULL;
         ShowWindowMessage(hWnd,hWndNow);
        }
       }
       //如果左键松开,则跳出
       else if(msg.message==WM_LBUTTONUP)
       {
        break;
       }
      }
      XorBorder(hWndNow);
      //释放鼠标并恢复鼠标样式
      SetCursor(LoadCursor(NULL,(LPCTSTR)IDC_ARROW));
      ReleaseCapture();
     }
     break;
    }

    这里的问题就在 WindowFromPoint 和 XorBorder 中.这里我们先看第一版XorBorder:

    void XorBorder(HWND hWnd)
    {
     RECT rect; //当前窗体区域
     HDC hdc=GetWindowDC(hWnd);
     GetWindowRect(hWnd,&rect);
     //调整边框
     rect.bottom-=rect.top;
     rect.right-=rect.left;
     rect.left=rect.top=0;
     SetROP2(hdc,R2_NOT);
     FrameRect(hdc,&rect,GetStockObject(BLACK_BRUSH));
     ReleaseDC(hWnd,hdc);
    }

      然而结果总以失败告终,查看SetROP2资料,隐隐约约感觉它仅对画笔起作用,画刷无效(仅代表个人观点,正误难辨)。于是将其改为Rectangle,然而它可是连边框带矩形内部全部搞定了,这并不是我要的效果呀,这该怎么办呢?看我的最终解决方案:

    void XorBorder(HWND hWnd)
    {
     HPEN hPen,hOldPen;
     RECT rect; //当前窗体区域
     HDC hdc=GetWindowDC(hWnd);
     GetWindowRect(hWnd,&rect);
     //调整边框
     rect.bottom-=rect.top;
     rect.right-=rect.left;
     rect.left=rect.top=0;
     SetROP2(hdc,R2_NOT);
     hPen=CreatePen(PS_SOLID,6,RGB(0,0,0));
     hOldPen=SelectObject(hdc,hPen);
     //选择刷子为空,使矩形不填充内部
     SelectObject(hdc,GetStockObject(NULL_BRUSH));
     Rectangle(hdc,rect.left,rect.top,rect.right,rect.bottom);
     SelectObject(hdc,hOldPen);
     DeleteObject(hPen);
     ReleaseDC(hWnd,hdc);
    }

      我将当前画刷选择为空,我不知道这种方法是否正统,反正msdn未找到,虽然看着仅仅是小小的改动,倒是费了我好大功夫,我可是一直在FrameRect上打转呀!

      现在我们开看 WindowFromPoint :msdn上说该函数跳过无效按钮,需要使用ChildWindowFromPoint来解决,然而,问题并不是那么简单,先看下面这段资源文件:

    IDD_DIALOG1 DIALOGEX 0, 0, 186, 110
    STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION |
    WS_SYSMENU
    CAPTION "Dialog"
    FONT 8, "MS Shell Dlg", 400, 0, 0x1
    BEGIN
    GROUPBOX "静态",IDC_STATIC,45,14,81,69
    CONTROL "选中1",IDC_CHECK1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
    57,28,61,16
    CONTROL "选中1",IDC_CHECK2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
    57,54,60,10
    END

      我试验过,要想得到IDC_CHECK1,IDC_CHECK2,用上面的两个函数是无法实现的,(你知道吗,GROUPBOX是button类,而不是static,直到今天我才知道),请不要告诉我改变GROUPBOX的Tab键顺序,我们的探测器要面对各种情况,下面看我的解决方案:

    HWND BrotherWindowFromPoint(HWND hWndPoint,const POINT Point)
    {
     //检测兄弟窗口
     RECT rcPoint;
     RECT rcNow;
     HWND hWndBrother=hWndPoint;//GetWindow(hWndPoint,GW_HWNDFIRST);
     hWndPoint=NULL;
     do
     {
      if(GetWindowStyle(hWndBrother)&WS_VISIBLE)//可见
      {
       GetWindowRect(hWndBrother,&rcNow);
       if(PtInRect(&rcNow,Point))
       {
        //检验矩形嵌套情况
        if(hWndPoint==NULL)
        {
         hWndPoint=hWndBrother;
         rcPoint=rcNow;
        }
        else if(
         ((rcNow.bottom<rcPoint.bottom)&&(rcNow.bottom>rcPoint.top)&&
         (rcNow.left>rcPoint.left)&&(rcNow.left<rcPoint.right))//左下角
         ||((rcNow.bottom<rcPoint.bottom)&&(rcNow.bottom>rcPoint.top)&&
         (rcNow.right>rcPoint.left)&&(rcNow.right<rcPoint.right))//右下角
         ||((rcNow.top>rcPoint.top)&&(rcNow.top<rcPoint.bottom)&&
         (rcNow.left>rcPoint.left)&&(rcNow.left<rcPoint.right))//左上角
         ||((rcNow.top>rcPoint.top)&&(rcNow.top<rcPoint.bottom)
         &&(rcNow.right>rcPoint.left)&&(rcNow.right<rcPoint.right))//右上角
        )
       {
        hWndPoint=hWndBrother;
        rcPoint=rcNow;
       }
      }
     }
     }while(hWndBrother=GetWindow(hWndBrother,GW_HWNDNEXT));
      return hWndPoint;
    }


      该函数检测同层窗口,获得指定点内,嵌套最深的窗口,由此我们便可以生成我们自己的WindowFromPoint

    HWND MyWindowFromPoint(const POINT Point)
    {
     HWND hWndPoint=WindowFromPoint(Point);
     if(hWndPoint)
     {
      //宽度搜索兄弟窗口
      HWND hWndChild;
      if(!(GetWindowLong(hWndPoint,GWL_STYLE)&WS_CHILD))//顶层窗口
      return hWndPoint;
      //非顶层窗口,要进行兄弟查找.
      hWndPoint=MyBrotherWindowFromPoint(hWndPoint,Point);
      assert(hWndPoint);
      //深度搜索子窗口
      while(hWndChild=GetTopWindow(hWndPoint))
      {
       //宽度搜索兄弟子窗口
       if(NULL==(hWndChild=MyBrotherWindowFromPoint(hWndChild,Point)))
       break;
       hWndPoint=hWndChild;
      } //*/
     }
     return hWndPoint;
    }

      该函数首先判断是否是顶层窗体,如果不是,首先进行宽度搜索,虽然麻烦了些,然而却不得不如此。顺便说一下,VC资源编辑器中正在设计的对话框拥有disable属性,spyxx你的窗体探测器也不能得到其内的所有控件句柄,而该函数所向无敌,如果去掉BrotherWindowFromPoint函数内的可见性判断,隐藏窗体也无处藏身。有兴趣的朋友可以亲自设计一下,如果你是懒惰者,可到华军软件园下在该程序红色间谍.


       收藏   分享  
    顶(0)
      




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

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2007/8/11 11:53:00
     
     GoogleAdSense
      
      
      等级:大一新生
      文章:1
      积分:50
      门派:无门无派
      院校:未填写
      注册:2007-01-01
    给Google AdSense发送一个短消息 把Google AdSense加入好友 查看Google AdSense的个人资料 搜索Google AdSense在『 C/C++编程思想 』的所有贴子 访问Google AdSense的主页 引用回复这个贴子 回复这个贴子 查看Google AdSense的博客广告
    2024/5/19 10:53:57

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

     *树形目录 (最近20个回帖) 顶端 
    主题:  用Visual C++设计窗体探测器(6724字) - 卷积内核,2007年8月11日

    W3C Contributing Supporter! W 3 C h i n a ( since 2003 ) 旗 下 站 点
    苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
    62.500ms