C++开发截屏小程序,Win32程序,可以显示截屏区域并保存。
上次的流星雨屏幕程序就简单涉及到GDI绘图了,这次简单介绍几个API函数,涉及到GDI的。
GetDC,获取当前创建的窗口的设备环境。
CreateDC,获取当前屏幕的设备环境。
CreateCompatibleDC,创建一个兼容性的设备环境(相当于一个虚拟的设备环境)
BitBlt,这个函数,相当于拷贝,将一个环境的设备内容拷贝到另一个设备中。
CreateCompatibleBitmap,创建一块画布,将其放在兼容性的DC里面,这样就可以在里面画图了,当然还要放入画笔和画刷这些。
介绍完这些函数之后,那么设计思路就来了:
1.首先当然还是定义并创建窗口,还有消息循环。
ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDC_CAPTURESCREEN)); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(BLACK_BRUSH); wcex.lpszMenuName = NULL; wcex.lpszClassName = szWindowClass; wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); return RegisterClassEx(&wcex); } BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { hInst = hInstance; // 将实例句柄存储在全局变量中 //创建自己的窗口 hWnd = CreateWindow(szWindowClass, szTitle, WS_POPUP, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); if (!hWnd) { return FALSE; } //显示和更新窗口 ShowWindow(hWnd, SW_MAXIMIZE); UpdateWindow(hWnd); return TRUE; } int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, _In_ int nCmdShow) { //playsound只能播放wav格式,而mcisendstring可以播放任意格式的。 //PlaySound("yixi.wav", NULL, SND_FILENAME | SND_ASYNC | SND_LOOP); mciSendString("open ./abc.mp3 alias bk", 0, 0, 0); mciSendString("play bk repeat", 0, 0, 0); UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); // TODO: 在此放置代码。 MSG msg; HACCEL hAccelTable; // 初始化全局字符串 LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); LoadString(hInstance, IDC_CAPTURESCREEN, szWindowClass, MAX_LOADSTRING); MyRegisterClass(hInstance); //注册类 // 执行应用程序初始化: if (!InitInstance(hInstance, nCmdShow)) //初始化窗口 { return FALSE; } hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_CAPTURESCREEN)); // 主消息循环: while (GetMessage(&msg, NULL, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return (int)msg.wParam; }
2.之后获取当前屏幕的设备环境,
3.然后将它保存到兼容性的DC中,这就相当于将当前屏幕图片放到一个缓冲区中。在WM_CREATE
消息里面做这个动作。
void ScreenDisplay() { HDC disDc = ::CreateDC("DISPLAY", NULL, NULL, NULL); g_memDC = ::CreateCompatibleDC(disDc); g_ScreenW = GetDeviceCaps(disDc, HORZRES); g_ScreenH = GetDeviceCaps(disDc, VERTRES); HBITMAP hbitmap = CreateCompatibleBitmap(disDc, g_ScreenW, g_ScreenH); SelectObject(g_memDC, hbitmap); BitBlt(g_memDC, 0, 0, g_ScreenW, g_ScreenH, disDc, 0, 0, SRCCOPY); }
4.接着再将它放到我们创建的窗口中,这时就会看到整个桌面就不动了,就呈现的是一张图片,
5.之后我们就可以在这张图片上绘制我们想截取的区域。
6.呈现的是静止的图片,如果绘制之后,需要更新,这就用到一个函数InvalidateRgn
,会无效选定的区域,这样会触发消息WM_PAINT,所以在这个消息里面将重新绘制图形,然后显示即可。
case WM_PAINT: hdc = BeginPaint(hWnd, &ps); // TODO: 在此添加任意绘图代码... SelectObject(hdc, hpen); SelectObject(hdc, hBrush); BitBlt(hdc, 0, 0, g_ScreenW, g_ScreenH, g_memDC, 0, 0, SRCCOPY); Rectangle(hdc, rect.left, rect.top, rect.right, rect.bottom); EndPaint(hWnd, &ps); break;
接下来就是绘制想要区域的操作,需要用到的几个鼠标的消息函数,鼠标按下,鼠标弹起,鼠标移动,鼠标双击。
那么思路来了:
鼠标按下,确定左上角的点,然后鼠标移动绘制矩形区域,然后鼠标弹起,确定右下角的点,这样矩形区域绘制完成。
case WM_LBUTTONDOWN: { if (!Iselect) { POINT pt; GetCursorPos(&pt); rect.left = pt.x; rect.top = pt.y; rect.right = pt.x; rect.bottom = pt.x; InvalidateRgn(hWnd, 0, FALSE); Isdowmn = TRUE; } } break; case WM_LBUTTONUP: { if (Isdowmn == TRUE&&!Iselect) { POINT pt; GetCursorPos(&pt); rect.right = pt.x; rect.bottom = pt.y; InvalidateRgn(hWnd, 0, FALSE); Isdowmn = FALSE; Iselect = TRUE; } } break; case WM_MOUSEMOVE: { if (Isdowmn == TRUE&&!Iselect) { POINT pt; GetCursorPos(&pt); rect.right = pt.x; rect.bottom = pt.y; InvalidateRgn(hWnd, 0, FALSE); } } break;
最后鼠标双击将截取到的图片保存剪切板,这样就完成了屏幕截取。
case WM_LBUTTONDBLCLK: if (Iselect == TRUE) { int iNum = MessageBox(hWnd, "截图成功!", "张一西", MB_OKCANCEL | MB_ICONINFORMATION); if (iNum == 1) { CopyToCliboard(); Iselect = FALSE; PostQuitMessage(0); } else { Iselect = FALSE; } } break;
void CopyToCliboard() { HDC hScreenDC = ::CreateDC("DISPLAY", 0, 0, 0); HDC memDC = ::CreateCompatibleDC(hScreenDC); int Width = rect.right - rect.left-2; int Height = rect.bottom - rect.top-2; HBITMAP hBmap = CreateCompatibleBitmap(hScreenDC, Width, Height); HBITMAP hOldBmap = (HBITMAP)SelectObject(memDC, hBmap); BitBlt(memDC, 0, 0, Width, Height, hScreenDC, rect.left+1, rect.top+1, SRCCOPY); HBITMAP hNewBmap = (HBITMAP)SelectObject(memDC, hOldBmap); if (OpenClipboard(0)) //打开粘贴板 { EmptyClipboard(); //清空粘贴板 SetClipboardData(CF_BITMAP, hNewBmap); //把图片放入粘贴板 CloseClipboard(); //关闭粘贴板 } }