【API函数】 注册全局系统热键_捕获鼠标位置窗口控件_获取窗口控件的截图

本文将介绍几个WindowsAPI函数的使用,WindowFromPoint获取鼠标位置窗口控件,GetWindowRect、GetClientRect获取控件区域,用屏幕画图标记出区域,通过RegisterHotKey注册全局类系统热键操作窗口控件的截图。

C#.NET代码如下:(点击下载完整源代码)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace Hotkey_Screenshot
{
    public partial class Form1 : Form
    {
        #region API引用区域
        /// <summary>  
        /// 获得窗体大小,包括客户区和非客户区,非客户区包括标题及感知鼠标靠近的区域(鼠标在这区域时显示调节窗口大小的光标)
        /// </summary>  
        /// <param name="hwnd">窗体句柄</param>  
        /// <param name="lpRect">存入矩形对象(相对屏幕的左上角坐标及右下角坐标)</param>  
        /// <returns></returns>  
        [DllImport("user32.dll", EntryPoint = "GetWindowRect")]
        public static extern int GetWindowRect(IntPtr hwnd, ref RECT lpRect);
        /// <summary>  
        /// 获得窗体客户区域大小,不包括非客户区
        /// </summary>  
        /// <param name="hwnd">窗体句柄</param>  
        /// <param name="lpRect">存入矩形对象(相对窗体的左上角坐标及右下角坐标)</param>  
        /// <returns></returns>  
        [DllImport("user32.dll", EntryPoint = "GetClientRect")]
        public static extern int GetClientRect(IntPtr hwnd, ref RECT lpRect);
        /// <summary>
        /// 将控件的相对坐标转换为屏幕坐标
        /// </summary>
        /// <param name="hWnd">控件句柄</param>
        /// <param name="lpPoint">引用坐标参数:使用前用控件相对坐标赋值,转换成功则变成屏幕坐标</param>
        /// <returns>是否成功</returns>
        [DllImport("user32.dll")]
        public static extern bool ClientToScreen(IntPtr hWnd, ref Point lpPoint);
        /// <summary>  
        /// 通过全屏幕坐标获取控件句柄,不获取隐藏或禁止的窗口句柄
        /// </summary>  
        /// <param name="p">屏幕坐标</param>  
        /// <returns>返回值为包含该点的窗口的句柄。如果包含指定点的窗口不存在,返回值为NULL。如果该点在静态文本控件之上,返回值是在该静态文本控件的下面的窗口的句柄。</returns>  
        [DllImport("user32.dll")]
        public static extern IntPtr WindowFromPoint(Point p);
        [DllImport("gdi32.dll")]
        public static extern bool BitBlt(IntPtr hObject, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hObjectSource, int nXSrc, int nYSrc, int dwRop);
        [DllImport("gdi32.dll")]
        public static extern IntPtr CreateCompatibleBitmap(IntPtr hDC, int nWidth, int nHeight);
        /// <summary>
        /// 创建一个与指定设备兼容的内存设备上下文环境(DC)。通过GetDc()获取的HDC直接与相关设备沟通,而本函数创建的DC,则是与内存中的一个表面相关联。
        /// </summary>
        /// <param name="hDC"></param>
        /// <returns></returns>
        [DllImport("gdi32.dll")]
        public static extern IntPtr CreateCompatibleDC(IntPtr hDC);
        [DllImport("gdi32.dll")]
        public static extern bool DeleteDC(IntPtr hDC);
        [DllImport("gdi32.dll")]
        public static extern bool DeleteObject(IntPtr hObject);
        /// <summary>
        /// 该函数选择一对象到指定的设备上下文环境中,该新对象替换先前的相同类型的对象
        /// </summary>
        /// <param name="hDC"></param>
        /// <param name="hObject"></param>
        /// <returns></returns>
        [DllImport("gdi32.dll")]
        public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
        [DllImport("user32.dll")]
        public static extern IntPtr GetDesktopWindow();
        [DllImport("user32.dll")]
        public static extern IntPtr GetWindowDC(IntPtr hWnd);
        [DllImport("user32.dll")]
        public static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDC);

        [DllImport("User32.dll")]
        public extern static System.IntPtr GetDC(System.IntPtr hWnd);
        
        public struct RECT
        {
            /// <summary>
            /// 左上角横坐标
            /// </summary>
            public int X;
            /// <summary>
            /// 左上角纵坐标
            /// </summary>
            public int Y;
            /// <summary>
            /// 右下角横坐标
            /// </summary>
            public int X2;
            /// <summary>
            /// 右下角纵坐标
            /// </summary>
            public int Y2;
        };
        /// <summary>
        /// BitBlt函数参数:将源矩形区域直接拷贝到目标矩形区域
        /// </summary>
        public const int SRCCOPY = 0x00CC0020;

        /// <summary>
        /// 注册全局热键(需要重写消息处理函数WndProc处理WM_HOTKEY = 0x0312消息)
        /// </summary>
        /// <param name="hWnd">处理热键消息的窗体句柄</param>
        /// <param name="id">热键标识符:应用程序范围0X0000-0xBFFF,共享的动态链接库范围0xC000-0xFFFF,若在程序内注册同一ID值,则旧的热键被覆盖</param>
        /// <param name="fsModifiers">组合键类型</param>
        /// <param name="vk">热键按键的虚拟码</param>
        /// <returns></returns>
        [DllImportAttribute("user32.dll")]        
        public static extern bool RegisterHotKey(IntPtr hWnd, int id, KeyModifiers fsModifiers, Keys vk);
        /// <summary>
        /// 释放之前注册的全局热键
        /// </summary>
        /// <param name="hWnd">窗体句柄,同RegisterHotKey的hWnd</param>
        /// <param name="id">热键标识符,同RegisterHotKey的id</param>
        /// <returns></returns>
        [DllImportAttribute("user32.dll")]        
        public static extern bool UnregisterHotKey(IntPtr hWnd, int id);
        public enum KeyModifiers //组合键枚举

        {
            None = 0,
            Alt = 1,
            Control = 2,
            Shift = 4,
            Windows = 8
        }

        [DllImport("user32")]
        public static extern bool InvalidateRect(IntPtr hWnd, ref RECT lpRect, bool bErase);
        [DllImport("user32.dll")]
        public static extern bool UpdateWindow(IntPtr hWnd);
        [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
        public static extern bool RedrawWindow(IntPtr hwnd, ref RECT rcUpdate, IntPtr hrgnUpdate, int flags);
        #endregion

        /// <summary>
        /// 是否标出控件边框
        /// </summary>
        bool showRect = false;
        /// <summary>
        /// 鼠标最后停留位置的控件句柄
        /// </summary>
        IntPtr lastControl = IntPtr.Zero;
        public Form1()
        {
            InitializeComponent();
            this.FormClosing += Form1_FormClosing;
        }
        private void Form1_Load(object sender, EventArgs e)
        {
            label1.Text = "\r\n\r\n"; 
            label1.Text += "    热键使用说明:\r\n\r\n";
            label1.Text += "    (ALT+D) 全屏幕截图\r\n\r\n";
            label1.Text += "    (ALT+S) 显示鼠标停留位置的控件区域\r\n\r\n";
            label1.Text += "    (ALT+F) 鼠标停留位置的控件所在区域的屏幕截图\r\n\r\n";
            label1.Text += "    (ALT+C) 鼠标停留位置的控件客户区截图";
            bool r=RegisterHotKey(this.Handle, 100, KeyModifiers.Alt, Keys.D);
            r = RegisterHotKey(this.Handle, 101, KeyModifiers.Alt, Keys.S);
            r = RegisterHotKey(this.Handle, 102, KeyModifiers.Alt, Keys.F);
            r = RegisterHotKey(this.Handle, 103, KeyModifiers.Alt, Keys.C);

            BackgroundWorker bw = new BackgroundWorker();
            bw.DoWork += Bw_DoWork;
            bw.ProgressChanged += Bw_ProgressChanged;
            bw.WorkerReportsProgress = true;
            bw.RunWorkerAsync();
        }

        private void Bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            
        }

        private void Bw_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker me = sender as BackgroundWorker;            
            while (true)
            {
                IntPtr curControl = WindowFromPoint(Cursor.Position);//获取鼠标位置的控件句柄
                if (curControl != lastControl)
                {
                    if (showRect)
                    {
                        //RECT rt = new RECT();
                        //GetWindowRect(lastControl, ref rt);//获取上个控件在屏幕中的区域
                        //ClearRect(IntPtr.Zero,rt);//清除上个控件的矩形标记
                        //System.Threading.Thread.Sleep(100);//等待刷新完成再画框,否则框会被清除
                        //GetWindowRect(curControl, ref rt);//获取当前控件在屏幕中的区域
                        //DrawingRect(new Point(rt.X, rt.Y), rt.X2 - rt.X, rt.Y2 - rt.Y);//标记矩形框
                        ClearRect(lastControl);
                        System.Threading.Thread.Sleep(100);//等待刷新完成再画框,否则框会被清除
                        DrawingRect(curControl);
                    }
                    lastControl = curControl;
                }
                System.Threading.Thread.Sleep(200);//每隔200毫秒刷新一次鼠标位置的控件句柄
            }
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            UnregisterHotKey(this.Handle, 100);
            UnregisterHotKey(this.Handle, 101);
            UnregisterHotKey(this.Handle, 102);
            UnregisterHotKey(this.Handle, 103);
        }
        protected override void WndProc(ref Message m)
        {
            const int WM_HOTKEY = 0x0312;            
            if (m.Msg== WM_HOTKEY)
            {
                string fileName = System.IO.Path.GetTempPath() + "\\"+DateTime.Now.Ticks.ToString()+".jpg";
                Bitmap bmp;
                switch (m.WParam.ToInt32())
                {
                    case 100:
                        bmp=ShotScreen();//全屏幕截图
                        bmp.Save(fileName);
                        bmp.Dispose();
                        System.Diagnostics.Process.Start(fileName);
                        break;
                    case 101:
                        if (showRect)
                            showRect = false;
                        else
                            showRect = true;
                        break;
                    case 102:
                        bmp = CaptureWindow(lastControl);//获取控件所在区域的屏幕图像,如果控件上方有其它窗体遮盖,则显示遮盖的图像
                        bmp.Save(fileName);
                        bmp.Dispose();
                        System.Diagnostics.Process.Start(fileName);
                        break;
                    case 103:
                        bmp = CaptureClientWindow(lastControl);//获取控件的显示图像,如果控件上方有其它窗体遮盖不受影响,但无法获取标题部分图像
                        bmp.Save(fileName);
                        bmp.Dispose();
                        System.Diagnostics.Process.Start(fileName);
                        break;
                    default:
                        break;
                }
            }
            base.WndProc(ref m);
        }

        /// <summary>  
        /// 桌面截图,效果等同ShotScreen
        /// </summary>  
        /// <returns></returns>  
        public static Bitmap CaptureDesktop()
        {
            return CaptureClientWindow(GetDesktopWindow());
        }
        /// <summary>  
        /// 屏幕截图,效果等同ShotScreen
        /// </summary>  
        /// <returns></returns>  
        public static Bitmap CaptureScreen()
        {
            return CaptureClientScreen(GetDesktopWindow());
        }
        /// <summary>  
        /// 屏幕截图,效果等同CaptureScreen
        /// </summary>  
        /// <returns></returns>  
        public static Bitmap ShotScreen()
        {
            Graphics g_Screen = Graphics.FromHwnd(IntPtr.Zero);//从屏幕创建Graphics对象
            Bitmap bitmap = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, g_Screen);//创建位图            
            Graphics g_Bitmap = Graphics.FromImage(bitmap);//创建位图关联的Graphics对象            
            IntPtr sDC = g_Screen.GetHdc();//获取屏幕DC            
            IntPtr bmpDC = g_Bitmap.GetHdc();//获取位图DC
            BitBlt(bmpDC, 0, 0, bitmap.Width, bitmap.Height, sDC, 0, 0, SRCCOPY);//拷贝图像
            g_Bitmap.ReleaseHdc(bmpDC);
            g_Screen.ReleaseHdc(sDC);
            g_Bitmap.Dispose();
            g_Screen.Dispose();
            return bitmap;
        }

        /// <summary>  
        /// 通过窗体句柄获取此窗体区域的屏幕图像(如果窗体上方有其它窗体遮盖,获取的是遮盖后的图像)  
        /// </summary>  
        /// <param name="hwnd"></param>  
        /// <returns></returns>  
        public static Bitmap CaptureClientScreen(IntPtr hwnd)
        {
            IntPtr sDC = GetDC(IntPtr.Zero);//获取屏幕DC        
            RECT windowRect = new RECT();
            GetWindowRect(hwnd, ref windowRect);//获取控件在屏幕中的矩形坐标(包含非客户区)
            int width = windowRect.X2 - windowRect.X;
            int height = windowRect.Y2 - windowRect.Y;           
            IntPtr dDC = CreateCompatibleDC(sDC);           
            IntPtr bmpHandle = CreateCompatibleBitmap(sDC, width, height);           
            IntPtr oldObj = SelectObject(dDC, bmpHandle);//替换成功后返回被替换的对象句柄          
            BitBlt(dDC, 0, 0, width, height, sDC, windowRect.X, windowRect.Y, SRCCOPY);//拷贝坐标位置图像
            SelectObject(dDC, oldObj);//将被替换的对象句柄重新复位回去           
            DeleteDC(dDC);
            ReleaseDC(IntPtr.Zero, sDC);            
            Image img = Image.FromHbitmap(bmpHandle);           
            DeleteObject(bmpHandle);
            return (Bitmap)img;
        }
        /// <summary>  
        /// 通过窗体句柄获取此窗体的客户区图像(非客户区如标题部分无法获取)
        /// </summary>  
        /// <param name="hwnd">窗体句柄</param>  
        /// <returns></returns>  
        public static Bitmap CaptureClientWindow(IntPtr hwnd)
        {
            IntPtr sDC = GetDC(hwnd);//获取控件DC
            RECT clientRect = new RECT();
            GetClientRect(hwnd, ref clientRect);//获取控件在客户区的矩形坐标,左上角一般都是Point(0,0)
            int width = clientRect.X2 - clientRect.X;
            int height = clientRect.Y2 - clientRect.Y;            
            IntPtr dDC = CreateCompatibleDC(sDC);//由控件DC创建内存DC            
            IntPtr bmpHandle = CreateCompatibleBitmap(sDC, width, height);//创建与指定的设备环境相关的设备兼容的位图           
            IntPtr oldObj = SelectObject(dDC, bmpHandle);//替换同类对象,替换成功后返回被替换的对象句柄           
            BitBlt(dDC, 0, 0, width, height, sDC, clientRect.X, clientRect.Y, SRCCOPY);//拷贝控件图像           
            SelectObject(dDC, oldObj);//将原来被替换的对象重新复位回去           
            DeleteDC(dDC);
            ReleaseDC(hwnd, sDC);            
            Image img = Image.FromHbitmap(bmpHandle);           
            DeleteObject(bmpHandle);
            return (Bitmap)img;
        }
        /// <summary>  
        /// 通过窗体句柄获取此窗体的图像(细节处理优化)
        /// </summary>  
        /// <param name="hwnd">窗体句柄</param>  
        /// <returns></returns>  
        public static Bitmap CaptureWindow(IntPtr hwnd)
        {
            IntPtr sDC = GetDC(hwnd);//获取控件DC
            RECT clientRect = new RECT();
            GetClientRect(hwnd, ref clientRect);//获取控件在客户区的矩形坐标,左上角一般都是Point(0,0)
            int width = clientRect.X2 - clientRect.X;
            int height = clientRect.Y2 - clientRect.Y;
            IntPtr dDC = CreateCompatibleDC(sDC);//由控件DC创建内存DC            
            IntPtr bmpHandle = CreateCompatibleBitmap(sDC, width, height);//创建与指定的设备环境相关的设备兼容的位图           
            IntPtr oldObj = SelectObject(dDC, bmpHandle);//替换同类对象,替换成功后返回被替换的对象句柄           
            BitBlt(dDC, 0, 0, width, height, sDC, clientRect.X, clientRect.Y, SRCCOPY);//拷贝控件图像           
            SelectObject(dDC, oldObj);//将原来被替换的对象重新复位回去           
            DeleteDC(dDC);
            ReleaseDC(hwnd, sDC);
            Image imgClient = Image.FromHbitmap(bmpHandle);
            DeleteObject(bmpHandle);

            Point p_client1 = new Point(clientRect.X, clientRect.Y);
            Point p_client2 = new Point(clientRect.X2, clientRect.Y2);
            ClientToScreen(hwnd, ref p_client1);
            ClientToScreen(hwnd, ref p_client2);
            RECT windowRect = new RECT();
            GetWindowRect(hwnd, ref windowRect);//获取控件在屏幕中的矩形坐标(包含非客户区)
            if (windowRect.X == p_client1.X && windowRect.Y == p_client1.Y)//如果客户区就是完整的区域返回客户区图像
                return (Bitmap)imgClient;

            sDC = GetDC(IntPtr.Zero);//获取屏幕DC
            width = p_client2.X - p_client1.X;//标题部分的宽度
            height = p_client1.Y - windowRect.Y;//标题部分的高度
            dDC = CreateCompatibleDC(sDC);
            bmpHandle = CreateCompatibleBitmap(sDC, width, height);
            oldObj = SelectObject(dDC, bmpHandle);         
            BitBlt(dDC, 0, 0, width, height, sDC, p_client1.X, windowRect.Y, SRCCOPY);//拷贝标题区域的屏幕图像
            SelectObject(dDC, oldObj);           
            DeleteDC(dDC);
            ReleaseDC(IntPtr.Zero, sDC);
            Image imgTitle = Image.FromHbitmap(bmpHandle);
            DeleteObject(bmpHandle);

            Bitmap bmp = new Bitmap(imgClient.Width,imgClient.Height+imgTitle.Height);//创建Bitmap对象,尺寸高度加了标题
            Graphics g = Graphics.FromImage(bmp);
            g.DrawImage(imgTitle, new Point(0, 0));//先将标题填充进图像
            g.DrawImage(imgClient, new Point(0, imgTitle.Height));//然后往下将客户区部分填充进图像
            g.Dispose();
            return bmp;//返回拼接好的图像
        }
        /// <summary>
        /// 在屏幕指定位置画个红色矩形框
        /// </summary>
        /// <param name="p">相对屏幕的左上角坐标</param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        public static void DrawingRect(Point p, int width, int height)
        {
            IntPtr dc = GetDC(IntPtr.Zero);
            System.Drawing.Graphics g = System.Drawing.Graphics.FromHdc(dc);
            g.DrawRectangle(new System.Drawing.Pen(System.Drawing.Brushes.Red, 2), new System.Drawing.Rectangle(p.X, p.Y, width, height));
            ReleaseDC(IntPtr.Zero, dc);
        }
        /// <summary>
        /// 在控件客户区的指定区域画红色矩形框
        /// </summary>
        /// <param name="hwnd">控件句柄</param>
        /// <param name="p">相对控件的左上角坐标</param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        public static void DrawingRect(IntPtr hwnd, Point p, int width, int height)
        {
            IntPtr dc = GetDC(hwnd);
            System.Drawing.Graphics g = System.Drawing.Graphics.FromHdc(dc);
            g.DrawRectangle(new System.Drawing.Pen(System.Drawing.Brushes.Red, 2), new System.Drawing.Rectangle(p.X, p.Y, width, height));
            ReleaseDC(hwnd, dc);
        }
        /// <summary>
        /// 用红色矩形框标出控件区域(经过细节处理)
        /// </summary>
        /// <param name="hwnd"></param>
        public static void DrawingRect(IntPtr hwnd)
        {
            RECT rt_window = new RECT();
            GetWindowRect(hwnd, ref rt_window);//获取控件在屏幕中的矩形坐标(包含非客户区)
            RECT rt_client = new RECT();
            GetClientRect(hwnd, ref rt_client);//获取控件的客户区区域
            Point p_client1 = new Point(rt_client.X, rt_client.Y);//客户区左上角坐标
            Point p_client2 = new Point(rt_client.X2, rt_client.Y2);//客户区右下角坐标
            ClientToScreen(hwnd, ref p_client1);//客户区左上角坐标转化为屏幕坐标
            ClientToScreen(hwnd, ref p_client2);//客户区右下角坐标转化为屏幕坐标
            int width = p_client2.X-p_client1.X;
            int height = p_client2.Y - rt_window.Y;//这里的高度包含了标题部分
            Point p = new Point(p_client1.X, rt_window.Y);//这里的坐标是包含标题的左上角位置
            DrawingRect(p, width, height);
        }
        /// <summary>
        /// 通知控件重绘,清除所画红色框
        /// </summary>
        /// <param name="hWnd">控件句柄</param>
        public static void ClearRect(IntPtr hWnd,RECT rect)
        {            
            InvalidateRect(hWnd, ref rect, true);
            //UpdateWindow(hWnd);
            //const int RDW_FRAME = 1024;
            //const int RDW_INVALIDATE = 1;
            //const int RDW_UPDATENOW = 256;
            //const int RDW_ALLCHILDREN = 128;
            //RedrawWindow(hWnd, ref rect, IntPtr.Zero, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);
        }
        /// <summary>
        /// 通知控件重绘,对应DrawingRect(IntPtr hwnd)使用
        /// </summary>
        /// <param name="hWnd">控件句柄</param>
        public static void ClearRect(IntPtr hwnd)
        {
            RECT rt_window = new RECT();
            GetWindowRect(hwnd, ref rt_window);//获取控件在屏幕中的矩形坐标(包含非客户区)
            RECT rt_client = new RECT();
            GetClientRect(hwnd, ref rt_client);//获取控件的客户区区域
            Point p_client1 = new Point(rt_client.X, rt_client.Y);//客户区左上角坐标
            Point p_client2 = new Point(rt_client.X2, rt_client.Y2);//客户区右下角坐标
            ClientToScreen(hwnd, ref p_client1);//客户区左上角坐标转化为屏幕坐标
            ClientToScreen(hwnd, ref p_client2);//客户区右下角坐标转化为屏幕坐标
            RECT rect = new RECT();
            rect.X= p_client1.X;
            rect.Y= rt_window.Y;//这里的坐标包含了标题部分
            rect.X2 = p_client2.X;
            rect.Y2 = p_client2.Y;
            InvalidateRect(IntPtr.Zero, ref rect, true);           
            //UpdateWindow(IntPtr.Zero);
            //const int RDW_FRAME = 1024;
            //const int RDW_INVALIDATE = 1;
            //const int RDW_UPDATENOW = 256;
            //const int RDW_ALLCHILDREN = 128;
            //RedrawWindow(IntPtr.Zero, ref rect, IntPtr.Zero, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);
        }
    }
}

【源码下载】

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lzl_li

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值