关于截图工具,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 }