[转]C#制作QQ截图的自动框选功能的个人思路(三)<自动框选>

 效果图:

 

[csharp] view plain copy
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.ComponentModel;  
  4. using System.Data;  
  5. using System.Drawing;  
  6. using System.Linq;  
  7. using System.Text;  
  8. using System.Windows.Forms;  
  9.   
  10. using System.Threading;  
  11. using System.Runtime.InteropServices;  
  12.   
  13. namespace AutoDrawRect  
  14. {  
  15.     public partial class Form2 : Form  
  16.     {  
  17.         [DllImport("user32.dll")]//获取桌面的句柄  
  18.         public static extern IntPtr GetDesktopWindow();  
  19.         [DllImport("user32.dll")]//在桌面找寻子窗体  
  20.         public static extern IntPtr ChildWindowFromPointEx(IntPtr pHwnd, Point pt, uint uFlgs);  
  21.         private const int CWP_SKIPDISABLED = 0x2;   //忽略不可用窗体  
  22.         private const int CWP_SKIPINVISIBL = 0x1;   //忽略隐藏的窗体  
  23.         private const int CWP_All = 0x0;    //一个都不忽略  
  24.           
  25.         [DllImport("user32.dll")]//获得句柄对象的位置  
  26.         public static extern bool GetWindowRect(IntPtr hWnd, out LPRECT lpRect);  
  27.         public struct LPRECT {//位置信息的结构体  
  28.             public int left;  
  29.             public int top;  
  30.             public int right;  
  31.             public int bottom;  
  32.         }  
  33.   
  34.         [DllImport("user32.dll")]//进行坐标转换 (再窗体内部进行查找)  
  35.         public static extern bool ScreenToClient(IntPtr hWnd, out LPPOINT lpPoint);  
  36.         public struct LPPOINT {//要转换的坐标信息的结构体  
  37.             public int x;  
  38.             public int y;  
  39.         }  
  40.   
  41.         public Form2() {  
  42.             InitializeComponent();  
  43.             this.FormBorderStyle = FormBorderStyle.None;  
  44.             this.Location = new Point(0, 0);  
  45.             this.Size = new Size(Screen.PrimaryScreen.Bounds.Width,  
  46.                 Screen.PrimaryScreen.Bounds.Height);  
  47.             pictureBox1.Dock = DockStyle.Fill;  
  48.         }  
  49.   
  50.         ~Form2() {//窗体关闭时卸载Hook  
  51.             mHook.UnLoadMouseHook();  
  52.         }  
  53.   
  54.         Bitmap screenBmp;   //保存全屏的图像  
  55.         bool isDrawed;      //是否已经画出了一个区域  
  56.         bool isDraw;        //是否允许绘制  
  57.         IntPtr hWnd;        //保存相应对象的句柄  
  58.         int sx, sy;         //鼠标点下时的鼠标坐标  
  59.           
  60.         MouseHook mHook = new MouseHook();//创建一个Hook  
  61.           
  62.         private void Form2_Load(object sender, EventArgs e) {  
  63.             screenBmp = GetScreen();  
  64.             pictureBox1.Image = GetScreen();  
  65.             using (Graphics g = Graphics.FromImage(pictureBox1.Image)) {  
  66.                 SolidBrush sb = new SolidBrush(Color.FromArgb(125, 0, 0, 0));  
  67.                 g.FillRectangle(sb, 0, 0, this.Width, this.Height);  
  68.                 sb.Dispose();  
  69.             }  
  70.             this.Enabled = false;       //禁用此窗体(这样在查找的时候才能把当前窗体忽略)  
  71.             mHook.HooKMouseEvent +=new MouseHook.MEventhandler(mHook_HooKMouseEvent);  
  72.             mHook.SetMouseHook();       //启用Hook  
  73.         }  
  74.   
  75.         public Bitmap GetScreen() {//获得全屏图像  
  76.             Bitmap bmp = new Bitmap(this.Width, this.Height);  
  77.             using (Graphics g = Graphics.FromImage(bmp)) {  
  78.                 g.CopyFromScreen(0, 0, 0, 0, this.Size);  
  79.             }  
  80.             return bmp;  
  81.         }  
  82.           
  83.         private void mHook_HooKMouseEvent(object sender,MouseInfoEventArys e) {//绑定到Hook上面的事件  
  84.             if (e.MBtn == Btn.LeftDowm) {//判断左键是否点下  
  85.                 this.Enabled = true;    //点下的时候恢复窗体的禁用  
  86.                 sx = MousePosition.X;   //记录下当前鼠标位置  
  87.                 sy = MousePosition.Y;  
  88.                 DrawRect("Normal");     //然后把当前区域绘制出来  
  89.                 isDrawed = true;        //已经绘制  
  90.                 isDraw = true;          //允许绘制  
  91.                 return;//返回  
  92.             } else if(e.MBtn == Btn.RightUp) {//如果右键抬起  
  93.                 if (isDrawed) {             //判断是否已经绘制 已经绘制取消绘制  
  94.                     hWnd = IntPtr.Zero;     //句柄清空 重新找寻  
  95.                     isDrawed = false;       //没有绘制  
  96.                     this.Enabled = false;   //继续禁用窗体 (查找时忽略禁用的窗体)  
  97.                 } else {  
  98.                     this.Enabled = true;        //如果没有绘制 那关闭窗体 先恢复窗体  
  99.                     new Thread(new ThreadStart(ThreadCloseForm)).Start();  
  100.                     /*起一个线程来关闭窗体 目的是为了让当前窗体 接受到这个右键信息  
  101.                      不然这个现在关闭了 那么这个右键信息就传递到下面去了 
  102.                      */  
  103.                 }  
  104.             }  
  105.             if (isDrawed) {     //如果已经绘制了 就不用继续自动框选区域了  
  106.                 return;  
  107.             }  
  108.             FoundRectToDraw();  //找寻框选区域并且绘制  
  109.         }  
  110.         public void FoundRectToDraw() {  
  111.             //在桌面根据鼠标位置去找寻鼠标下面的控件句柄  
  112.             IntPtr thWnd = ChildWindowFromPointEx(GetDesktopWindow(),  
  113.                     MousePosition, CWP_SKIPDISABLED | CWP_SKIPINVISIBL);//忽略桌面上隐藏或者禁用的窗体  
  114.             //thWnd是找到的桌面的窗体的句柄 再次 根据鼠标坐标在此窗体里面找寻控件  
  115.             LPPOINT lp = new LPPOINT();     //注意这里的鼠标不是屏幕坐标 而是 窗体内部坐标  
  116.             lp.x = MousePosition.X; lp.y = MousePosition.Y;  
  117.             ScreenToClient(thWnd, out lp);  //所以把屏幕坐标转换为客户区内部坐标  
  118.             //再次用刚才的thWnd句柄去找寻 这次一个都不忽略  
  119.             IntPtr temphWnd = ChildWindowFromPointEx(thWnd, new Point(lp.x, lp.y),CWP_All);  
  120.             //判断这个句柄是否为空  
  121.             if (temphWnd == IntPtr.Zero) {  //如果没有找到 就用刚才那个句柄(这里我只查找了两层)  
  122.                 temphWnd = thWnd;  
  123.             }  
  124.             if (temphWnd == hWnd) {     //如果这个句柄 和 上一个区域的句柄没有变化 那么久返回吧  
  125.                 return;  
  126.             } else {  
  127.                 hWnd = temphWnd;        //不然就把这个新找到的句柄赋值给它   
  128.             }  
  129.             DrawRect("Normal");         //然后在这个新的句柄外面绘制一圈  
  130.         }  
  131.           
  132.         public void DrawRect(string type) {//绘制区域  
  133.             using (Graphics g = pictureBox1.CreateGraphics()) {//建立画布  
  134.                 using (Pen p = new Pen(Color.FromArgb(255, 24, 219, 255), 2)) {//画笔  
  135.                     pictureBox1.Refresh();  //绘制前先清空画布  
  136.                     switch (type) {  
  137.                         case "Normal"://绘制找到的区域  
  138.                             LPRECT rect = new LPRECT();  
  139.                             GetWindowRect(hWnd, out rect);  
  140.                             g.DrawRectangle(p, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);  
  141.                             Rectangle drawRect = new Rectangle(new Point(rect.left, rect.top),  
  142.                                 new Size(rect.right - rect.left, rect.bottom - rect.top));  
  143.                             g.DrawImage(screenBmp, drawRect, drawRect, GraphicsUnit.Pixel);  
  144.                             break;  
  145.                         case "Rectangle"://鼠标点下并且拖动的时候 绘制矩形(也就是自定义区域)  
  146.                             g.DrawRectangle(p, sx, sy, MousePosition.X - sx + 1, MousePosition.Y - sy + 1);  
  147.                             break;  
  148.                         case "MouseUp"://鼠标抬起的时候 把相应区域的图片也画上去(正常的不是黑色的图)  
  149.                             Rectangle imgRect = new Rectangle(new Point(sx, sy),   
  150.                                 new Size(MousePosition.X - sx + 1, MousePosition.Y - sy + 1));  
  151.                             g.DrawRectangle(p, sx, sy, MousePosition.X - sx + 1, MousePosition.Y - sy + 1);  
  152.                             g.DrawImage(screenBmp, imgRect, imgRect, GraphicsUnit.Pixel);  
  153.                             break;  
  154.                     }  
  155.                 }  
  156.             }  
  157.         }  
  158.         public delegate void CloseFormDel();//创建委托 因为要跨线程操作  
  159.         //Form.CheckForIllegalCrossThreadCalls = false;这样也可以就可以直接跨线程操作了 不过 话说不好  
  160.         public void ThreadCloseForm() {  
  161.             Thread.Sleep(100);//暂停100毫秒再关闭窗体 (让改窗体接受到鼠标)  
  162.             try {//好吧 我承认 这里我也不知道为什么 有时候提示改 不能再该句柄创建前调用Invoke(我很郁闷我的窗体不是创建了么)  
  163.                 this.Invoke(new CloseFormDel(CloseForm));  
  164.             } catch {  
  165.                 this.Close();  
  166.             }   
  167.         }  
  168.         public void CloseForm() {//关闭窗体  
  169.             this.Close();  
  170.         }  
  171.   
  172.         private void pictureBox1_MouseMove(object sender, MouseEventArgs e) {  
  173.             //判断鼠标是否点下 如果点下没有松开 那么移动的时候就绘制区域  
  174.             if (isDraw) {  
  175.                 //因为窗体在恢复禁用的时候就会马上出发Move事件 所以判断一下 如果鼠标在点下的时候 位置没用动那么什么都不做  
  176.                 if (sx == MousePosition.X && sy == MousePosition.Y) {  
  177.                     return;  
  178.                 }  
  179.                 DrawRect("Rectangle");//还有自己判断 反方向拖动鼠标的情况 - -!、、这里就不写了 代码太多看着麻烦  
  180.             }  
  181.         }  
  182.   
  183.         private void pictureBox1_MouseUp(object sender, MouseEventArgs e) {  
  184.             isDraw = false;//取消鼠标移动的绘制  
  185.             //因为第一次点下鼠标 默认是 绘制自动框选的区域 所以 坐标没有变的话 什么都不做  
  186.             if (sx == MousePosition.X && sy == MousePosition.Y) {  
  187.                 return;  
  188.             }  
  189.             DrawRect("MouseUp");  
  190.         }  
  191.     }  
  192. }  

