Win32 SDK 编写截图小工具

开发环境为windows xp,vc6

主要方案为,创建一个主窗口,不进行截图的时候将其隐藏,并在托盘区创建小图标,截图时使窗口全屏透明,捕捉鼠标行为截图保存,再使窗口隐藏。

以下根据主要步骤将核心内容点出:


(1)注册窗口类时,将背景色定为黑色,以使截图时有灰暗的效果


ATOM MyRegisterClass(PWNDCLASSEX wcex,HINSTANCE hInstance)
{
	...
	wcex->hCursor		= LoadCursor(NULL, IDC_CROSS);
	wcex->hbrBackground	= (HBRUSH)GetStockObject(BLACK_BRUSH);
	...

	return RegisterClassEx(wcex);
}

(2)创建窗口时,用到两个扩展风格,WS_EX_LAYERED,WS_EX_TOOLWINDOW

前者是将窗口透明化必须设置的窗口风格,后者使窗口成为一个工具窗口,不显示在任务栏中。

HWND hWnd = CreateWindowEx(WS_EX_LAYERED | WS_EX_TOOLWINDOW, "MYSCREENSHOT", 0, WS_POPUP,
		CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

(3)注册两套热键,用RegisterHotKey

函数原型为:

BOOL RegisterHotKey(
  HWND hWnd,         // handle to window
  int id,            // hot key identifier
  UINT fsModifiers,  // key-modifier options
  UINT vk            // virtual-key code
);

    RegisterHotKey(hWnd, 1346, MOD_ALT | MOD_CONTROL, 65);
    RegisterHotKey(hWnd, 1347, MOD_CONTROL, 222);
65为A的虚键码,222为' or " 的虚键码,适用于左手与右手。

这样当按下这两个组合键时,窗口会受到一个    WM_HOTKEY    消息,其中,lParam的低位字显示了alt或是control键是否被按下,高位字为虚键码,那么我们只需要在窗口函数中处理该消息,发送一个命令消息开始截图:

	case WM_HOTKEY:
		{
			if(HIWORD(lParam) == 65 || HIWORD(lParam) == 222)
				SendMessage(hWnd, WM_COMMAND, MAKEWPARAM(IDM_RUN, 0), 0);
		}
		break;


IDM_RUN 是一个开始截图的命令消息

注意窗口销毁时注销热键,在WM_DESTROY中

			UnregisterHotKey(hWnd, 1346);
			UnregisterHotKey(hWnd, 1347);

(4)在不进行截图时,将窗口最小化到托盘,这里要用到 API函数Shell_NotifyIcon和结构体NOTIFYICONDATA,先来看下NOTIFYICONDATA:

typedef struct _NOTIFYICONDATA { 
DWORD cbSize; 
HWND hWnd; 
UINT uID; 
UINT uFlags; 
UINT uCallbackMessage; 
HICON hIcon; 
char szTip[64]; 
} NOTIFYICONDATA, *PNOTIFYICONDATA; 
其中,

cbSize : 结构的长度,用“位”来做单位。一般在程序中,我们用(DWORD)sizeof(NOTIFYICONDATA) 给它赋值。
HWnd : 一个句柄,如果对托盘中的图标进行操作,相应的消息就传给这个句柄所代表的窗口。自然了,大多数情况下是this->m_hWnd喽。
uID : 在工程中定义的图标ID 


uFlags : 这个成员标志着其他哪些成员的数据是有效的,分别为NIF_ICON, NIF_MESSAGE, NIF_TIP,分别代表着数据有效的成员是hIcon, uCallbackMessage, szTip。当然,三个值可以用“|”联系到一起。下面分别对涉及到的成员进行阐述 


hIcon : 要增加,删除或修改的图标句柄。如果只知道个uID, 一般可能会用函数LoadIcon来得到句柄。例如LoadIcon ( AfxGetInstanceHandle() ,MAKEINTRESOURCE (IDR_MAINFRAME) )。

uCallbackMessage : 这在对托盘区的操作中,是比较重要的数据成员。这是个消息标志,当用鼠标对托盘区相应图标进行操作的时候,就会传递消息给Hwnd所代表的窗口。所以说,在uFlags中,一般都得标志它有效。这里一般都是自定义的消息。

szTip : 鼠标移动到托盘图标上时的提示文字。

我们这里用一个自定义消息来接收托盘图标的消息:

#define MY_ICON_MESSAGE (WM_USER+1)
...
NOTIFYICONDATA g_nid;
...
g_nid.uCallbackMessage = MY_ICON_MESSAGE;

那么此时,第三个参数可以随便填。接下来处理这个消息,其中,wParam接收的是图标的ID,而lParam接收的是鼠标的行为

	case MY_ICON_MESSAGE:
		{
			switch(lParam)
			{
			case WM_RBUTTONDOWN:
				{
					HMENU hMenu = CreateMenu();
					HMENU hPopupMenu = CreateMenu();
					AppendMenu(hMenu, MF_POPUP, (UINT_PTR)hPopupMenu, 0);
					AppendMenu(hPopupMenu, MF_STRING, IDM_RUN, "&Run");
					AppendMenu(hPopupMenu, MF_SEPARATOR, 0, 0);
					AppendMenu(hPopupMenu, MF_STRING, IDM_QUIT, "&Quit");
					POINT point;
					GetCursorPos(&point);
					SetForegroundWindow(hWnd);
					TrackPopupMenu(hPopupMenu, TPM_LEFTALIGN, point.x, point.y, 0, hWnd, 0);
				}
				break;

			case WM_LBUTTONDBLCLK:
				SendMessage(hWnd, WM_COMMAND, MAKEWPARAM(IDM_RUN, 0), 0);
				break;
			}
		}
		break;


右键单击时,弹出一个菜单,有两个选项,运行和退出,当左键双击时,与运行截图有相同的作用,发送一个IDM_RUN 的命令消息。注意,TrackPopupMenu之前的SetForegroundWindow是必要的,没有这一句,则弹出的菜单不会自动消失。


(5)接下去来处理IDM_RUN消息:

			case IDM_RUN:
				{
					g_nid.hIcon = LoadIcon(g_hInstance, (LPCTSTR)IDI_ON); 
					Shell_NotifyIcon(NIM_MODIFY, &g_nid);
				
					hSrcDC = GetDC(GetDesktopWindow());

					HMODULE hModule = LoadLibrary("User32.dll");
					lpfnSetLayeredWindowAttributes myfunc = (lpfnSetLayeredWindowAttributes)GetProcAddress(hModule, "SetLayeredWindowAttributes"); 
					FreeLibrary(hModule);
					ShowWindow(hWnd, SW_MAXIMIZE);
					myfunc(hWnd, 0, 100, LWA_ALPHA);

					g_State = TRUE;
				}
				break;

首先是一个替换托盘区图标的操作,显示截图正在进行中。

然后先取得桌面DC,(先把全屏给截了,然后再跟踪鼠标的操作来对这张全屏图“剪裁”)hSrcDC是一个窗口消息处理函数的静态局部DC句柄。

接着利用API函数SetLayeredWindowAttributes来设置窗口为透明,其函数原型为:

BOOL SetLayeredWindowAttributes(
  HWND hwnd,           // handle to the layered window
  COLORREF crKey,      // specifies the color key
  BYTE bAlpha,         // value for the blend function
  DWORD dwFlags        // action
);

详细的解答请参照MSDN

这里需要申明对该函数的指针,注意,LWA_ALPHA需要自行定义

typedef	BOOL (WINAPI *lpfnSetLayeredWindowAttributes)(HWND,COLORREF,BYTE,DWORD);
...
#define   LWA_ALPHA		0x00000002

g_State 是一个全局变量,用以记录是否在截图状态,在鼠标的消息处理函数中会对此变量进行判断,是的话再进行相应的操作。


(6)接下去跟踪鼠标的行为“剪裁”刚才的桌面DC,先找到需要的矩形块坐标。

主要过程为:

1.在WM_LBUTTONDOWN中记录第一个点,将它保存在静态POINT变量中;

2.在WM_MOUSEMOVE中在透明窗口上实时刷新并画线以显示截图的区域;

3.在WM_LBUTTONUP中记录第二个点,从而取得需要剪裁的区域矩形,用BitBlt复制DC内容,并保存位图文件。最后使窗口隐藏起来,更换托盘区图标等初始化工作。


具体代码在此不再罗嗦了,详见源码。


成品exe及源代码:点击打开链接


原文地址:https://www.cnblogs.com/silyvin/p/9106906.html