用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函数内的可见性判断,隐藏窗体也无处藏身。有兴趣的朋友可以亲自设计一下,如果你是懒惰者,可到华军软件园下在该程序红色间谍.

上一篇: 利用shell编程实现进程删除自身
下一篇: MFC响应机制

最近心血来潮,对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函数内的可见性判断,隐藏窗体也无处藏身。有兴趣的朋友可以亲自设计一下,如果你是懒惰者,可到华军软件园下在该程序红色间谍.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值