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

