【原創】自己動手寫工具----簽到器[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

 聲明:本博客原創文字只代表本人工作中在某一時間內總結的觀點或結論,與本人所在單位沒有直接利益關系。非商業,未授權貼子請以現狀保留,轉載時必須保留此段聲明,且在文章頁面明顯位置給出原文連接。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM