Chinaunix首页 | 论坛 | 博客
  • 博客访问: 9546951
  • 博文数量: 1227
  • 博客积分: 10026
  • 博客等级: 上将
  • 技术积分: 20273
  • 用 户 组: 普通用户
  • 注册时间: 2008-01-16 12:40
文章分类

全部博文(1227)

文章存档

2010年(1)

2008年(1226)

我的朋友

分类: C/C++

2008-03-11 17:48:26

看到QQ这样的截图实在忍心不住想,是怎以实现的?想了许久想到一个实现法子,下面就让我给大家展示一下,模拟QQ静态模拟截图, 我写的代码比较乱,是想到哪写到哪,我想肯实还有比这更简单的办法!!程序实现思路,程序是基于对话框,无标题栏,系统菜单,最小化框,最大化框,无边框....,程序初始化时,把整个桌面屏幕截图,保存下来,然后把对话框设置全屏顶层窗口,然后把保存下来的位图,在OnEraseBkgnd中把整个位图贴到对话框上,然后用像皮筋类选择截取范围,看起来就像操作屏幕一样 。



图一 示例

下面说说实现细节:

一、初始化程序,在CCatchScreenDlg构造函数中 初始化像皮筋类和保存全屏位图,

      //初始化像皮筋类
 m_rectTracker.m_nStyle=CRectTracker::resizeInside|CRectTracker::solidLine;  
 m_rectTracker.m_rect.SetRect(-1,-1,-1,-1);

    m_hCursor=AfxGetApp()->LoadCursor(IDC_CURSOR1);  
    
 
 m_bDraw=FALSE;
 m_bFirstDraw=FALSE;
 m_bQuit=FALSE;
 m_bShowMsg=FALSE;
    m_startPt=0;
    
 //获取屏幕分辩率
 m_xScreen = GetSystemMetrics(SM_CXSCREEN);
 m_yScreen = GetSystemMetrics(SM_CYSCREEN);

 //截取屏幕到位图中
 CRect rect(0, 0,m_xScreen,m_yScreen);
 m_pBitmap=CBitmap::FromHandle(CopyScreenToBitmap(&rect));
    
 //一定要初始化m_rgn,
    m_rgn.CreateRectRgn(0,0,50,50);
  然后在CCatchScreenDlg::OnInitDialog()中设置对话框成顶层窗口。在 CCatchScreenDlg::OnEraseBkgnd中 用整个桌面位图填充全屏对话框背景

二、从 CRectTracker中派生了一个CTrack 类在类中重载了DrawTrackerRect函数,在函数中直接 更新窗口,消除拖动和改变大小时的虚线框可能还有更简单的方法,本人能力有限想不出更简的!

三、从Edit类中派生一个CMyEdit类,用该类的实例对像显示操作提示信息,在 CMyEdit::OnEraseBkgnd中,用一位图填充背景,类中响应了WM_MOUSEMOVE消息,在CMyEdit::OnMouseMove(UINT nFlags, CPoint point) 中移动窗口在左上角和右上角之间来回移动.

 CRect rect;
 GetWindowRect(&rect);

 int xScreen = GetSystemMetrics(SM_CXSCREEN);
 //int ySCreen = GetSystemMetrics(SM_CYSCREEN);

 if(m_bMove)
 {
  //移动到左上角
  MoveWindow(10,10,rect.Width(),rect.Height());
  m_bMove=FALSE;
 }
 else
 {
  //移动到右上角
  MoveWindow(xScreen-180,10,rect.Width(),rect.Height());
  m_bMove=TRUE;
 }


在主对话框上放置编缉框,然后关联一个CMyEdit的变量,在主对话显示时移动到左上角

 //把对化框设置成全屏顶层窗口
 SetWindowPos(&wndTopMost,0,0,m_xScreen,m_yScreen,SWP_SHOWWINDOW);
    
 //移动操作提示窗口
 CRect rect;
 m_tipEdit.GetWindowRect(&rect);
         m_tipEdit.MoveWindow(10,10,rect.Width(),rect.Height());
    
 //显示操作提示窗口文字
 DrawTip();
 
 //捕获按键消息窗口,将对话框的句柄传递到CCatchScreenApp中
 ((CCatchScreenApp *)AfxGetApp())->m_hwndDlg=m_hWnd;

四、程序中有两个重要成员函数,一个是画截取矩形时的信息显示在鼠标右上角。

