关于截图工具,QQ的截图可以自动选择窗口,之前以为是颜色相近的选取,后来意识到不对,应该是窗口,用spy++找到的窗口和QQ截取到的窗口也是一样的,
但是有个问题,那就是怎么选择这些窗口? 我的想法是枚举所有窗口,记录这些窗口的位置和大小,然后当鼠标经过的时候用DC来画框,但是这样有个问题就是枚举到的窗口有几百个,太多了,当鼠标经过的从几百个大小的数组或vector里查找当前鼠标的位置在哪个窗口范围内实在是太拙计了,这种方法实在不太靠谱。
关于QQ的截图: 拷贝当前屏幕图像然后全屏打开一个(透明?)窗口,把拷贝到的图像贴在那个全屏窗口上,然后鼠标经过窗口时画框。。。
由于没能实现后面那几步,所以只是做了一个简易的截图工具,其原理差不多,那就是:当截图时拷贝当前屏幕图像然后打开一个对话框,把这个对话框放大到屏幕大小,也就是全屏,然后把屏幕图像贴到对话框上,然后鼠标可以拖动选择区域,在拖动的时候有一个【确定】button在鼠标附近,点击这个button就可以保存选择的区域至bmp文件。 当然需要考虑背景重绘的情况!
把截图工具封装成了一个类,该类继承自CWnd类,对应的对话框为IDD_DLG_CAPTURE ,在Jietu.h第15行, 对话框上有一个button,在这里button的名字没改,就是button1,如果改了这个名字,对应的代码里也需要修改。
Jietu.h
1 #pragma once 2 3 4 // CJietu 对话框 5 6 class CJietu : public CDialog 7 { 8 DECLARE_DYNAMIC(CJietu) 9 10 public : 11 CJietu(CWnd* pParent = NULL); // 标准构造函数 12 virtual ~ CJietu(); 13 14 // 对话框数据 15 enum { IDD = IDD_DLG_CAPTURE }; 16 17 protected : 18 virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 19 20 DECLARE_MESSAGE_MAP() 21 public : 22 afx_msg void OnLButtonDown(UINT nFlags, CPoint point); 23 afx_msg void OnPaint(); 24 afx_msg void OnLButtonUp(UINT nFlags, CPoint point); 25 afx_msg void OnMouseMove(UINT nFlags, CPoint point); 26 afx_msg BOOL OnEraseBkgnd(CDC* pDC); 27 afx_msg void OnShowWindow(BOOL bShow, UINT nStatus); 28 virtual BOOL OnInitDialog(); 29 30 void WriteBMPFile(HBITMAP hBitMap, LPTSTR filename, HDC hDC, int cx, int cy); 31 32 protected : 33 int m_nCx; 34 int m_nCy; 35 36 CPoint m_pointStart; // 记录起点; 37 CPoint m_pointEnd; // 记录终点; 38 bool m_bIsBegin; 39 40 CDC m_dcGlobal; 41 HDC m_hDCGlobal; 42 HBITMAP m_hBitmapGlobal; 43 CBitmap m_bitmapGlobal; 44 CWnd *m_pWndButtonOK; // 确定 按钮; 45 46 public : 47 afx_msg void OnBnClickedButton1(); 48 };
Jietu.cpp
1 // Jietu.cpp : 实现文件 2 // 3 4 #include " stdafx.h " 5 #include " CameraMonitor.h " 6 #include " Jietu.h " 7 8 9 // CJietu 对话框 10 11 IMPLEMENT_DYNAMIC(CJietu, CDialog) 12 13 CJietu::CJietu(CWnd* pParent /* =NULL */ ) 14 : CDialog(CJietu::IDD, pParent) 15 { 16 m_nCx = GetSystemMetrics(SM_CXSCREEN); 17 m_nCy = GetSystemMetrics(SM_CYSCREEN); 18 m_bIsBegin = false ; 19 } 20 21 CJietu::~ CJietu() 22 { 23 if (m_hDCGlobal) 24 { 25 DeleteDC(m_hDCGlobal); 26 } 27 if (m_hBitmapGlobal) 28 { 29 DeleteObject(m_hBitmapGlobal); 30 } 31 32 if (m_dcGlobal) 33 { 34 m_dcGlobal.DeleteDC(); 35 } 36 } 37 38 void CJietu::DoDataExchange(CDataExchange* pDX) 39 { 40 CDialog::DoDataExchange(pDX); 41 } 42 43 44 BEGIN_MESSAGE_MAP(CJietu, CDialog) 45 ON_WM_LBUTTONDOWN() 46 ON_WM_PAINT() 47 ON_WM_LBUTTONUP() 48 ON_WM_MOUSEMOVE() 49 ON_WM_ERASEBKGND() 50 ON_WM_SHOWWINDOW() 51 ON_BN_CLICKED(IDC_BUTTON1, & CJietu::OnBnClickedButton1) 52 END_MESSAGE_MAP() 53 54 55 // CJietu 消息处理程序 56 57 void CJietu::OnLButtonDown(UINT nFlags, CPoint point) 58 { 59 // TODO: 在此添加消息处理程序代码和/或调用默认值; 60 61 ClientToScreen(& point); 62 m_pointStart = point; // 记录起点; 63 m_bIsBegin = true ; 64 65 SetCursor(AfxGetApp()-> LoadStandardCursor(IDC_CROSS)); 66 67 CDialog::OnLButtonDown(nFlags, point); 68 } 69 70 void CJietu::OnLButtonUp(UINT nFlags, CPoint point) 71 { 72 // TODO: 在此添加消息处理程序代码和/或调用默认值; 73 m_bIsBegin = false ; 74 ClientToScreen(& point); 75 76 // 鼠标左键抬起,鼠标指针恢复正常; 77 SetCursor(AfxGetApp()-> LoadStandardCursor(IDC_ARROW)); 78 79 80 81 CDialog::OnLButtonUp(nFlags, point); 82 } 83 84 void CJietu::OnMouseMove(UINT nFlags, CPoint point) 85 { 86 // TODO: 在此添加消息处理程序代码和/或调用默认值; 87 88 89 if ( true == m_bIsBegin) 90 { 91 int b = false ; 92 if ( false == b && m_pWndButtonOK != NULL) 93 { 94 m_pWndButtonOK->ShowWindow(SW_SHOW); // 确定 按钮显示; 95 b = true ; 96 } 97 98 Invalidate(TRUE); // 先把之前线条的清空; 99 UpdateWindow(); 100 101 ClientToScreen(& point); 102 m_pointEnd = point; // 记录终点; 103 104 CPen pen(PS_SOLID, 6 , RGB( 255 , 0 , 0 )); 105 CPen * oldPen; 106 CDC * pDC; 107 CWnd * pWnd; 108 CRect rect; 109 110 int cx = m_nCx; 111 int cy = m_nCy; 112 113 pWnd = FromHandle(m_hWnd); 114 pDC = pWnd-> GetDC(); 115 116 int x1 = m_pointStart.x; 117 int y1 = m_pointStart.y; 118 int x2 = point.x; 119 int y2 = y1; 120 int x3 = point.x; 121 int y3 = point.y; 122 int x4 = x1; 123 int y4 = y3; 124 125 oldPen = pDC->SelectObject(& pen); 126 127 pDC-> MoveTo(x1, y1); 128 pDC-> LineTo(x2, y2); 129 130 pDC-> MoveTo(x2, y2); 131 pDC-> LineTo(x3, y3); 132 133 pDC-> MoveTo(x3, y3); 134 pDC-> LineTo(x4, y4); 135 136 pDC-> MoveTo(x4, y4); 137 pDC-> LineTo(x1, y1); 138 139 int width = abs(x1 - x3); 140 int height = abs(y1 - y3); 141 char wArr[ 5 ] = { 0 }; 142 char hArr[ 5 ] = { 0 }; 143 _itoa_s(width, wArr, 10 ); 144 _itoa_s(height, hArr, 10 ); 145 146 CString str( " 尺寸: " ); 147 str += wArr; 148 str += " x " ; 149 str += hArr; 150 pDC-> SetBkMode(TRANSPARENT); 151 152 CFont font; 153 CFont * oldFont; 154 font.CreatePointFont( 150 , _T( " 宋体 " )); 155 oldFont = pDC->SelectObject(& font); 156 pDC->SetTextColor(RGB( 0 , 0 , 255 )); 157 158 int xPos = 0 ; 159 int yPos = 0 ; 160 int xStep = 100 ; 161 int yStep = 50 ; 162 163 // 选区到了边界的情况; 164 if (cx - x3 <= xStep || cy - y3 <= yStep || (cx - x3) >= (cx - xStep) || (cy - y3) >= (cy - yStep)) 165 { 166 if (x3 >= x1 && y3 <= y1) 167 { 168 pDC->TextOut(x3 - xStep - 50 , y3 + yStep - 10 , str); 169 m_pWndButtonOK->SetWindowPos(NULL, x3 - xStep - 50 , y3 + yStep + 20 , 0 , 0 , SWP_NOSIZE); 170 } 171 else if (x3 >= x1 && y3 >= y1) 172 { 173 pDC->TextOut(x3 - xStep - 50 , y3 - yStep, str); 174 m_pWndButtonOK->SetWindowPos(NULL, x3 - xStep - 50 , y3 - yStep - 60 , 0 , 0 , SWP_NOSIZE); 175 } 176 else if (x3 <= x1 && y3 >= y1) 177 { 178 pDC->TextOut(x3 + xStep - 20 , y3 - yStep, str); 179 m_pWndButtonOK->SetWindowPos(NULL, x3 + xStep - 20 , y3 - yStep - 60 , 0 , 0 , SWP_NOSIZE); 180 } 181 else 182 { 183 pDC->TextOut(x3 + xStep - 50 , y3 + yStep - 10 , str); 184 m_pWndButtonOK->SetWindowPos(NULL, x3 + xStep - 50 , y3 + yStep + 20 , 0 , 0 , SWP_NOSIZE); 185 } 186 187 } 188 else 189 { 190 pDC-> TextOut(x3, y3, str); 191 m_pWndButtonOK->SetWindowPos(NULL, x3, y3 + 30 , 0 , 0 , SWP_NOSIZE); 192 } 193 194 195 pDC-> SelectObject(oldFont); 196 pDC-> SelectObject(oldPen); 197 pWnd-> ReleaseDC(pDC); 198 pen.DeleteObject(); 199 font.DeleteObject(); 200 201 } 202 203 CDialog::OnMouseMove(nFlags, point); 204 } 205 206 void CJietu::OnPaint() 207 { 208 CPaintDC dc( this ); // device context for painting 209 // TODO: 在此处添加消息处理程序代码 210 // 不为绘图消息调用 CDialog::OnPaint() 211 212 HWND hWnd = m_hWnd; 213 CWnd *pWnd = FromHandle(hWnd); 214 CDC *pDC = pWnd-> GetDC(); 215 HDC hDC = pDC-> GetSafeHdc(); 216 217 StretchBlt(hDC, 0 , 0 , m_nCx, m_nCy, m_hDCGlobal, 0 , 0 , m_nCx, m_nCy, SRCCOPY); 218 219 pWnd-> ReleaseDC(pDC); 220 221 } 222 223 224 225 226 227 BOOL CJietu::OnEraseBkgnd(CDC* pDC) 228 { 229 // TODO: 在此添加消息处理程序代码和/或调用默认值; 230 231 HWND hWnd = m_hWnd; 232 CWnd *pWnd = FromHandle(hWnd); 233 HDC hDC = pDC-> GetSafeHdc(); 234 235 StretchBlt(hDC, 0 , 0 , m_nCx, m_nCy, m_hDCGlobal, 0 , 0 , m_nCx, m_nCy, SRCCOPY); 236 237 return TRUE; 238 // return CDialog::OnEraseBkgnd(pDC); 239 } 240 241 void CJietu::OnShowWindow(BOOL bShow, UINT nStatus) 242 { 243 m_pWndButtonOK = (CWnd*)GetDlgItem(IDC_BUTTON1); // 确定 按钮指针; 244 245 246 //////////////////////////////////////////////////////////////////////// // 247 CDialog::OnShowWindow(bShow, nStatus); 248 SetWindowPos(&wndTop, 0 , 0 , m_nCx, m_nCy, 0 ); 249 //////////////////////////////////////////////////////////////////////// // 250 251 CDC *pDC = GetDC(); 252 HDC hDC = pDC-> GetSafeHdc(); 253 254 StretchBlt(hDC, 0 , 0 , m_nCx, m_nCy, m_hDCGlobal, 0 , 0 , m_nCx, m_nCy, SRCCOPY); 255 256 ReleaseDC(pDC); 257 258 259 } 260 261 BOOL CJietu::OnInitDialog() 262 { 263 CDialog::OnInitDialog(); 264 265 // TODO: 在此添加额外的初始化; 266 267 // 改变指针形状为十字星; 268 SetCursor(AfxGetApp()-> LoadStandardCursor(IDC_CROSS)); 269 270 271 CWnd *pDesktop = GetDesktopWindow(); 272 HWND hWndDesktop = pDesktop-> m_hWnd; 273 CDC *pDCDesktop = pDesktop-> GetDC(); 274 HDC hDCDesktop = pDCDesktop-> GetSafeHdc(); 275 276 HWND hWnd = m_hWnd; 277 CWnd *pWnd = FromHandle(hWnd); 278 CDC *pDC = pWnd-> GetDC(); 279 HDC hDC = pDC-> GetSafeHdc(); 280 281 HDC hDCMem = CreateCompatibleDC(hDCDesktop); // 兼容DC; 282 HBITMAP hBitmap = CreateCompatibleBitmap(hDCDesktop, m_nCx,m_nCy); // 桌面DC的图片; 283 // SelectObject(hDCMem, hBitmap); 284 StretchBlt(hDCMem, 0 , 0 , m_nCx, m_nCy, hDCDesktop, 0 , 0 , m_nCx, m_nCy, SRCCOPY); // 拷贝进内存DC; 285 286 287 m_dcGlobal.CreateCompatibleDC(pDCDesktop); // 该DC用来写图片文件时使用; 288 m_hDCGlobal = m_dcGlobal.GetSafeHdc(); // 该HDC用来写图片文件时使用; 289 290 CBitmap bitmap; 291 bitmap.CreateCompatibleBitmap(pDCDesktop, m_nCx, m_nCy); // 该图片用来贴图; 292 m_bitmapGlobal.CreateCompatibleBitmap(pDCDesktop, m_nCx, m_nCy); // 该图片用来风写图片文件时使用; 293 294 m_dcGlobal.SelectObject(&bitmap); // 该DC用来写图片文件时使用; 295 m_dcGlobal.BitBlt( 0 , 0 , m_nCx, m_nCy, pDCDesktop, 0 , 0 , SRCCOPY); // 该DC用来写图片文件时使用; 296 297 298 pDesktop-> ReleaseDC(pDCDesktop); 299 pWnd-> ReleaseDC(pDC); 300 DeleteDC(hDCMem); 301 DeleteObject(hBitmap); 302 303 304 305 return TRUE; // return TRUE unless you set the focus to a control 306 // 异常: OCX 属性页应返回 FALSE 307 } 308 309
//HDC存为BMP 310 void CJietu::WriteBMPFile(HBITMAP hBitMap, LPTSTR filename, HDC hDC, int cx, int cy) 311 { 312 BITMAP bmp; 313 PBITMAPINFO pbmi; 314 WORD cClrBits; 315 HANDLE hf; // file handle 316 BITMAPFILEHEADER hdr; // bitmap file-header 317 PBITMAPINFOHEADER pbih; // bitmap info-header 318 LPBYTE lpBits; // memory pointer 319 DWORD dwTotal; // total count of bytes 320 DWORD cb; // incremental count of bytes 321 BYTE *hp; // byte pointer 322 DWORD dwTmp; 323 // create the bitmapinfo header information 324 if (!GetObject( hBitMap, sizeof (BITMAP), (LPTSTR)& bmp)) 325 { 326 return ; 327 } 328 // Convert the color format to a count of bits. 329 cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel); 330 if (cClrBits == 1 ) 331 cClrBits = 1 ; 332 else if (cClrBits <= 4 ) 333 cClrBits = 4 ; 334 else if (cClrBits <= 8 ) 335 cClrBits = 8 ; 336 else if (cClrBits <= 16 ) 337 cClrBits = 16 ; 338 else if (cClrBits <= 24 ) 339 cClrBits = 24 ; 340 else cClrBits = 32 ; 341 // Allocate memory for the BITMAPINFO structure. 342 if (cClrBits != 24 ) 343 pbmi = (PBITMAPINFO) LocalAlloc(LPTR, sizeof (BITMAPINFOHEADER) + sizeof (RGBQUAD) * ( 1 << cClrBits)); 344 else 345 pbmi = (PBITMAPINFO) LocalAlloc(LPTR, sizeof (BITMAPINFOHEADER)); 346 // Initialize the fields in the BITMAPINFO structure. 347 pbmi->bmiHeader.biSize = sizeof (BITMAPINFOHEADER); 348 pbmi->bmiHeader.biWidth = cx; // bmp.bmWidth; 349 pbmi->bmiHeader.biHeight = cy; // bmp.bmHeight; 350 pbmi->bmiHeader.biPlanes = bmp.bmPlanes; 351 pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel; 352 if (cClrBits < 24 ) 353 pbmi->bmiHeader.biClrUsed = 1 ; // If the bitmap is not compressed, set the BI_RGB flag. 354 pbmi->bmiHeader.biCompression = BI_RGB; 355 // Compute the number of bytes in the array of color 356 // indices and store the result in biSizeImage. 357 pbmi->bmiHeader.biSizeImage = (pbmi->bmiHeader.biWidth + 7 ) / 8 * pbmi->bmiHeader.biHeight * cClrBits; 358 // Set biClrImportant to 0, indicating that all of the 359 // device colors are important. 360 pbmi->bmiHeader.biClrImportant = 0 ; 361 // now open file and save the data 362 pbih = (PBITMAPINFOHEADER) pbmi; 363 lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih-> biSizeImage); 364 if (! lpBits) 365 { 366 return ; 367 } 368 // Retrieve the color table (RGBQUAD array) and the bits 369 if (!GetDIBits(hDC, HBITMAP(hBitMap), 0 , (WORD) pbih-> biHeight, lpBits, pbmi, DIB_RGB_COLORS)) 370 { 371 return ; 372 } 373 // Create the .BMP file. 374 hf = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, (DWORD) 0 , 375 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 376 (HANDLE) NULL); 377 if (hf == INVALID_HANDLE_VALUE) 378 { 379 return ; 380 } 381 hdr.bfType = 0x4d42 ; // 0x42 = "B" 0x4d = "M" 382 // Compute the size of the entire file. 383 hdr.bfSize = (DWORD) ( sizeof (BITMAPFILEHEADER) + pbih->biSize + pbih-> biClrUsed 384 * sizeof (RGBQUAD) + pbih-> biSizeImage); 385 hdr.bfReserved1 = 0 ; 386 hdr.bfReserved2 = 0 ; 387 // Compute the offset to the array of color indices. 388 hdr.bfOffBits = (DWORD) sizeof (BITMAPFILEHEADER) + 389 pbih->biSize + pbih->biClrUsed * sizeof (RGBQUAD); 390 // Copy the BITMAPFILEHEADER into the .BMP file. 391 if (!WriteFile(hf, (LPVOID) &hdr, sizeof (BITMAPFILEHEADER), (LPDWORD) & dwTmp, NULL)) 392 { 393 return ; 394 } 395 // Copy the BITMAPINFOHEADER and RGBQUAD array into the file. 396 if (!WriteFile(hf, (LPVOID) pbih, sizeof (BITMAPINFOHEADER) + pbih->biClrUsed * sizeof (RGBQUAD), (LPDWORD) & dwTmp, ( NULL))) 397 { 398 return ; 399 } 400 // Copy the array of color indices into the .BMP file. 401 dwTotal = cb = pbih-> biSizeImage; 402 hp = lpBits; 403 if (!WriteFile(hf, (LPSTR) hp, ( int ) cb, (LPDWORD) & dwTmp,NULL)) 404 { 405 return ; 406 } 407 // Close the .BMP file. 408 if (! CloseHandle(hf)) 409 { 410 return ; 411 } 412 // Free memory. 413 GlobalFree((HGLOBAL)lpBits); 414 }
//保存按钮 415 void CJietu::OnBnClickedButton1() 416 { 417 // TODO: 在此添加控件通知处理程序代码; 418 419 CDC dcSave; 420 dcSave.CreateCompatibleDC(& m_dcGlobal); 421 dcSave.SelectObject(& m_bitmapGlobal); 422 423 int x1 = m_pointStart.x; 424 int y1 = m_pointStart.y; 425 426 int x2 = m_pointEnd.x; 427 int y2 = m_pointEnd.y; 428 429 int width = abs(m_pointEnd.x - m_pointStart.x); 430 int height = abs(m_pointEnd.y - m_pointStart.y); 431 432 int xBegin = 0 ; 433 int yBegin = 0 ; 434 435 // 考虑四种画框的不同情况,寻找矩形不同的左上角坐标; 436 if (x2 >= x1 && y2 <= y1) 437 { 438 xBegin = x1; 439 yBegin = y2; 440 } 441 else if (x2 >= x1 && y2 >= y1) 442 { 443 xBegin = x1; 444 yBegin = y1; 445 } 446 else if (x2 <= x1 && y2 >= y1) 447 { 448 xBegin = x2; 449 yBegin = y1; 450 } 451 else 452 { 453 xBegin = x2; 454 yBegin = y2; 455 } 456 457 // 拷贝进dcSave必须从(0,0)开始存储,因为WriteBMPFile指定长宽写的时候就是从(0, 0)开始截取的长宽; 458 // 由于m_hDCGlobal保存的是整个桌面的图片,所以要从指定的坐标开始拷贝; 459 StretchBlt(dcSave, 0 , 0 , width, height, m_hDCGlobal, xBegin, yBegin, width, height, SRCCOPY); 460 CTime time = CTime::GetCurrentTime(); 461 CString strTime; 462 strTime = time.Format( " %Y-%m-%d %H%M%S " ); 463 int len = strTime.GetLength(); 464 TCHAR *pFileName = new TCHAR[len + 1 + 4 ]; 465 lstrcpy(pFileName, strTime.GetBuffer(len)); 466 lstrcat(pFileName, _T( " .bmp " )); 467 468 WriteBMPFile((HBITMAP)m_bitmapGlobal.m_hObject, pFileName, dcSave, width, height); 469 delete [] pFileName; 470 strTime.ReleaseBuffer(len); 471 dcSave.DeleteDC(); 472 473 OnCancel(); 474 }