值得注意的是这里

[csharp] view plain copy
  1. public void FoundRectToDraw() {  
  2.     //在桌面根据鼠标位置去找寻鼠标下面的控件句柄  
  3.     IntPtr thWnd = ChildWindowFromPointEx(GetDesktopWindow(),  
  4.             MousePosition, CWP_SKIPDISABLED | CWP_SKIPINVISIBL);//忽略桌面上隐藏或者禁用的窗体  
  5.     //thWnd是找到的桌面的窗体的句柄 再次 根据鼠标坐标在此窗体里面找寻控件  
  6.     LPPOINT lp = new LPPOINT();     //注意这里的鼠标不是屏幕坐标 而是 窗体内部坐标  
  7.     lp.x = MousePosition.X; lp.y = MousePosition.Y;  
  8.     ScreenToClient(thWnd, out lp);  //所以把屏幕坐标转换为客户区内部坐标  
  9.     //再次用刚才的thWnd句柄去找寻 这次一个都不忽略  
  10.     IntPtr temphWnd = ChildWindowFromPointEx(thWnd, new Point(lp.x, lp.y),CWP_All);  
  11.     //判断这个句柄是否为空  
  12.     if (temphWnd == IntPtr.Zero) {  //如果没有找到 就用刚才那个句柄(这里我只查找了两层)  
  13.         temphWnd = thWnd;  
  14.     }  
  15.     if (temphWnd == hWnd) {     //如果这个句柄 和 上一个区域的句柄没有变化 那么久返回吧  
  16.         return;  
  17.     } else {  
  18.         hWnd = temphWnd;        //不然就把这个新找到的句柄赋值给它   
  19.     }  
  20.     DrawRect("Normal");         //然后在这个新的句柄外面绘制一圈  
  21. }  

这里 我只进行了两层的查找 第一层桌面的窗体 第二层窗体里面的控件 (如果控件中还有控件 那么久得进行第三层的查找 比如窗体里面有一个panel上面有一个button那个只能把panel框选出来 那么久得得再次根据坐标去panel里面查找) 

注意查找的时候 坐标都是用的窗体的内部坐标 而不是 屏幕的坐标 (第一成就用屏幕的坐标 因为桌面也是一个窗体 屏幕坐标就是桌面窗体的内部坐标!)

源码在第一篇文章中有下载连接

posted @ 2016-12-16 18:46  Net-Spider  阅读(303)  评论(0)    收藏  举报