1. 程式人生 > >C++專案 18-11-21-截圖工具

C++專案 18-11-21-截圖工具

1.整體

1.開發工具

vs2012

2.語言

C++

2.瞭解帶視窗的應用程式

1.做一個帶視窗的應用程式

Win32:就是Windows API(函式)
MFC :類庫,對Windows API的一種封裝

3.編寫程式

1.建立Win32專案(不選中建立空專案)

2._tWinMain 是程式的入口函式

3.函式: MyRegisterClass()

註冊視窗類,視窗的樣子由該函式決定
wcex.lpfnWndProc = WndProc; // 視窗過程函式

4.函式: InitInstance(HINSTANCE, int)

目的: 儲存例項控制代碼並建立主視窗(用於建立視窗)
註釋: 在此函式中,我們在全域性變數中儲存例項控制代碼並建立和顯示主程式視窗。
其中有 CreateWindow(類名稱,視窗的標題,視窗彈出的樣式,。。。。。。),本次改變視窗彈出的方式為WS_POPUP

5.顯示視窗

ShowWindow(hWnd, nCmdShow);				// 第二個引數表示視窗的大小樣式,可以修改為SW_MAXMIZE
UpdateWindow(hWnd);

6.CALLBACK 函式

系統呼叫,當有訊息時就呼叫該函式。

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

	switch (message)			// message 是訊息ID,用於表示滑鼠左鍵點選、鍵盤按鍵等等訊息
{ // 裡面用於處理一些感興趣的訊息 case WM_LBUTTONDOWN: MessageBox(hWnd, L"左鍵按下", L"info", MB_OK); break; case WM_COMMAND: wmId = LOWORD(wParam); wmEvent = HIWORD(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_PAINT: // 螢幕繪製訊息 hdc = BeginPaint(hWnd, &ps); // TODO: 在此新增任意繪圖程式碼... EndPaint(hWnd, &ps); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; }

4.設計思想流程

0.讓視窗全屏

1步.hWnd = CreateWindow(szWindowClass, szTitle, WS_POPUP, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
2步.ShowWindow(hWnd, nCmdShow); // 第二個引數表示視窗的大小樣式,可以修改為SW_MAXMIZE
3步.wcex.lpszMenuName = MAKEINTRESOURCE(IDC_MY1121); // 在視窗上不顯示選單欄

1.需要獲取整個影象

1.首先建立一個 ScreenCapture 函式,用來儲存獲取到的影象
1.首先建立一個桌面的 DC; HDC dis_dc = ::CreateDC(L"DISPLAY", 0, 0, 0); // 建立一個DC ,DISPLAY 表示桌面,與桌面相關
2.獲取整個桌面的大小(解析度);
screen_w = GetDeviceCaps(dis_dc, HORZRES); // 整個桌面水平的大小
screen_h = GetDeviceCaps(dis_dc, VERTRES); // 整個桌面垂直的大小
3.獲取桌面截圖;
g_src_men_dc = CreateCompatibleDC(dis_dc); // 建立一個相容性的dc(記憶體DC),與桌面的dc相關的
4.模擬一張畫布出來;
HBITMAP h_bitmap = CreateCompatibleBitmap(dis_dc, screen_w, screen_h);
5.將畫布選入dc
SelectObject(g_src_men_dc, h_bitmap);
6.將桌面畫到畫布上
BitBlt(g_src_men_dc, 0, 0, screen_w, screen_h, dis_dc, 0, 0, SRCCOPY); // 整個桌面已經被畫到了mem_dc中

2.將影象放到視窗上

1.需要進入 CALLBACK 函式,在 WM_PAINT 訊息中進行“搬運”截圖到視窗的工作;
2.將整個桌面放到了全屏的視窗上面
BitBlt(hdc, 0, 0, screen_w, screen_h, g_src_men_dc, 0, 0, SRCCOPY);
3.在進行建立視窗的時候就進行 ScreenCapture 函式的呼叫(即建立截圖的影象)

3.在視窗上畫出矩形

1.需要通過滑鼠左鍵按下進行記錄矩形的左和上座標,通過滑鼠左鍵彈起記錄矩形的右和下座標,因此定義一個全域性的 RECT 型別的變數 rect,RECT 是一個結構體
2.需要在 WM_PAINT 訊息裡進行繪製矩形,繪製矩形使用的時函式 Rectangle ,引數分別為 hdc、矩形的左、上、右、下座標,一共有五個引數。
3.當完成此步驟後,可以在按下滑鼠左鍵後到鬆開後在視窗上畫出白底的矩形框;
4.改變矩形框的顏色,使其透明,需要在 CALLBACK 函式中定義一個新的畫刷,使用 LOGBRUSH 宣告一個新的畫刷,並定義 brush.lbStyle = BS_NULL,在定義一個畫刷控制代碼 HBRUSH h_brush = CreateBrushIndirect(&brush),就可生成透明的畫刷;
5.改變完畫刷為透明之後,在 WM_PAINT 訊息中進行選擇畫刷,是宣告的透明的畫刷被選中,使用 SelectObject 函式進行選擇畫刷。
6.當前步驟完成後可以進行透明矩形的繪製,但是矩形的邊框仍為黑色,若想改變矩形邊框的顏色,需要進行 筆 的建立,
使用 LOGPEN pen 建立一個筆,
之後使用 POINT pt 來儲存矩形邊框的粗細,設定 pt.x = pt.y = 2,即設定矩形邊框為2個畫素的粗細,
pen.lopnColor = 0x0000FFFF 表示選擇矩形的邊框為 綠色(0000FFFF 是綠色的十六進位制表示),
pen.lopnStyle = PS_SOLID 表示繪製矩形的邊框為 實線,
pen.lopnWidth = pt 表示繪製的矩形的邊框的粗細為 2個畫素,
生成筆的控制代碼:HPEN h_pen = CreatePenIndirect(&pen);
最後在 WM_PAINT 訊息處理下選擇筆,使用 SelectObject(hdc, h_pen) 來選擇筆,進行繪製自定義顏色、粗細、邊框風格的矩形。
7.在進行完第6步之後,可以得到 綠色、實線、透明的矩形。

4.儲存畫出來的矩形

1.新增視窗可以處理滑鼠雙擊的樣式,需要在 註冊視窗類 在設定視窗時對視窗的樣式(style)進行設定,新增一條 CS_DBLCLKS 樣式。
2.在 CALLBACK 的訊息處理中新增 WM_LBUTTONDBLCLK 滑鼠雙擊訊息檢測機制,用來複制選中區域;
3.編寫複製滑鼠選中區域的函式 CopyBitmap2CipBoarD ,
1.首先需要複製矩形框內的內容,計算矩形框的寬和高;
2.獲取桌面 dc:HDC h_scr_dc = ::CreateDC(L"DISPLAY", 0, 0, 0);
3.建立記憶體 dc:HDC mem_dc = CreateCompatibleDC(h_scr_dc);
4.模擬畫布:HBITMAP bmp = CreateCompatibleBitmap(h_scr_dc, width, height);
5.將畫布選入筆中:HBITMAP old_map = (HBITMAP)SelectObject(mem_dc, bmp);
6.將矩形畫到記憶體dc中:BitBlt(mem_dc, 0, 0, width, height, h_scr_dc, rect.left, rect.top, SRCCOPY);
7.將舊的畫布放到新的畫布中:HBITMAP new_map = (HBITMAP)SelectObject(mem_dc, old_map);
8.將 new_map 複製到貼上板中:SetClipboardData(CF_BITMAP, new_map);
4.在滑鼠雙擊訊息處理中呼叫 CopyBitmap2CipBoarD,但是需要注意,滑鼠左鍵雙擊會包含單擊的過程,因此需要對單擊和雙擊進行區分,我們使用 雙擊 的操作是在選中矩形之後,因此可以定義一個全域性的變數來記錄是否選中了矩形:BOOL is_select = FALSE,該值預設為假;當我們未選中矩形的時候執行 單擊 操作,並將 is_select 修改為真;當我們選中矩形後執行 雙擊 操作,並修改 is_select 的值為假;
5.在 4 過程中,我們需要進行 滑鼠左鍵是否按下 的檢測,當滑鼠左鍵按下並且矩形未被選中的時候,我們才可以進行矩形區域的選擇,進而雙擊的時候進行矩形的複製(將矩形區域複製到貼上板上),因此需要引入一個全域性變數記錄是否滑鼠按鍵以按下:BOOL is_down = FALSE,預設值為假,當滑鼠左鍵單擊時修改為真,當選中矩形後修改為假;
6.5步執行完之後可以將選中的矩形框複製進剪貼簿當中;

5.基本完成,需要改進的地方

1.沒有被選中的地方需要變灰
2.被選中的地方應該變亮
3.最好是視窗不要出現,以快捷鍵的方式來做(全域性的鉤子)(也就是新增快捷鍵)
4.滑鼠移動的時候,矩形框能夠跟著變化
5.加上一個托盤(就是紅蜘蛛一直存在的地方的圖示)

5.創建出來的空 Win32 應用程式(建立時不選中 空專案)

// 1111.cpp : 定義應用程式的入口點。
//

#include "stdafx.h"
#include "1111.h"

#define MAX_LOADSTRING 100

// 全域性變數:
HINSTANCE hInst;								// 當前例項
TCHAR szTitle[MAX_LOADSTRING];					// 標題欄文字
TCHAR szWindowClass[MAX_LOADSTRING];			// 主視窗類名

// 此程式碼模組中包含的函式的前向宣告:
ATOM				MyRegisterClass(HINSTANCE hInstance);
BOOL				InitInstance(HINSTANCE, int);
LRESULT CALLBACK	WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK	About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPTSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);

 	// TODO: 在此放置程式碼。
	MSG msg;
	HACCEL hAccelTable;

	// 初始化全域性字串
	LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
	LoadString(hInstance, IDC_MY1111, szWindowClass, MAX_LOADSTRING);
	MyRegisterClass(hInstance);

	// 執行應用程式初始化:
	if (!InitInstance (hInstance, nCmdShow))
	{
		return FALSE;
	}

	hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_MY1111));

	// 主訊息迴圈:
	while (GetMessage(&msg, NULL, 0, 0))
	{
		if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}

	return (int) msg.wParam;
}



