使用c#實現(xiàn)微信自動化功能
引言
上個月,在一個群里摸魚劃水空度日,看到了一個老哥分享的一個微信自動化的一個類庫,便下載了他的Demo,其本意就是模擬鼠標來操作UI,實現(xiàn)UI自動化;然后自己在瞎琢磨研究,寫了一個簡單的例子,用來獲取好友列表,獲取聊天列表,以及最后一次接收或者發(fā)送消息的時間,以及最后一次聊天的內(nèi)容,還有自動刷朋友圈,獲取朋友圈誰發(fā)的,發(fā)的什么文字,以及配的圖片是什么,什么時候發(fā)的,再就是一個根據(jù)獲取的好友列表,來實現(xiàn)給指定好友發(fā)送消息的功能。
正文
話不多說,咱們開始,首先映入眼簾的是界面,左側(cè)是獲取好友列表,然后在右邊就是一個RichTextBox用來根據(jù)左側(cè)選中的好友列表來發(fā)送消息,中間是獲取聊天列表,好友名稱,最后一次聊天的內(nèi)容,以及最后一次聊天的時間,最右邊是獲取朋友圈的內(nèi)容,刷朋友圈,找到好友發(fā)的朋友圈內(nèi)容,以及附帶的媒體是圖片還是視頻,發(fā)朋友圈的時間。
首先需要在Nuget下載兩個包,F(xiàn)laUI.Core和FlaUI.UIA3,用這兩個包,來實現(xiàn)鼠標模擬,UI自動化的,接下來,咱們看代碼。
上面就是一整個界面的截圖,接下來,咱們講講代碼,在界面被創(chuàng)建的時候,去獲取微信的進程ID,然后,給獲取好友列表,聊天列表,朋友圈的CancelTokenSource賦值以及所關(guān)聯(lián)的CancelToken,以此來實現(xiàn)中斷取消的功能,同時在上面的List是用來存儲朋友圈信息的,下面的Content存儲聊天列表的內(nèi)容的,Key是聊天的用戶昵稱,Value是最后一次的聊天內(nèi)容,在往下的SendInput是我們用來模擬鼠標滾動的這樣我們才可以滾動獲取聊天列表,朋友圈內(nèi)容,好友列表,在下面的FindWindow,GetWindowThreadProcessID是用來根據(jù)界面名稱找到對應(yīng)的進程Id的,因為如果雙擊了朋友圈在彈出界面中,使用Process找不太方便,直接就引用這個來查找朋友圈的彈出界面。
private List<dynamic> list = new List<dynamic>(); private Dictionary<string, string> Content = new Dictionary<string, string>(); /// <summary> /// 滾動條模擬 /// </summary> /// <param name="nInputs"></param> /// <param name="pInputs"></param> /// <param name="cbSize"></param> /// <returns></returns> [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize); //根據(jù)名稱獲取窗體句柄 [DllImport("user32.dll", EntryPoint = "FindWindow")] private extern static IntPtr FindWindow(string lpClassName, string lpWindowName); //根據(jù)句柄獲取進程ID [DllImport("User32.dll", CharSet = CharSet.Auto)] public static extern int GetWindowThreadProcessId(IntPtr hwnd, out int ID); public Form1() { InitializeComponent(); GetWxHandle(); GetFriendTokenSource = new CancellationTokenSource(); GetFriendCancellationToken = GetFriendTokenSource.Token; ChatListTokenSource = new CancellationTokenSource(); ChatListCancellationToken = ChatListTokenSource.Token; FriendTokenSource = new CancellationTokenSource(); FriendCancellationToken = FriendTokenSource.Token; } private CancellationToken FriendCancellationToken { get; set; } private CancellationTokenSource FriendTokenSource { get; set; } private CancellationToken ChatListCancellationToken { get; set; } private CancellationTokenSource ChatListTokenSource { get; set; } private CancellationToken GetFriendCancellationToken { get; set; } private CancellationTokenSource GetFriendTokenSource { get; set; } private int ProcessId { get; set; } private Window wxWindow { get; set; } private bool IsInit { get; set; } = false; void GetWxHandle() { var process = Process.GetProcessesByName("Wechat").FirstOrDefault(); if (process != null) { ProcessId = process.Id; } }
接下來則是使用獲取的進程ID和Flaui綁定起來,然后獲取到微信的主UI界面,
void InitWechat() { IsInit = true; //根據(jù)微信進程ID綁定FLAUI var application = FlaUI.Core.Application.Attach(ProcessId); var automation = new UIA3Automation(); //獲取微信window自動化操作對象 wxWindow = application.GetMainWindow(automation); //喚起微信 }
接下來是獲取好友列表,判斷微信界面是否加載,如果沒有,就調(diào)用InitWeChat方法,然后在下面判斷主界面不為空,設(shè)置界面為活動界面,然后在主界面找到ui控件的name是通訊錄的,然后模擬點擊,這樣就從聊天界面切換到了通訊錄界面,默認的界面第一條都是新朋友,而我沒有做就是說當前列表在哪里就從哪里獲取,雖然你點擊了獲取好友列表哪怕沒有在最頂部的新朋友那里,也依舊是可以模擬滾動來實現(xiàn)獲取好友列表的,然后接下來調(diào)用FindAllDescendants,獲取主界面的所有子節(jié)點,在里面找到所有父節(jié)點不為空并且父節(jié)點的Name是聯(lián)系人的節(jié)點,之所以是Parent的Name是聯(lián)系人, 是因為我們的好友列表,都是隸屬于聯(lián)系人這個父節(jié)點之下的,找到之后呢,我們?nèi)ケ闅v找到的這些聯(lián)系人,名字不為空的過濾掉了,如果存在同名的也可能會過濾掉,沒有做處理,并且,找到的類型必須是ListItem,因為聯(lián)系人本身就是一個列表,他的子類具體的聯(lián)系人肯定就是一個列表項目,就需要這樣過濾,就可以找到好友列表,同時添加到界面上,在最后我們調(diào)用了Scroll方法,模擬滾動700像素,這塊可能有的電腦大小不一樣或者是微信最大化,可以根據(jù)具體情況設(shè)置。最后在寫了取消獲取好友列表的事件。
/// <summary> /// 獲取好友列表 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button1_Click(object sender, EventArgs e) { if (!IsInit) { InitWechat(); } if (wxWindow != null) { if (wxWindow.AsWindow().Patterns.Window.PatternOrDefault != null) { //將微信窗體設(shè)置為默認焦點狀態(tài) wxWindow.AsWindow().Patterns.Window.Pattern.SetWindowVisualState(FlaUI.Core.Definitions.WindowVisualState.Normal); } } wxWindow.FindAllDescendants().Where(s => s.Name == "通訊錄").FirstOrDefault().Click(false); wxWindow.FindAllDescendants().Where(s => s.Name == "新的朋友").FirstOrDefault()?.Click(false); string LastName = string.Empty; var list = new List<AutomationElement>(); var sync = SynchronizationContext.Current; Task.Run(() => { while (true) { if (GetFriendCancellationToken.IsCancellationRequested) { break; } var all = wxWindow.FindAllDescendants(); var allItem = all.Where(s => s.Parent != null && s.Parent.Name == "聯(lián)系人").ToList(); var sss = all.Where(s => s.ControlType == ControlType.Text && !string.IsNullOrWhiteSpace(s.Name)).ToList(); foreach (var item in allItem) { if (item.Name != null && item.ControlType == ControlType.ListItem && !string.IsNullOrWhiteSpace(item.Name) && !listBox1.Items.Contains(item.Name.ToString())) { sync.Post(s => { listBox1.Items.Add(s); }, item.Name.ToString()); } } Scroll(-700); } }, GetFriendCancellationToken); } ? ? ? ? ?private void button4_Click(object sender, EventArgs e)? ? ? ? ?{? ? ? ? ? ? ? ? ?GetFriendTokenSource.Cancel();? ? ? ? ?}
接下來是獲取朋友圈的事件,找到了進程ID實際上和之前Process獲取的一樣,此處應(yīng)該可以是不需要調(diào)用Finwindow也可以,找到之后獲取Window的具體操作對象,即點擊朋友圈彈出的朋友圈界面,然后找到第一個項目模擬點擊一下,本意在將鼠標移動過去,不然后面不可以實現(xiàn)自動滾動,在循環(huán)里,獲取這個界面的所有子元素,同時找到父類屬于朋友圈,列表這個的ListItem,找到之后,開始遍歷找到的集合,由于找到的朋友圈的昵稱還有媒體類型,以及時間,還有具體的朋友圈文字內(nèi)容都包含在了Name里面,所以就需要我們根據(jù)他的格式去進行拆分,獲取對應(yīng)的時間,昵稱,還有朋友圈內(nèi)容,媒體類型等,最后添加到DataGridView里面。
private void button3_Click(object sender, EventArgs e) { if (!IsInit) { InitWechat(); } if (wxWindow != null) { if (wxWindow.AsWindow().Patterns.Window.PatternOrDefault != null) { //將微信窗體設(shè)置為默認焦點狀態(tài) wxWindow.AsWindow().Patterns.Window.Pattern.SetWindowVisualState(FlaUI.Core.Definitions.WindowVisualState.Normal); } } var a = Process.GetProcesses().Where(s => s.ProcessName == "朋友圈"); wxWindow.FindAllDescendants().Where(s => s.Name == "朋友圈").FirstOrDefault().Click(false); var handls = FindWindow(null, "朋友圈"); if (handls != IntPtr.Zero) { GetWindowThreadProcessId(handls, out int FridId); var applicationFrid = FlaUI.Core.Application.Attach(FridId); var automationFrid = new UIA3Automation(); //獲取微信window自動化操作對象 var Friend = applicationFrid.GetMainWindow(automationFrid); Friend.FindAllDescendants().FirstOrDefault(s => s.ControlType == ControlType.List).Click(false); var sync = SynchronizationContext.Current; Task.Run(async () => { while (true) { try { if (FriendCancellationToken.IsCancellationRequested) { break; } var allInfo = Friend.FindAllDescendants(); var itema = allInfo.Where(s => s.ControlType == ControlType.ListItem && s.Parent.Name == "朋友圈" && s.Parent.ControlType == ControlType.List); if (itema != null) { foreach (var item in itema) { var ass = item.FindAllDescendants().FirstOrDefault(s => s.ControlType == ControlType.Text); //ass.FocusNative(); //ass.Focus(); var index = item.Name.IndexOf(':'); var name = item.Name.Substring(0, index); var content = item.Name.Substring(index + 1); var split = content.Split("\n"); if (split.Length > 3) { var time = split[split.Length - 2]; var mediaType = split[split.Length - 3]; var FriendContent = split[0..(split.Length - 3)]; var con = string.Join(",", FriendContent); if (list.Any(s => s.Content == con)) { continue; } sync.Post(s => { dataGridView2.Rows.Add(name, s, mediaType, time); dynamic entity = new { Name = name, Content = s, MediaType = mediaType, Time = time }; list.Add(entity); }, con); } } Scroll(-500); await Task.Delay(100); } } catch (Exception ex) { continue; } } }); } } private void button6_Click(object sender, EventArgs e) { FriendTokenSource.Cancel(); }
然后接下來就是獲取聊天列表,以及給指定好友發(fā)送消息的功能了,在下面這段代碼里,上面都是判斷有沒有設(shè)置為活動界面,然后找到所有的子元素,找到屬于會話的子節(jié)點,并且子節(jié)點是ListItem,過濾掉折疊的群聊,如果點擊倒折疊的群聊,就得在模擬點擊回退回來,這里我沒有寫具體的代碼,不過也很簡單,找到對應(yīng)的聊天列表之后,開始遍歷每一個聊天對象,根據(jù)Xpath,我們找到了符合條件的Text,這Text包括我們的時間,內(nèi)容,還有昵稱,關(guān)于Xpath,不熟悉結(jié)構(gòu)的可以看看我們的C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x64路徑下,可能有的Bin里面的版本不是我這個版本,你們可以根據(jù)自己的系統(tǒng)版本去找對應(yīng)64或者32位里面的有一個程序叫做inspect.exe這塊可以看需要操作界面的UI結(jié)構(gòu),然后根據(jù)這個去寫Xpath就行,在獲取倒這些內(nèi)容之后,我們添加到界面上面去,然后模擬滾動去獲取聊天列表,
private void button2_Click(object sender, EventArgs e) { if (!IsInit) { InitWechat(); } if (wxWindow != null) { if (wxWindow.AsWindow().Patterns.Window.PatternOrDefault != null) { //將微信窗體設(shè)置為默認焦點狀態(tài) wxWindow.AsWindow().Patterns.Window.Pattern.SetWindowVisualState(FlaUI.Core.Definitions.WindowVisualState.Normal); } } wxWindow.FindAllDescendants().Where(s => s.Name == "聊天").FirstOrDefault().Click(false); wxWindow.FindAllDescendants().Where(s => s.Name == "媽媽").FirstOrDefault().Click(false); var sync = SynchronizationContext.Current; Task.Run(async () => { object obj; while (true) { var all = wxWindow.FindAllDescendants(); try { if (ChatListCancellationToken.IsCancellationRequested) { break; } var allItem = all.Where(s => s.ControlType == ControlType.ListItem && !string.IsNullOrEmpty(s.Name) && s.Parent.Name == "會話" && s.Name != "折疊的群聊"); foreach (var item in allItem) { var allText = item.FindAllByXPath("http://*/Text"); if (allText != null && allText.Length >= 3) { var name = allText[0].Name; var time = allText[1].Name; var content = allText[2].Name; if (Content.ContainsKey(name)) { var val = Content[name]; if (val != content) { Content.Remove(name); Content.Add(name, content); } } else { Content.Add(name, content); } sync.Post(s => { dataGridView1.Rows.Add(item.Name, content, time); }, null); } } Scroll(-700); await Task.Delay(100); } catch (Exception) { continue; } } }, ChatListCancellationToken); } private void button5_Click(object sender, EventArgs e) { ChatListTokenSource.Cancel(); }
接下來有一個發(fā)送的按鈕的事件,主要功能就是根據(jù)所選擇的好友列表,去發(fā)送RichTextBox的消息,在主要代碼塊中,我們是獲取了PC微信的搜索框,然后設(shè)置焦點,然后模擬點擊,模擬點擊之后將我們選擇的好友名稱輸入到搜索框中,等待500毫秒之后,在重新獲取界面的子元素,這樣我們的查找結(jié)果才可以在界面上顯示出來,不等待的話是獲取不到的,找到了之后呢,我們拿到默認的第一個然后模擬點擊,就到了聊天界面,獲取到了聊天界面,然后獲取輸入信息的 文本框,也就是代碼的MsgBox,將他的Text的值設(shè)置為我們在Richtextbox輸入的值,然后找到發(fā)送的按鈕,模擬點擊發(fā)送,即可實現(xiàn)自動發(fā)送。
private async void button7_Click(object sender, EventArgs e) { var sendMsg=richTextBox1.Text.Trim(); var itemName = listBox1.SelectedItem?.ToString(); if (!IsInit) { InitWechat(); } if (wxWindow != null) { if (wxWindow.AsWindow().Patterns.Window.PatternOrDefault != null) { //將微信窗體設(shè)置為默認焦點狀態(tài) wxWindow.AsWindow().Patterns.Window.Pattern.SetWindowVisualState(FlaUI.Core.Definitions.WindowVisualState.Normal); } } var search=wxWindow.FindAllDescendants().FirstOrDefault(s => s.Name == "搜索"); search.FocusNative(); search.Focus(); search.Click(); await Task.Delay(500); var text=wxWindow.FindAllDescendants().FirstOrDefault(s => s.Name == "搜索").Parent; if (text!=null) { await Task.Delay(500); var txt=text.FindAllChildren().FirstOrDefault(s=>s.ControlType==ControlType.Text) .AsTextBox(); txt.Text = itemName; await Task.Delay(500); var item = wxWindow.FindAllDescendants().Where(s => s.Name==itemName&&s.ControlType==ControlType.ListItem).ToList(); wxWindow.FocusNative(); if (item!=null&& item.Count>0&&!string.IsNullOrWhiteSpace(sendMsg)) { if (item.Count<=1) { item.FirstOrDefault().Click(); } else { item.FirstOrDefault(s => s.Parent != null && s.Parent.Name.Contains("@str:IDS_FAV_SEARCH_RESULT")).Click(); } var msgBox = wxWindow.FindFirstDescendant(x => x.ByControlType(FlaUI.Core.Definitions.ControlType.Text)).AsTextBox(); msgBox.Text = sendMsg; var button = wxWindow.FindAllDescendants().Where(s => s.Name == "發(fā)送(S)").FirstOrDefault(); button?.Click(); } } }
下圖是我獲取的好友列表,朋友圈列表,以及聊天列表的信息。
下面是使用c#調(diào)用win api模擬鼠標滾動的代碼。有關(guān)SendInput的講解,詳情請看官網(wǎng)https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput?! ?/p>
#region Scroll Event void Scroll(int scroll) { INPUT[] inputs = new INPUT[1]; // 設(shè)置鼠標滾動事件 inputs[0].type = InputType.INPUT_MOUSE; inputs[0].mi.dwFlags = MouseEventFlags.MOUSEEVENTF_WHEEL; inputs[0].mi.mouseData = (uint)scroll; // 發(fā)送輸入事件 SendInput(1, inputs, Marshal.SizeOf(typeof(INPUT))); } public struct INPUT { public InputType type; public MouseInput mi; } // 輸入類型 public enum InputType : uint { INPUT_MOUSE = 0x0000, INPUT_KEYBOARD = 0x0001, INPUT_HARDWARE = 0x0002 } // 鼠標輸入結(jié)構(gòu)體 public struct MouseInput { public int dx; public int dy; public uint mouseData; public MouseEventFlags dwFlags; public uint time; public IntPtr dwExtraInfo; } // 鼠標事件標志位 [Flags] public enum MouseEventFlags : uint { MOUSEEVENTF_MOVE = 0x0001, MOUSEEVENTF_LEFTDOWN = 0x0002, MOUSEEVENTF_LEFTUP = 0x0004, MOUSEEVENTF_RIGHTDOWN = 0x0008, MOUSEEVENTF_RIGHTUP = 0x0010, MOUSEEVENTF_MIDDLEDOWN = 0x0020, MOUSEEVENTF_MIDDLEUP = 0x0040, MOUSEEVENTF_XDOWN = 0x0080, MOUSEEVENTF_XUP = 0x0100, MOUSEEVENTF_WHEEL = 0x0800, MOUSEEVENTF_HWHEEL = 0x1000, MOUSEEVENTF_MOVE_NOCOALESCE = 0x2000, MOUSEEVENTF_VIRTUALDESK = 0x4000, MOUSEEVENTF_ABSOLUTE = 0x8000 } const int MOUSEEVENTF_WHEEL = 0x800; #endregion
結(jié)尾
使用這個類庫當然可以實現(xiàn)一個自動回復(fù)機器人,以及消息朋友圈某人更新訂閱,消息訂閱等等,公眾號啊 一些信息的收錄。
以上是使用FlaUi模擬微信自動化的一個簡單Demo,記得好像也可以模擬QQ的,之前簡單的嘗試了一下,可以獲取一些東西,代碼地址:https://gitee.com/cxd199645/we-chat-auto.git。歡迎各位大佬討論
到此這篇關(guān)于使用c#實現(xiàn)微信自動化 的文章就介紹到這了,更多相關(guān)c#微信自動化 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C#實現(xiàn)遞歸調(diào)用的Lambda表達式
這篇文章介紹了C#實現(xiàn)遞歸調(diào)用的Lambda表達式,文中通過示例代碼介紹的非常詳細。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-06-06c#根據(jù)文件類型獲取相關(guān)類型圖標的方法代碼
c#根據(jù)文件類型獲取相關(guān)類型圖標的方法代碼,需要的朋友可以參考一下2013-05-05C#使用第三方組件實現(xiàn)動態(tài)解析和求值字符串表達式
這篇文章主要介紹了C#如何使用第三方組件(LambdaParser、DynamicExpresso、Z.Expressions)實現(xiàn)動態(tài)解析和求值字符串表達式,感興趣的小伙伴可以跟隨小編一起了解一下2022-06-06