• 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及源代码:点击打开链接


  • 相关阅读:
    WPF线程中获取控件的值和给控件赋值
    sublime text3 安装以及主要插件安装
    云服务IaaS,PaaS,SaaS
    What is JSON
    Core Java
    英语单词及语义
    设置PyCharm创建文件时自动添加头文件
    【练习】字典的循环遍历:实现多层级节点存取
    字符串格式化
    常用数据类型的方法--str、int、list、dict
  • 原文地址:https://www.cnblogs.com/silyvin/p/9106906.html
Copyright © 2020-2023  润新知