//
//  函式: MyRegisterClass()
//
//  目的: 註冊視窗類。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX);

	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MY1111));
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= MAKEINTRESOURCE(IDC_MY1111);
	wcex.lpszClassName	= szWindowClass;
	wcex.hIconSm		= LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

	return RegisterClassEx(&wcex);
}

//
//   函式: InitInstance(HINSTANCE, int)
//
//   目的: 儲存例項控制代碼並建立主視窗
//
//   註釋:
//
//        在此函式中,我們在全域性變數中儲存例項控制代碼並
//        建立和顯示主程式視窗。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;

   hInst = hInstance; // 將例項控制代碼儲存在全域性變數中

   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

//
//  函式: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  目的: 處理主視窗的訊息。
//
//  WM_COMMAND	- 處理應用程式選單
//  WM_PAINT	- 繪製主視窗
//  WM_DESTROY	- 傳送退出訊息並返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	PAINTSTRUCT ps;
	HDC hdc;

	switch (message)
	{
	case WM_COMMAND:
		wmId    = LOWORD(wParam);
		wmEvent = HIWORD(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_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		// TODO: 在此新增任意繪圖程式碼...
		EndPaint(hWnd, &ps);
		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;
}

6.截圖工具程式

// 11-21-截圖工具.cpp : 定義應用程式的入口點。
//

#include "stdafx.h"
#include "11-21-截圖工具.h"

#define MAX_LOADSTRING 100

// 全域性變數:
HINSTANCE hInst;								// 當前例項
TCHAR szTitle[MAX_LOADSTRING];					// 標題欄文字
TCHAR szWindowClass[MAX_LOADSTRING];			// 主視窗類名


// 源桌面截圖
HDC g_src_men_dc;
int screen_w;		// 螢幕的寬
int screen_h;		// 螢幕的高
void ScreenCapture();

void CopyBitmap2CipBoarD();		// 將矩形拷貝到貼上板中
// 但是由於雙擊和單擊滑鼠會產生衝突,因此需要一個記錄是否選中矩形的 BOOL型 變數
BOOL is_select = FALSE;		// 當單擊滑鼠彈起時即為選中矩形
// 檢測是否已經按下
BOOL is_down = FALSE;

RECT rect;			// 需要畫矩形時記錄矩形的長和寬,RECT是一個結構體




// 此程式碼模組中包含的函式的前向宣告:
ATOM				MyRegisterClass(HINSTANCE hInstance);
BOOL				InitInstance(HINSTANCE, int);
LRESULT CALLBACK	WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK	About(HWND, UINT, WPARAM, LPARAM);


// 入口函式
int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPTSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);

 	// TODO: 在此放置程式碼。
	MSG msg;
	HACCEL hAccelTable;

	// 初始化全域性字串
	LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
	LoadString(hInstance, IDC_MY1121, szWindowClass, MAX_LOADSTRING);
	MyRegisterClass(hInstance);

	// 執行應用程式初始化:
	if (!InitInstance (hInstance, nCmdShow))
	{
		return FALSE;
	}

	hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_MY1121));

	// 主訊息迴圈:
	while (GetMessage(&msg, NULL, 0, 0))
	{
		if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}

	return (int) msg.wParam;
}