//显示截取矩形信息
void CCatchScreenDlg::DrawMessage(CRect &inRect)
{
 //截取矩形大小信息离鼠标间隔
 const int space=3;
    
 //设置字体颜色大小
 CClientDC dc(this);
 CPoint pt;
 CPen pen(PS_SOLID,1,RGB(147,147,147));
 dc.SetTextColor(RGB(147,147,147));
 CFont font;
 font.CreatePointFont(90,"宋体");
 dc.SelectObject(&font);

 //得到字体宽度和高度
 GetCursorPos(&pt);
 dc.SetBkMode(TRANSPARENT);
 TEXTMETRIC tm;
 int charHeight;
 CSize size;
 int lineLength;
 dc.GetTextMetrics(&tm);
 charHeight = tm.tmHeight+tm.tmExternalLeading;
 size=dc.GetTextExtent("顶点位置  ",strlen("顶点位置  "));
 lineLength=size.cx;
    
 //初始化矩形, 以保证写下六行文字
 CRect rect(pt.x+space,pt.y-charHeight*6-space,pt.x+lineLength+space,pt.y-space);

 int x = GetDeviceCaps(dc, HORZRES);
 int y = GetDeviceCaps(dc, VERTRES);

    //创建临时矩形
    CRect rectTemp;
 //当矩形到达桌面边缘时调整方向和大小
 if((pt.x+rect.Width())>=x)
 {
  //桌面上方显示不下矩形
  rectTemp=rect;
  rectTemp.left=rect.left-rect.Width()-space*2;
  rectTemp.right=rect.right-rect.Width()-space*2;;
  rect=rectTemp;
 }

 if((pt.y-rect.Height())<=0)
 {
  //桌面右方显示不下矩形
  rectTemp=rect;
  rectTemp.top=rect.top+rect.Height()+space*2;;
  rectTemp.bottom=rect.bottom+rect.Height()+space*2;;
  rect=rectTemp;
  
 }
    
 //创建空画刷画矩形
 dc.SelectObject((HBRUSH)GetStockObject(NULL_BRUSH));
 dc.Rectangle(rect);
    
 //在矩形中显示文字
 CString string(" 顶点位置 ");
    dc.TextOut(rect.left,rect.top,string);

 string.Format(" (%d,%d)",inRect.left,inRect.top);
 dc.TextOut(rect.left,rect.top+charHeight,string);

 string=" 矩形大小 ";
 dc.TextOut(rect.left,rect.top+charHeight*2,string);

 string.Format(" (%d,%d)",inRect.Width(),inRect.Height());
 dc.TextOut(rect.left,rect.top+charHeight*3,string);

 string=" 光标坐标 ";
 dc.TextOut(rect.left,rect.top+charHeight*4,string);

 string.Format(" (%d,%d)",pt.x,pt.y);
 dc.TextOut(rect.left,rect.top+charHeight*5,string);
 
}
  根据鼠标的位置和要显示的字的宽度各高度在鼠标左上角位置处构造一个CRect对像,在CRect中显示矩形信息,同时根据鼠标当前的位置,在左边和上边不能正常显示信息时动态度反转矩形,还有一个类是DrawTip()在鼠标按下、松下是显示相应的操作提示
