• 【原创】自己动手写工具----签到器[Beta 1.0]


    写在前面

    最近公司没有什么项目,想通过项目练练手的机会也没有,只能自己学习了,因此空下来的时间也挺多的,就打开网页看看吧,哎,一打开就让签到(像什么百度知道啊、百度云盘啊之类的),我签到的目的是获取积分,便于下载资料^_^,真是烦的很,要是有个工具能帮助我全自动处理该有多好,想着想着大概的思路就出来了,无非就是开启进程,传入参数,确定坐标,点击。OK啦啊哈哈~ 起来码砖了……
    好了,不闲聊了,干活,先上效果图。(我对win7的毛玻璃界面情有独钟……^_^)

                          

    功能概览

    总体来说,这款小工具就是打开IE浏览器,输入地址,然后通过API给鼠标定位,在模拟鼠标点击的过程。但是在写代码的过程中,发现其实它不仅仅可以打开浏览器,还可以打开各种Windows下的工具,但是相对于浏览器的签到功能,还是比较麻烦的,因为打开Windows下的工具后需要的操作比较复杂,而这个工具当初的定位也仅仅是签到(点击一下)。此外,这个小工具还可以添加任务、批量执行任务,添加任务时,首先把中间的三个文本框填好,也就是任务名称、进程名称、进程参数,详细的内容参考上面的截图,除了这些参数,还需要确定鼠标点击的位置,在指定的位置点击鼠标右键,就可以将任务中需要点击的位置存储起来,每点击一次,该坐标都会更新一次。所有的参数都满足了之后,最后单击“新增任务”,那么一个任务就建立好了,以后就可以通过签到器来帮你实现了~~。另外,下面有两个线程休眠时间,一个是任务与任务之间的,另一个是单个任务之内的。在执行任务的过程中,需要将指定的线程Sleep,否则程序执行太快,浏览器还没有打开,程序就结束了,岂不是很悲催~~~~所以这就需要设置线程内的休眠时间。那么线程间的休眠呢,其实这个倒无所谓了,怎么设置都可以,因为它不会影响任务的执行,不过为了不把我的签到器累坏,还是设置了2秒钟~~~~~~~哈哈。另外,任务的存储我是采用XML格式的文件进行存储,因为数据量不是很大,读写操作也比较方便。这里需要注意的是一般的网站签到前提是需要登录,所以必须在浏览器上有自己登录信息的缓存才可以哦~

    功能详解

    首先,签到器中用到了全局的鼠标钩子,运用鼠标钩子的目的是可以使得鼠标在脱离Winform窗体后,仍然能够捕获鼠标的消息,如鼠标的移动、按键的按下等,当移动鼠标的时候,最上方的X、Y坐标是实时变化的,当单击鼠标右键时,可以记录单击时的坐标信息,作为新建任务的一部分信息。这一块用到了WinAPI的函数SetWindowsHookEx,具体代码如下:

     1 //装置钩子的函数  
     2 [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
     3 public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);
     4 
     5 //卸下钩子的函数  
     6 [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
     7 public static extern bool UnhookWindowsHookEx(int idHook);
     8 
     9 //下一个钩挂的函数  
    10 [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
    11 public static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam);
    12 
    13 //声明委托
    14 public delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);

    安装钩子,代码如下:

     1 public void Start()
     2 {
     3       //安装鼠标钩子  
     4       if (hMouseHook == 0)
     5       {
     6           //生成一个HookProc的实例.  
     7           MouseHookProcedure = new HookProc(MouseHookProc);
     8 
     9           //hMouseHook = SetWindowsHookEx(WH_MOUSE_LL, MouseHookProcedure, Marshal.GetHINSTANCE(System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0]), 0);
    10          hMouseHook = SetWindowsHookEx(WH_MOUSE_LL, MouseHookProcedure, GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName), 0);
    11 
    12          //如果装置失败停止钩子  
    13          if (hMouseHook == 0)
    14          {
    15              Stop();
    16              throw new Exception("SetWindowsHookEx failed.");
    17          }
    18      }
    19 }

    卸载钩子,代码如下:

     1 public void Stop()
     2 {
     3      bool retMouse = true;
     4      if (hMouseHook != 0)
     5      {
     6          retMouse = UnhookWindowsHookEx(hMouseHook);
     7          hMouseHook = 0;
     8      }
     9      //如果卸下钩子失败
    10      if (!(retMouse)) throw new Exception("UnhookWindowsHookEx failed.");
    11 }

     最重要的一步,监听鼠标消息,代码如下:

     1 private int MouseHookProc(int nCode, Int32 wParam, IntPtr lParam) 
     2 {
     3      //如果正常运行并且用户要监听鼠标的消息
     4      if ((nCode >= 0) && (OnMouseActivity != null)) {
     5          MouseButtons button = MouseButtons.None;
     6          int clickCount = 0;
     7 
     8          switch (wParam) {
     9          case WM_LBUTTONDOWN:
    10              button = MouseButtons.Left;
    11              clickCount = 1;
    12              break;
    13          case WM_LBUTTONUP:
    14              button = MouseButtons.Left;
    15              clickCount = 2;
    16              break;
    17          case WM_LBUTTONDBLCLK:
    18              button = MouseButtons.Left;
    19              clickCount = 3;
    20              break;
    21          case WM_RBUTTONDOWN:
    22              button = MouseButtons.Right;
    23              clickCount = 4;
    24              break;
    25          case WM_RBUTTONUP:
    26              button = MouseButtons.Right;
    27              clickCount = 5;
    28              break;
    29          case WM_RBUTTONDBLCLK:
    30              button = MouseButtons.Right;
    31              clickCount = 6;
    32              break;
    33          }
    34 
    35          //从回调函数中得到鼠标的信息
    36          MouseHookStruct MyMouseHookStruct = (MouseHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseHookStruct));
    37          MouseEventArgs e = new MouseEventArgs(button, clickCount, MyMouseHookStruct.pt.x, MyMouseHookStruct.pt.y, 0);
    38          //if(e.X>700)return 1;//如果想要限制鼠标在屏幕中的移动区域可以在此处设置
    39          OnMouseActivity(this, e);
    40      }
    41      return CallNextHookEx(hMouseHook, nCode, wParam, lParam);
    42 }

    最后在MainForm中,实例化并绑定委托方法就可以了。代码如下:

     1 public MainFrom()
     2 {
     3     InitializeComponent();
     4     mouse = new MouseHookEvents();
     5     mouse.OnMouseActivity += new MouseEventHandler(mouse_OnMouseActivity);
     6     mouse.Start();
     7     task = new Task();
     8 }
     9 
    10 
    11 private void mouse_OnMouseActivity(object sender, MouseEventArgs e)
    12 {
    13     txt_X.Text = e.X.ToString();
    14     txt_Y.Text = e.Y.ToString();
    15     //使用鼠标右键对当前坐标进行存储
    16     if (e.Button == MouseButtons.Right)
    17     {
    18         SavePoint();
    19         lblMsg.Text = string.Format("当前坐标(X,Y)=({0},{1})已存储", txt_X.Text, txt_Y.Text);
    20     }
    21 }

    OK,到这里鼠标消息的捕获工作就完成了。下面说说数据的存储,如下XML代码就是存储任务数据的结构:

     1 <tasks>
     2     <task>
     3         <taskName>百度云盘</taskName>
     4         <application>iexplore.exe</application>
     5         <param>http://www.baiduyun.me/forum.php</param>
     6         <position>
     7             <x>1244</x>
     8             <y>140</y>
     9         </position>
    10     </task>
    11 </tasks>

    然后建立Task实体,包括对Task的增加、修改和执行等操作,这里比较简单,无非就是对XML节点的操作。

     1 public static List<Task> GetXmlTaskList()
     2 {
     3     doc.Load(path);//注意Load数据
     4     XmlNodeList list = doc.SelectNodes("tasks/task");
     5     List<Task> tasks = new List<Task>();
     6     foreach (XmlElement item in list)
     7     {
     8         Task task = new Task();
     9         task.Name = item.SelectSingleNode("./taskName").InnerText;
    10         task.Application = item.SelectSingleNode("./application").InnerText;
    11         task.Url = item.SelectSingleNode("./param").InnerText;
    12         task.PositionX = item.SelectSingleNode("./position/x").InnerText.ToInt();
    13         task.PositionY = item.SelectSingleNode("./position/y").InnerText.ToInt();
    14         tasks.Add(task);
    15     }
    16     return tasks;
    17 }

    然后在MainForm中取出任务,依次执行。对了,前面的增加任务无非就是执行任务的反向操作,向XML中添加节点。
    还有一个问题,当执行任务时,如果执行任务的线程在UI主线程中时,签到器的界面会出现假死的情况,这时候就需要采用多线程来进行处理,避免主线程的休眠导致的假死,代码如下:

     1  //执行任务列表
     2  private void button2_Click(object sender, EventArgs e)
     3  {
     4      //ExcuteTask()
     5      List<Task> tasks = TaskModel.GetTaskList();
     6      new Thread(() =>
     7      {//开启新线程,避免与主线程UI冲突,导致界面假死
     8          foreach (Task item in tasks)
     9          {
    10              ExcuteTask(item);
    11              Thread.Sleep((int)numericUpDown1.Value*BASENUM);
    12          }
    13      }).Start();
    14  }

     开启进程使用Process类来完成,代码如下:

     1  public void ExcuteTask(Task task)
     2  {
     3      ProcessStartInfo ps = new ProcessStartInfo(task.Application, task.Url);
     4      Process.Start(ps);
     5      Thread.Sleep((int)numericUpDown2.Value * BASENUM);
     6      //设置鼠标位置
     7      MouseEvents.SetCursorPosition(task.PositionX, task.PositionY);
     8      //模拟鼠标单击
     9      MouseEvents.MouseClick();
    10  }

    至此,签到工具基本的功能就实现了,原来懒人不是这么好做的呀啊哈哈,这就是为一个“懒”字付出的代价~~~~

    总结

    通过制作这个小玩意儿,也巩固和扩展了自己的知识,写代码的过程中也遇到了一些问题,但是都被一个一个地解决掉了,只有这样,自己的印象才会加深,下一次才不会在同一块石头上绊倒。比如,前面遇到了一个棘手的问题,关于钩子的卸载,每次关闭程序,都会提示钩子卸载失败!最后发现,卸载钩子写在了创建钩子的类的析构函数中,把卸载的函数放在Form_CLosed事件中就OK了。这只是一个初级的版本,我还设想了一些新的功能,如果能实现Task的Step定制就好了,就可以突破每次只能单击一次的局限了。还有,不仅仅是执行IE下的任务,结合多步定制还可以执行其他应用程序,那这个小玩意儿的功能就丰富了。各位大神觉得有改进的意见或建议尽管提出,小弟感激不尽~~如果觉得好玩儿就给个赞吧~(≧▽≦)/~

    补充点儿

    下班把项目带回家,我发现出现Visual Studio无可用源,不可用源的错误,就是不能进入调试。原来是自己的IDE设置问题,这里提供个办法,进入工具->选项->调试->常规->去掉“要求源文件与原始版本完全匹配”复选框就OK啦~~

    另外,下一个版本准备添加一些新功能进去,那就不单单是个简单的签到器了,比如:

    (1)任务按照进程进行分类:用IE就是执行IE的任务,与其他(如资源管理器等)无关

    (2)新增其他的进程任务:如上面提到的资源管理器,我们日常工作中常用的文件夹就那么几个,但每次都要双击,双击,再双击才能进入目标文件夹,简直是浪费时间,有了这个东东,就可以把最常用的目录放在眼前,当然你喜欢创建快捷方式也无所谓,萝卜青菜,各有所爱,我强调的只是集中管理,把懒人精神发扬到底~

    (3)每执行完一个任务关闭任务窗口:目前的功能比较粗糙,一两个任务还好,一旦加到十个八个就会发现,窗口是一个接一个地往外蹿,简直受不了啊 ...

    (4)对签到性质的任务进行”已签到“过滤:如果当天的签到任务完成了,那么再次运行时,直接忽略已签到的任务,直接去执行因各种原因而没执行签到的任务

    嗯,暂时就这些吧,谢谢大家,不早了,洗洗睡了 ... ...

     补充GitHub地址:请点我

     作者:悠扬的牧笛

     博客地址:http://www.cnblogs.com/xhb-bky-blog/p/4112441.html

     声明:本博客原创文字只代表本人工作中在某一时间内总结的观点或结论,与本人所在单位没有直接利益关系。非商业,未授权贴子请以现状保留,转载时必须保留此段声明,且在文章页面明显位置给出原文连接。

  • 相关阅读:
    ASP.NET Core的Kestrel服务器(转载)
    ASP.NET Core MVC中的IActionFilter.OnActionExecuting方法,可以获取Controller的Action方法参数值
    SQL 中常用存储过程xp_cmdshell运行cmd命令 (转载)
    SQL Server数据库中外键强制约束的好处是什么,什么时候设置外键非强制约束?(转载)
    EF Core中如何取消跟踪DbContext中所有被跟踪的实体
    c# .NET开发邮件发送功能的全面教程(含邮件组件源码)
    jquery操作select(取值,设置选中) 基础
    Visiual Studio2012 CLR20r3问题
    关于同时查询父子名称的SQL查询语句的写法 id name parentId parentName .
    Linq to SQL Like Operator
  • 原文地址:https://www.cnblogs.com/xhb-bky-blog/p/4112441.html
Copyright © 2020-2023  润新知