使用c#實現(xiàn)微信自動化功能
引言
上個月,在一個群里摸魚劃水空度日,看到了一個老哥分享的一個微信自動化的一個類庫,便下載了他的Demo,其本意就是模擬鼠標(biāo)來操作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)鼠標(biāo)模擬,UI自動化的,接下來,咱們看代碼。

上面就是一整個界面的截圖,接下來,咱們講講代碼,在界面被創(chuàng)建的時候,去獲取微信的進(jìn)程ID,然后,給獲取好友列表,聊天列表,朋友圈的CancelTokenSource賦值以及所關(guān)聯(lián)的CancelToken,以此來實現(xiàn)中斷取消的功能,同時在上面的List是用來存儲朋友圈信息的,下面的Content存儲聊天列表的內(nèi)容的,Key是聊天的用戶昵稱,Value是最后一次的聊天內(nèi)容,在往下的SendInput是我們用來模擬鼠標(biāo)滾動的這樣我們才可以滾動獲取聊天列表,朋友圈內(nèi)容,好友列表,在下面的FindWindow,GetWindowThreadProcessID是用來根據(jù)界面名稱找到對應(yīng)的進(jìn)程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ù)句柄獲取進(jìn)程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;
}
}接下來則是使用獲取的進(jìn)程ID和Flaui綁定起來,然后獲取到微信的主UI界面,
void InitWechat()
{
IsInit = true;
//根據(jù)微信進(jìn)程ID綁定FLAUI
var application = FlaUI.Core.Application.Attach(ProcessId);
var automation = new UIA3Automation();
//獲取微信window自動化操作對象
wxWindow = application.GetMainWindow(automation);
//喚起微信
}接下來是獲取好友列表,判斷微信界面是否加載,如果沒有,就調(diào)用InitWeChat方法,然后在下面判斷主界面不為空,設(shè)置界面為活動界面,然后在主界面找到ui控件的name是通訊錄的,然后模擬點擊,這樣就從聊天界面切換到了通訊錄界面,默認(rèn)的界面第一條都是新朋友,而我沒有做就是說當(dāng)前列表在哪里就從哪里獲取,雖然你點擊了獲取好友列表哪怕沒有在最頂部的新朋友那里,也依舊是可以模擬滾動來實現(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è)置為默認(rèn)焦點狀態(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();? ? ? ? ?}
接下來是獲取朋友圈的事件,找到了進(jìn)程ID實際上和之前Process獲取的一樣,此處應(yīng)該可以是不需要調(diào)用Finwindow也可以,找到之后獲取Window的具體操作對象,即點擊朋友圈彈出的朋友圈界面,然后找到第一個項目模擬點擊一下,本意在將鼠標(biāo)移動過去,不然后面不可以實現(xiàn)自動滾動,在循環(huán)里,獲取這個界面的所有子元素,同時找到父類屬于朋友圈,列表這個的ListItem,找到之后,開始遍歷找到的集合,由于找到的朋友圈的昵稱還有媒體類型,以及時間,還有具體的朋友圈文字內(nèi)容都包含在了Name里面,所以就需要我們根據(jù)他的格式去進(jìn)行拆分,獲取對應(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è)置為默認(rèn)焦點狀態(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è)置為默認(rèn)焦點狀態(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é)果才可以在界面上顯示出來,不等待的話是獲取不到的,找到了之后呢,我們拿到默認(rèn)的第一個然后模擬點擊,就到了聊天界面,獲取到了聊天界面,然后獲取輸入信息的 文本框,也就是代碼的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è)置為默認(rèn)焦點狀態(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模擬鼠標(biāo)滾動的代碼。有關(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è)置鼠標(biāo)滾動事件
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
}
// 鼠標(biāo)輸入結(jié)構(gòu)體
public struct MouseInput
{
public int dx;
public int dy;
public uint mouseData;
public MouseEventFlags dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
// 鼠標(biāo)事件標(biāo)志位
[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é)尾
使用這個類庫當(dāng)然可以實現(xiàn)一個自動回復(fù)機(jī)器人,以及消息朋友圈某人更新訂閱,消息訂閱等等,公眾號啊 一些信息的收錄。
以上是使用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表達(dá)式
這篇文章介紹了C#實現(xiàn)遞歸調(diào)用的Lambda表達(dá)式,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-06-06
c#根據(jù)文件類型獲取相關(guān)類型圖標(biāo)的方法代碼
c#根據(jù)文件類型獲取相關(guān)類型圖標(biāo)的方法代碼,需要的朋友可以參考一下2013-05-05
C#使用第三方組件實現(xiàn)動態(tài)解析和求值字符串表達(dá)式
這篇文章主要介紹了C#如何使用第三方組件(LambdaParser、DynamicExpresso、Z.Expressions)實現(xiàn)動態(tài)解析和求值字符串表達(dá)式,感興趣的小伙伴可以跟隨小編一起了解一下2022-06-06