//显示操作提示信息
void CCatchScreenDlg::DrawTip()
{
    
    //得当前坐标像素,
 CPoint pt;
 GetCursorPos(&pt);

 //当到当前R,G,B,各像素值
 COLORREF color;
 CClientDC dc(this);
 color=dc.GetPixel(pt);
 BYTE rValue,gValue,bValue;
 rValue=GetRValue(color);
 gValue=GetGValue(color);
 bValue=GetGValue(color);
 
 //按格式排放字符串
 CString string;
 CString strTemp;
 string.Format("\r\n\r\n\r\n ·当前像素RGB (%d,%d,%d)\r\n",rValue,gValue,bValue);
    
 if(!m_bDraw&&!m_bFirstDraw)
 {
  strTemp="\r\n ·按下鼠标左键不放选择截取\r\n 范围\r\n\r\n ·按ESC键或鼠标右键退出";
 }
 else
 if(m_bDraw&&m_bFirstDraw)
 {
  strTemp="\r\n ·松开鼠标左键确定截取范围\r\n\r\n ·按ESC键退出";
 }
 else
 if(m_bFirstDraw)
 {
  strTemp="\r\n ·用鼠标左键调整截取范围的\r\n 大小和位置\r\n\r\n·截取范围内双击鼠标左键保存\r\n 图像,

结束操作\r\n\r\n·点击鼠标右键重新选择"; } string+=strTemp; //显示到编缉框中,操作提示窗口 m_tipEdit.SetWindowText(string); }
程序有点乱,文字都是硬编码写上去的,整个字符串显示在一对话框的编辑出框中,用空格和回车换行来控制摆放格式。

五、由于对话框中有一个编辑框,并且背景是一张位图,由于程截图时要经常刷新窗口,如果刷新整个窗口的话,编辑框会有很大的闪烁,
所以只能更新编辑框以外的区域,函数PaintWindow()计算更新区域,并更新窗口

void CCatchScreenDlg::PaintWindow()
{
 //获取当全屏对话框窗口大小
 CRect rect1;
 GetWindowRect(rect1);

 //获取编辑框窗口大小
 CRect rect2;
 m_tipEdit.GetWindowRect(rect2);

 CRgn rgn1,rgn2;
 rgn1.CreateRectRgnIndirect(rect1);
 rgn2.CreateRectRgnIndirect(rect2);

 //获取更新区域,就是除了编辑框窗口不更新
 m_rgn.CombineRgn(&rgn1,&rgn2,RGN_DIFF);
 
 InvalidateRgn(&m_rgn);
}
六、当然不能忘了方向键来微调截取矩形大小和位置,由于是基于对话框的程序,按键消息,被Windows内部的对话框过程处理了(即在基类中完成了处理,读者可以查看MFC的源代码),或者被发送给子控件进行处理,所以在对话框类中可能利用
BOOLProcessMessageFilter(int code, LPMSG lpMsg)这个虚函数来过滤或响应菜单和对话框的特定Windows消息在头文件中增加一个类型为HWND,Public成员变量名m_hwndDlg,在构造函数中初始化为NULL;在 CCatchScreenDlg::OnInitDialog()中加入
((CWinSunApp*)AfxGetApp())->m_hwndDlg=m_hWnd; 

将对话框的句柄传递到CWinSunApp类中

//********************************************************************************
#define SHIFTED 0x8000 
//********************************************************************************

BOOL CCatchScreenApp::ProcessMessageFilter(int code, LPMSG lpMsg) 
{
 // TODO: Add your specialized code here and/or call the base class
 if(m_hwndDlg!=NULL)
 {   //判断消息,如果消息是从对话框发出的或者其子控件发出的,就进行处理
  if((lpMsg->hwnd==m_hwndDlg) || ::IsChild(m_hwndDlg,lpMsg->hwnd))
  {
   //如果消息是WM_KEYDOWN
   //用方向键调整位置
   if(lpMsg->message==WM_KEYDOWN)
   {
    CRect rect(0,0,0,0);
    CCatchScreenDlg * pDlg=(CCatchScreenDlg *)AfxGetMainWnd();
    
    rect=pDlg->m_rectTracker.m_rect;

    if(pDlg->m_bFirstDraw)
    {
     
     //如果Shift键按下则方向键调整大小
     BOOL isShifeDowm=FALSE;
     int nVirtKey;
     nVirtKey = GetKeyState(VK_SHIFT); 
     if (nVirtKey & SHIFTED) 
      isShifeDowm=TRUE;

     switch(lpMsg->wParam)
     {
     case VK_UP:
      //如果按下Shift,则只调整一边
      if(!isShifeDowm)
       rect.top-=1;
      rect.bottom-=1;
      pDlg->m_rectTracker.m_rect=rect;
      pDlg->PaintWindow();
      break;
     case VK_DOWN:
      rect.top+=1;
      if(!isShifeDowm)
       rect.bottom+=1;
      pDlg->m_rectTracker.m_rect=rect;
      pDlg->PaintWindow();
      break;
     case VK_LEFT:
      if(!isShifeDowm)
       rect.left-=1;
      rect.right-=1;
      pDlg->m_rectTracker.m_rect=rect;
      pDlg->PaintWindow();
      break;
     case VK_RIGHT:
      rect.left+=1;
      if(!isShifeDowm)
       rect.right+=1;
      pDlg->m_rectTracker.m_rect=rect;
      pDlg->PaintWindow();
      break;
     }
    }
   }
  }
 } 
 return CWinApp::ProcessMessageFilter(code, lpMsg);
}
  程序中涉及到像皮筋类的使用,就不多说了,知识库中有许多文章,接着就双击选区保存图片到剪贴板中。就说到这。我嘴笨 , 详细的请参看源文件注释,,有什么问题或您有更好的建意请不要忘了和我联系哦,邮箱: jzlkcn@yahoo.com.cn
阅读(1646) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~