//
//  函式: MyRegisterClass()
//
//  目的: 註冊視窗類。			// 視窗的樣子由該函式決定
//
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(IDI_MY1121));
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= 0; //MAKEINTRESOURCE(IDC_MY1121);
	wcex.lpszClassName	= szWindowClass;
	wcex.hIconSm		= LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

	return RegisterClassEx(&wcex);
}

//
//   函式: InitInstance(HINSTANCE, int)
//
//   目的: 儲存例項控制代碼並建立主視窗
//
//   註釋:
//
//        在此函式中,我們在全域性變數中儲存例項控制代碼並
//        建立和顯示主程式視窗。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;

   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;
}

//
//  函式: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  目的: 處理主視窗的訊息。
//
//  WM_COMMAND	- 處理應用程式選單
//  WM_PAINT	- 繪製主視窗
//  WM_DESTROY	- 傳送退出訊息並返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	PAINTSTRUCT ps;
	HDC hdc;

	
	// 定義一個透明的畫刷
	LOGBRUSH brush;
	brush.lbStyle = BS_NULL;
	HBRUSH h_brush = CreateBrushIndirect(&brush);

	// 定義一個筆
	LOGPEN pen;
	POINT pt;
	pt.x = 2;
	pt.y = 2;
	pen.lopnColor = 0x0000FFFF;			// 綠色的 十六進位制表示
	pen.lopnStyle = PS_SOLID;			// 畫的矩形邊框為 實線
	pen.lopnWidth = pt;					// 矩形邊框的寬度為 2個畫素
	HPEN h_pen = CreatePenIndirect(&pen);

	switch (message)			// 訊息ID
	{

	// 裡面用於處理一些感興趣的訊息
	
	// 需要用到 WM_LBUTTONDOWN、WM_LBUTTONUP、以及 WM_MOUSEMOVE 這三個訊息進行繪製矩形
	case WM_LBUTTONDOWN:
		{
			if(!is_select)			// 沒有選中矩形的時候才允許選中矩形,若已經有選中的矩形,則不允許再次選中矩形
			{
				POINT pt;
				GetCursorPos(&pt);			// 得到當前游標的位置
				rect.left = pt.x;
				rect.top = pt.y;
				rect.right = pt.x;
				rect.bottom = pt.y;
				InvalidateRgn(hWnd, 0, true);	// 重新整理螢幕

				is_down = TRUE;
			}
		}
		break;

	case WM_LBUTTONUP:
		{
			if(is_down && !is_select)
			{
				POINT pt;
				GetCursorPos(&pt);			// 得到當前游標的位置
				rect.right = pt.x;
				rect.bottom = pt.y;
				InvalidateRgn(hWnd, 0, true);	// 重新整理螢幕

				is_down = FALSE;
				is_select = TRUE;			// 彈起時表示區域已經選中
			}
		}
		break;

	case WM_MOUSEMOVE:
		break;

	case WM_LBUTTONDBLCLK:			// 滑鼠雙擊檢測機制,用來複制選中區域
		if(is_select)
		{
			CopyBitmap2CipBoarD();
			is_select = FALSE;
		}
		
		break;



	case WM_CREATE:
		ScreenCapture();		// 繪出矩形
		break;

	case WM_COMMAND:
		wmId    = LOWORD(wParam);
		wmEvent = HIWORD(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_PAINT:				// 螢幕繪製訊息
		hdc = BeginPaint(hWnd, &ps);				// 代表當前視窗
		// TODO: 在此新增任意繪圖程式碼...


		// 選擇透明的畫刷
		SelectObject(hdc, h_brush);
		// 選擇綠色為矩形的邊框
		SelectObject(hdc, h_pen);

		// 將整個桌面放到了全屏的視窗上面
		BitBlt(hdc, 0, 0, screen_w, screen_h, g_src_men_dc