开发环境为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 是一个开始截图的命令消息
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及源代码:点击打开链接