屏幕截图小软件的编写——WINDOWS API

近期在学习windows api编程,完全模仿的学习了,熟悉windows api和纯windows api编程

屏幕截图步骤:

1.如何捕获屏幕

2.捕获屏幕后怎么做到选定区域

3.选定区域后将图片写入剪贴板

具体还有很多细节需要考虑,如选定区域需要用不同颜色的画笔,如何知道区域被选择了......

主要代码如下,核心都在窗口回调函数中,不详细解释,几个重要的API函数在后面说明

//函数前置声明
void CaptureScreen();
void WriteDateToClipBoard();
// 全局变量: 
HINSTANCE hInst;                                // 当前实例
WCHAR szTitle[MAX_LOADSTRING];                  // 标题栏文本
WCHAR szWindowClass[MAX_LOADSTRING];            // 主窗口类名

int screenW, screenH;
HDC g_memDC;					//全局内存DC,存储位图
RECT rect;						//截图矩形区域
bool isSelect=FALSE, isDrawing=FALSE;

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	PAINTSTRUCT ps;
	HDC hdc;

	//画刷
	LOGBRUSH brush;
	brush.lbStyle = BS_NULL;
	HBRUSH hBrush = CreateBrushIndirect(&brush);
	//画笔
	LOGPEN pen;
	POINT penWidth;
	penWidth.x = 2;
	penWidth.y = 2;
	pen.lopnColor = 0x0000FFFF;
	pen.lopnStyle = BS_SOLID;
	pen.lopnWidth = penWidth;
	HPEN hPen = CreatePenIndirect(&pen);

	//窗口信息
	WINDOWINFO windowInfo;
	windowInfo.cbSize = sizeof(WINDOWINFO);
	

    switch (message)
    {
    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            // 分析菜单选择: 
            switch (wmId)
            {
            case IDM_ABOUT:
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                break;
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;

	case WM_CREATE:
		CaptureScreen();
		break;

	
	case WM_PAINT:
        {
            
            hdc = BeginPaint(hWnd, &ps);
			
            // TODO: 在此处添加使用 hdc 的任何绘图代码...
			HDC memDC = CreateCompatibleDC(hdc);
			HBITMAP hbmp = CreateCompatibleBitmap(hdc, screenW, screenH);
			SelectObject(memDC, hbmp);

			BitBlt(memDC, 0, 0, screenW, screenH, g_memDC, 0, 0, SRCCOPY);
			SelectObject(memDC, hPen);
			SelectObject(memDC, hBrush);

			if (isDrawing || isSelect)
			{
				BitBlt(memDC, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, g_memDC, rect.left, rect.top, SRCCOPY);
				Rectangle(memDC, rect.left, rect.top, rect.right, rect.bottom);
			}

			BitBlt(hdc, 0, 0, screenW, screenH, memDC, 0, 0, SRCCOPY);

			DeleteObject(hbmp);
			DeleteObject(memDC);

            EndPaint(hWnd, &ps);
        }
        break;

	case WM_LBUTTONDOWN:
		if (!isSelect)
		{
			POINT point;
			GetCursorPos(&point);
			//获取矩形区域起点
			rect.top = point.y;
			rect.left = point.x;
			rect.right = point.x;
			rect.bottom = point.y;

			isDrawing = TRUE;
			InvalidateRgn(hWnd, NULL, FALSE);
		}
		
		break;

	case WM_LBUTTONUP:
		if (isDrawing&&!isSelect)
		{
			isDrawing = FALSE;
			POINT point;
			GetCursorPos(&point);
			//获取矩形区域终点
			rect.right = point.x;
			rect.bottom = point.y;

			isSelect = TRUE;
			
			InvalidateRgn(hWnd, NULL, FALSE);
		}
		break;

	case WM_LBUTTONDBLCLK:
		if (isSelect)
		{
			WriteDateToClipBoard();
			InvalidateRgn(hWnd, NULL, FALSE);
			ShowWindow(hWnd, SW_MINIMIZE);
		}

		isSelect = FALSE;
		isDrawing = FALSE;
		
		break;

	case WM_MOUSEMOVE:
		//鼠标移动实时捕获鼠标位置
		if (isDrawing&&!isSelect) 
		{
			POINT pt;
			GetCursorPos(&pt);
			rect.right = pt.x;
			rect.bottom = pt.y;
			InvalidateRgn(hWnd, 0, false);
		}

		break;

    case WM_DESTROY:
        PostQuitMessage(0);
        break;

    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

// “关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}

void CaptureScreen()
{
	HDC disDC = ::CreateDC(L"DISPLAY", 0, 0, 0);//截取整个屏幕
	screenW = GetDeviceCaps(disDC, HORZRES);
	screenH = GetDeviceCaps(disDC, VERTRES);

	//创建与DC相兼容的内存DC和位图
	g_memDC = CreateCompatibleDC(disDC);
	HBITMAP hbmp = CreateCompatibleBitmap(disDC, screenW, screenH);
	SelectObject(g_memDC, hbmp);
	BitBlt(g_memDC, 0, 0, screenW, screenH, disDC, 0, 0, SRCCOPY);

	DeleteDC(disDC);
	DeleteObject(hbmp);
}

void WriteDateToClipBoard()
{
	HDC hMemDC, hdc;
	HBITMAP hMemBmp, hbmp;
	//计算矩形区域的长和宽
	int width, height;
	width = rect.right - rect.left;
	height = rect.bottom - rect.top;

	//创建相兼容的内存DC和位图
	hdc = CreateDC(L"DISPLAY", 0, 0, 0);
	hMemDC = CreateCompatibleDC(hdc);
	hbmp = CreateCompatibleBitmap(hdc, width, height);

	hMemBmp = (HBITMAP)SelectObject(hMemDC, hbmp);
	BitBlt(hMemDC, 0, 0, width, height, g_memDC, rect.left, rect.bottom, SRCCOPY);
	hbmp = (HBITMAP)SelectObject(hMemDC, hMemBmp);

	DeleteDC(hdc);
	DeleteDC(hMemDC);

	//清空,写入,关闭剪贴板 
	if (OpenClipboard(0))
	{
		EmptyClipboard();
		SetClipboardData(CF_BITMAP, hbmp);
		CloseClipboard();
	}

	DeleteObject(hbmp);
	DeleteObject(hMemBmp);
}
窗口风格要是POPUP的,这样才能覆盖整个屏幕
DC,即device context,学会使用DC和内存DC传送位图,虽然还不是很理解,先抄,模仿别人的......

按我现在的理解,DC相当于一个容器,可以将画刷画笔等工具放入其中.....


SelectObject函数说明,摘录自MSDN

HGDIOBJ SelectObject(
  _In_ HDC     hdc,
  _In_ HGDIOBJ hgdiobj
);

Parameters

hdc [in]

A handle to the DC.

hgdiobj [in]

A handle to the object to be selected.

参数只能是这几个GDI对象Bitmap ,Brush ,Font,Pen,Region

初次接触windows api编程,所有API 都不熟悉,完全copy加模仿,此程序运行后只能截图一次

以后接触多了后再来写,改进思想如下:

1.能够对屏幕进行实时截图,即软件运行时可以在任意时刻对屏幕截图

2.能够撤销之前截图




版权声明:本文为ProgrammeringLearner原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。