C# 通过窗口标题关闭窗口和弹出窗口的进程
最近閑來無事玩玩游戲,這游戲有個毛病就是不定時掉線,每次掉線都不知道,所以浪費了大把時間。
作為C#初學者這我能忍么,寫個軟件監控一下掉線,順便學習歷練一下。
搜遍所有代碼站點也沒有找到符合自己需求的代碼,所以經過兩天的不斷嘗試終于解決了自己的問題,現在將經驗分享:
思路:通過彈出掉線提示的窗口里面獲取信息,來判斷是否掉線,然后關閉窗口和對應的進程。
難點:鑒于net框架的設計初衷,沒有操作內存類的權限,所以只能使用API來完成。
附加:之所以用了兩天時間,是因為大部分時間都浪費在搞懂線程,進程,句柄,窗口,子窗口之間的關系,現分享我的理解:
進程>線程? ? ? ? ? ? ? ? ?主窗體>子窗體>控件>控件屬性
窗體就像套娃,一個套一個,每個控件或者窗體都有一個的ID,我們叫做指針(API),所以我們想解決問題就要從小往大一步一步的進行
所以我們要解決問題,就要先找到彈窗控件的指針,通過這個指針獲取到相關信息進行對比是否符合所有條件,符合的話就通過這個指針對他進行操作,然后找到這個指針的進程ID(PID-ProcessID)或者線程ID(TID-ThreadProcessID),通過ID找到對應的進程,然后干掉他。
廢話不多說,直接上代碼,至于API的相關詳細參數請參考我的上一篇文章。
首先是導入命名空間
using System.Runtime.InteropServices;然后是導入API函數
/// <summary>/// 查找所有窗口(只要是在進程里面的)/// 如果不限制類名或者標題使用null代替/// </summary>/// <param name="lpClassName">窗口類名,不限制使用null</param>/// <param name="lpWindowName">窗口標題,不限制使用null</param>/// <returns>找到的窗口句柄</returns>[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);// 獲取窗口的類名/// <summary>/// 獲取目標句柄的類名/// </summary>/// <param name="hWnd">目標窗口句柄</param>/// <param name="lpClassName">返回的Class名稱</param>/// <param name="nMaxCount">允許返回的Class名稱的字符數量上限</param>/// <returns>獲取到的實際Class字符長度</returns>[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);// 判斷窗口是否可見/// <summary>/// 判斷目標窗口是否可見/// </summary>/// <param name="hWnd">窗口句柄</param>/// <returns>可見true,不可見false</returns>[DllImport("user32.dll")]public static extern bool IsWindowVisible(IntPtr hWnd);// 給窗口發送消息/// <summary>/// 給目標句柄發送消息/// </summary>/// <param name="hwnd">目標句柄</param>/// <param name="wMsg">消息內容</param>/// <param name="wParam">附加自定義參數1</param>/// <param name="lParam">附加自定義參數2</param>/// <returns></returns>[DllImport("user32.dll", EntryPoint = "SendMessageA")]public static extern int SendMessage(IntPtr hwnd, int wMsg, int wParam, int lParam);/// <summary>/// 獲取當前活動窗口(當前焦點窗口,鍵盤可操作的那種)/// </summary>/// <returns>返回窗口句柄</returns>[DllImport("user32.dll")]public static extern IntPtr GetActiveWindow();/// <summary>/// 通過窗口句柄獲取線程ID(TID)和進程ID(PID)/// </summary>/// <param name="hwnd">窗口句柄</param>/// <param name="PID">返回進程ID</param>/// <returns>返回線程ID</returns>[DllImport("User32.dll", CharSet = CharSet.Auto)]public static extern int GetWindowThreadProcessId(IntPtr hwnd, out int PID);你以為這就完了嗎?我也是做到這一步了,獲取到進程ID后還要判斷這個進程ID對應的進程名是不是我們要找到?這個進程的主窗口名字是不是我們要操作的?再看看API函數,沒有這類操作了,我在這里卡了小半天,后來突然想到既然有了進程ID我們可以用NET里面的Process類來進行操作判斷,到了自己的地盤我想怎么搞就怎么搞
完整代碼如下:
using System; using System.Diagnostics; using System.Runtime.InteropServices; using System.Text;namespace 游戲插件 {internal static class ProcCheck{[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);[DllImport("user32.dll")]public static extern bool IsWindowVisible(IntPtr hWnd);[DllImport("user32.dll", EntryPoint = "SendMessageA")]public static extern int SendMessage(IntPtr hwnd, int wMsg, int wParam, int lParam);[DllImport("User32.dll", CharSet = CharSet.Auto)]public static extern int GetWindowThreadProcessId(IntPtr hwnd, out int PID); const int WM_CLOSE = 0x10;internal static void CheckCloseProcess(bool KillPorcess){//條件值(API函數接口)string findTitle = "提示信息";//窗口標題string findClass1 = "#32770";//窗口類名string findClass2 = "GRootViewClass";//窗口類名int findClassMaxLen = 2048;//返回類名字符上限//返回值(API函數接口)IntPtr hWnd = IntPtr.Zero;//窗口句柄StringBuilder sbr = new StringBuilder(500);//返回類名字符串數據流string scr = string.Empty;//返回的類名字符串int ClassLen = 0;//實際返回類名字符長度int hwTID = 0;//線程IDint hwPID = 0;//進程IDint hwMSR = 0;//關閉窗口返回值//C#部分變量初始化string pchkTetris = "Tetris";//游戲窗口進程名string pchkQQGame1 = "QQGame";//新版游戲大廳進程名string pchkQQGame2 = "QQGameHall";//舊版游戲大廳進程名string pchkTitle = "火拼俄羅斯";string ProcTitleResult = string.Empty;//標題名Process ProcInfo = null;//實例化進程數據類string ProcNameResult = string.Empty;//進程名// 查找標題為Error的窗口hWnd = FindWindow(null, findTitle);//通過標題找句柄//判斷窗口是否存在if (hWnd != IntPtr.Zero){ClassLen = GetClassName(hWnd, sbr, findClassMaxLen);//獲取類名if (ClassLen > 0) {scr = sbr.ToString();//將返回的類名字符串數據流轉換為字符串if (scr == findClass1 | scr == findClass2){if (IsWindowVisible(hWnd)){hwTID = GetWindowThreadProcessId(hWnd, out hwPID);//獲取線程ID和進程IDProcInfo = Process.GetProcessById(hwPID);//通過進程ID獲取進程所有信息ProcNameResult = ProcInfo.ProcessName;//獲取進程名ProcTitleResult = ProcInfo.MainWindowTitle;//主進程標題if (ProcNameResult == pchkTetris | ProcNameResult == pchkQQGame1 | ProcNameResult == pchkQQGame2) {if (ProcTitleResult == pchkTitle){if (KillPorcess){hwMSR = SendMessage(hWnd, WM_CLOSE, 0, 0);if (hwMSR == 0){if (ProcNameResult == pchkTetris){ProcInfo.CloseMainWindow();//結束進程return;}return;}}else{return;}}}}}}}}} }正常情況下你們可以把IF的連續判斷用&&連接符號串起來,這樣代碼就能少很多行,我是為了方便更直觀的展示判斷的條件才這樣寫的。
總結
以上是生活随笔為你收集整理的C# 通过窗口标题关闭窗口和弹出窗口的进程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Sygate
- 下一篇: 第一课时(下):破解基础之常见加壳程序特