Fork me on GitHub

如何用C语言写一个简易的截屏程序

某天当我写完了鼠标连点器之后,鹏哥问我能不能写个截屏工具出来

然而事情没这么简单,随后鹏哥向我展示了他声称找了很久的截屏小工具:当时我的表情: > _ < 这..我可不包UI设计啊,还有我只是个刚学会一点C皮毛的纯新手…只好回去百度我需要的相关接口了

思路

截取屏幕,怎么截,截哪里,怎么存,存哪里,都是问题
于是一脸纠结的我理所当然的选择了最好存的图片格式:Bitmap位图,就是.bmp格式的图片,它的特征特别明显:容易存容易读,就是占用空间大。
然后就是怎么截,不管怎么截,我产生一个全屏幕窗口设备描述表的兼容位图总是没错的,你要截全屏我就都存到文件,你要截一部分我就在上面划那个一部分给你再存也就完事了
至于截哪里的问题,我本来想模仿qq截图来着,但是人家是按下快捷键后已经截取了全屏,然后让你用鼠标操作…(你懂得,我这菜鸟哪懂这样要怎么实现嘛),那我想,那我就退而求其次。利用鼠标的实时位置以及按键触发来确定你的截图起始位置,这样也算是能够以相对简单的方式实现了(我想你应该不会想用直接输入你想从哪个像素开始到哪个像素结束的方式来截一个图吧)。
存到哪里这个问题倒是好解决,我在c盘下创建一个专门存放截屏的目录,然后对每个截屏都取一个独一无二的名字
这样就解决了存哪里的问题

源文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <wtypes.h>
#include <tchar.h>
#include<math.h>
#include<time.h>
//#include<afx.h>
//默认储存路径
char path[260] = "c:/shots/";
//设置文件名称
char* setname(char* p)
{
//strcat(p, "-");
//strcat(p, __DATE__);
char * ret = p;
while (*p)
{
if (*p == ':' || *p == ' '||*p == '\n')*p = '-';
p++;
}
*(p - 1) = 0;
strcat(ret, ".bmp");
return ret;
}
//设置打印位置
void CaptureScreen(int x1, int y1, int x2, int y2)
{
time_t lt = time(NULL);
char p[100] = {0};
char name[100] = {0};
int t = 0;
strcpy(p, ctime(&lt));
setname(p);
sprintf(name, "%s%s",path,p);
printf("保存至:\n%s",name);
//system("pause");
if (x2 < x1)
{
t = x2; x2 = x1; x1 = t;
}
if (y2 < y1)
{
t = y2; y2 = y1; y1 = t;
}
//获得桌面窗口句柄
HWND hDesktopWnd = GetDesktopWindow();
//获得桌面窗口设备环境
HDC hDesktopDC = GetDC(hDesktopWnd);
//产生全屏幕窗口设备描述表的兼容设备环境
HDC hCaptureDC = CreateCompatibleDC(hDesktopDC);
//产生全屏幕窗口设备描述表的兼容位图
HBITMAP hCaptureBitmap = CreateCompatibleBitmap(hDesktopDC, x2, y2);
printf("\n宽度:%d\n高度:%d\n", x2 - x1, y2 - y1);
//system("pause");
//将兼容位图选入兼容设备环境
SelectObject(hCaptureDC, hCaptureBitmap);
//将全屏幕窗口位图的象素数据拷贝到兼容设备描述表
BitBlt(hCaptureDC, 0, 0, x2-x1, y2-y1, hDesktopDC, x1, y1, SRCCOPY);
BITMAPINFO bi;
void *pBits = NULL;
ZeroMemory(&bi, sizeof(bi));
bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
bi.bmiHeader.biHeight = (y2 - y1);
bi.bmiHeader.biWidth = (x2 - x1);
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = 24;
bi.bmiHeader.biCompression = BI_RGB;
bi.bmiHeader.biSizeImage = 3 * (y2 - y1)*(x2 - x1);
//产生位图兼容设备描述表
//DECLARE_HANDLE(HDC);
HDC hBmpFileDC = CreateCompatibleDC(hDesktopDC);
//产生位图兼容位图
HBITMAP hBmpFileBitmap = CreateDIBSection(hDesktopDC, &bi, DIB_RGB_COLORS, &pBits, NULL, 0);
//将兼容位图选入兼设备描述表容设备描述表
SelectObject(hBmpFileDC, hBmpFileBitmap);
//将捕获的位图的象素拷贝到位图设备环境
BitBlt(hBmpFileDC, 0, 0, x2 - x1, y2 - y1, hCaptureDC, 0, 0, SRCCOPY);
//创建文件 typedef void * HANDLE;
HANDLE hFile = CreateFile(name, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
//保存位图文件
if (hFile != INVALID_HANDLE_VALUE)
{
DWORD dwRet = 0;
BITMAPFILEHEADER bmfHeader;
ZeroMemory(&bmfHeader, sizeof(bmfHeader));
bmfHeader.bfOffBits = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
bmfHeader.bfSize = bi.bmiHeader.biSizeImage + bmfHeader.bfOffBits;
bmfHeader.bfType = 'MB';
//写入位图信息头
WriteFile(hFile, &bmfHeader, sizeof(bmfHeader), &dwRet, NULL);
//写入位图尺寸颜色表等信息
WriteFile(hFile, &bi.bmiHeader, sizeof(bi.bmiHeader), &dwRet, NULL);
//写入位图数据块
WriteFile(hFile, pBits, bi.bmiHeader.biSizeImage, &dwRet, NULL);
//剪切板操作,当前有兼容性问题,故注释,正在解决
////写入剪切板
//if (OpenClipboard(NULL) && EmptyClipboard())
//{
// SetClipboardData(CF_BITMAP, hFile);
//}
//CloseClipboard();
CloseHandle(hFile);
}
DeleteDC(hBmpFileDC);
DeleteObject(hBmpFileBitmap);
ReleaseDC(hDesktopWnd, hDesktopDC);
DeleteDC(hCaptureDC);
DeleteObject(hCaptureBitmap);
}
void Pos(int x, int y)
{
COORD pos;
HANDLE hOutput;
pos.X = x;
pos.Y = y;
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(hOutput, pos);
}
void printpos(int *x, int* y,int z)
{
long zx = -1;
long zy = -1;
POINT ptB = { 0, 0 };

//typedef struct tagPOINT
//{
//LONG x;
//LONG y;
//} POINT, * PPOINT, NEAR * NPPOINT, FAR * LPPOINT;

while (1)
{
LPPOINT xy = &ptB; //位置变量
GetCursorPos(xy); //获取鼠标当前位置
//如果鼠标移动,(即当前的坐标改变则打印出坐标)打印出做坐标。
if ((zx != xy->x) || (zy != xy->y))
{
Pos(0, z);
printf("x=%4d,y=%4d\n", xy->x*1920/1536, xy->y*1080/864);
zx = xy->x;
zy = xy->y;
}
if (GetAsyncKeyState('Q')&&GetAsyncKeyState( VK_CONTROL))
{
ShowWindow(FindWindow(NULL, _T("shot")), SW_MINIMIZE);
Sleep(500);
CaptureScreen(0, 0, 1920, 1080);
break;
}
if (GetAsyncKeyState(VK_SPACE))
{
//若打印出来的坐标超过1920 1080请将下面的乘除操作去掉
*x = xy->x*1920/1536;
*y = xy->y*1080/864;
break;
}
}
}
char * change(char*p)
{
char*ret = p;
while (*p)
{
if (*p == '/')
*p = '\\';
p++;
}
if (*(p - 1) != '\\')strcat(p-1, "\\");
return ret;
}
int main()
{
int i = 0;
int x1, x2, y1, y2;
char c = 0;
char cpath[270] = { 0 };
//改变窗口名称,方便进行最小化
system("title shot");
//修改窗口大小
system("mode con cols=40 lines=10");
printf("是否改变储存路径?(y/n)");
scanf("%c", &c);
if (c == 'y')
{
system("cls");
printf("请输入新路径:\n$ ");
scanf("%s", path);
change(path);
}
sprintf(cpath, "md %s", path);
//创建相应路径,如果已经存在也没事
system(cpath);
system("cls");
Pos(0, 9);
printf("按下Ctrl+Q截取全屏\n按下Ctrl+W进入截屏");
while (1)
{
Pos(0, 0);
if (GetAsyncKeyState('Q') && GetAsyncKeyState(VK_CONTROL))
{
i = 0;
while (i < 8)
{
Pos(0, i++);
printf(" ");
}
Pos(0, 0);
ShowWindow(FindWindow(NULL, _T("shot")), SW_MINIMIZE);
Sleep(500);
CaptureScreen(0, 0, 1920, 1080);
}
if (GetAsyncKeyState(VK_CONTROL) && GetAsyncKeyState('W'))
{
i = 0;
while (i < 8)
{
Pos(0, i++);
printf(" ");
}
Pos(0, 0);
printf("按下空格选取截图起点\n");
printpos(&x1, &y1, 1);
Sleep(300);
printf("按下空格选取截图终点\n");
printpos(&x2, &y2, 3);
CaptureScreen(x1, y1, x2, y2);
i = 0;

//if (GetAsyncKeyState(VK_ESCAPE))
//break;
}
}
return 0;
}

这样我们的截屏工具就做好了,它在前台运行时使用截全屏会自动隐藏,这样不会截到自己(就不会挡住一些东西)当然也可以后台运行,你只要记住快捷键就好了!
刚进入程序时不想改变默认路径可以直接回车,想改变的话就按提示操作吧~

-------------本文结束感谢您的阅读-------------
坚持原创技术分享,您的支持将鼓励我继续创作!