投稿者はネット
著者:陳明達
前の記事:C#を使ったWeChat自動化
原文へのリンク:https//www.cnblogs.com/1996-Chinese-Chen/p/17663064.html
はじめに
先月、グループで釣りをしていると、兄弟がWeChat自動化ライブラリを共有しているのを見て、彼のデモをダウンロードしました。本来は、UIを操作するためにマウスをシミュレートし、UI自動化を実現することです。その後、私は盲目的に研究し、友人リストを取得するための簡単な例を書いて、チャットリストを取得し、メッセージを最後に受信または送信した時間、最後のチャットの内容だけでなく、自動ブラシ友人サークルを取得し、送信したテキストを送信し、画像は何であり、いつ送信されたか、そして取得した友人リストに基づいて、指定した友人にメッセージを送信する機能を実現します。
魅力的な写真をいくつか紹介しましょう:
** 友達リストを取得し、メッセージを送信 **

** チャット記録を取得 **

** 友達の輪 **

本文の本文
言うまでもなく、最初に目に入るのはインターフェイスで、左側は友達リストを取得し、右側にはRichText Boxがあり、左側で選択された友達リストに応じてメッセージを送信し、真ん中はチャットリスト、友達名、最後のチャットの内容、最後のチャットの時間を取得し、最後のチャットの時間を取得し、右端は友達のサークルをブラシし、友達が送った友達のサークルの内容を見つけ、それに付随するメディアは写真かビデオかです。友達の輪の時間。
まず、NuGetで2つのパッケージ、FlaUI.CoreとFlaUI.UIA 3をダウンロードする必要があります。これらの2つのパッケージを使用して、マウスシミュレーション、UI自動化を実現し、次にコードを見てみましょう。

上記は、インターフェイス全体のスクリーンショットです。次に、コードについて話します。インターフェイスが作成されると、WeChatのプロセスIDを取得し、次に、友達リスト、チャットリスト、友達サークルのCancelTokenSource割り当てと関連するCancelTokenを取得して、中断キャンセル機能を実現します。上記のリストは友達サークル情報を格納するために使用され、以下のコンテンツはチャットリストの内容を格納し、キーはチャットユーザーのニックネームです。Valueは最後のチャットコンテンツであり、次のSendInputはマウススクロールをシミュレートするために使用しますので、チャットリスト、友人サークルのコンテンツ、友人リストをスクロールできます。以下のFindWindowでは、GetWindowThreadProcessIDはインターフェイス名に応じて対応するプロセスIDを見つけるために使用されます。ポップアップインターフェイスで友人サークルをダブルクリックすると、プロセスを使用するのは便利ではなく、直接これを参照して友人サークルのポップアップインターフェイスを見つけることができます。
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);
//根据名称获取窗体句柄
[DllImport("user32.dll", EntryPoint = "FindWindow")]
private extern static IntPtr FindWindow(string lpClassName, string lpWindowName);
//根据句柄获取进程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にバインドし、WeChatのメインUIインターフェイスにアクセスします。
void InitWechat()
{
IsInit = true;
//根据微信进程ID绑定FLAUI
var application = FlaUI.Core.Application.Attach(ProcessId);
var automation = new UIA3Automation();
//获取微信window自动化操作对象
wxWindow = application.GetMainWindow(automation);
//唤起微信
}
次に、友達のリストを取得し、WeChatインターフェイスがロードされているかどうかを判断し、そうでなければ、InitWeChatメソッドを呼び出し、メインインターフェイスが空ではないことを判断し、インターフェイスをアクティブなインターフェイスに設定し、メインインターフェイスでUIコントロールの名前がアドレス帳であり、クリックをシミュレートするので、チャットインターフェイスからアドレス帳インターフェイスに切り替え、デフォルトのインターフェイスの最初のものは新しい友達であり、私はしていないということです。新しい友達が一番上にいなくても友達リストの取得をクリックして友達リストの取得をシミュレートすることができます次にFindAllDescendantsを呼び出してメインインターフェイスのすべての子ノードを取得しますそこで親ノードが空でなく親ノードの名前が連絡先のノードであることを見つけます親の名前が連絡先ですなぜなら私たちの友達リストは連絡先の親ノードの下にあるからです見つかった後、見つかった連絡先をトラバースします。名前は空ではなくフィルタリングされ、同じ名前が存在する場合はフィルタリングされ、処理されません。そして、見つかった型はListItemでなければなりません。連絡先自体がリストであり、彼のサブクラスの具体的な連絡先はリスト項目であり、このようにフィルタリングする必要があります。友達のリストを見つけることができ、インターフェイスに追加します。最後にScrollメソッドを呼び出します。アナログスクロール700ピクセル、このコンピュータのサイズが異なるか、WeChat最大化され、特定の状況に応じて設定することができます。最後に、友達リストの取得をキャンセルするイベントを書きました。
/// <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)
{
//将微信窗体设置为默认焦点状态
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 == "联系人").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が実際には前のプロセスと同じであることを発見し、ここでFinwindowを呼び出す必要はありません。ウィンドウの具体的な操作オブジェクトを取得した後、友人サークルをクリックしてポップアップ友人サークルインターフェイスをクリックし、最初の項目を見つけてクリックし、マウスを過去に移動させることを意図しています。そうでなければ、後に自動スクロールを実現することはできません。ループで、このインターフェイスのすべての子要素を取得します。同時に、親クラスが友達のサークルに属していることを見つけ、ListItemのリストを見つけ、見つけた後、コレクションをトラバースし始めます。友達のサークルのニックネームとメディアタイプ、時間、および特定の友達のサークルのテキストコンテンツが名前に含まれているので、彼のフォーマットに応じて分割し、対応する時間、ニックネーム、友達のサークルのコンテンツ、メディアタイプなどを取得し、最後にDataGridViewに追加する必要があります。
private void button3_Click(object sender, EventArgs e)
{
if (!IsInit)
{
InitWechat();
}
if (wxWindow != null)
{
if (wxWindow.AsWindow().Patterns.Window.PatternOrDefault != null)
{
//将微信窗体设置为默认焦点状态
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();
}
次に、チャットリストを取得し、指定された友人にメッセージを送信する機能は、以下のコードでは、上記はアクティブなインターフェイスに設定されているかどうかを判断し、すべての子要素を見つけ、セッションに属する子ノードを見つけ、子ノードはListItemであり、折り畳まれたグループチャットをフィルタリングし、折り畳まれたグループチャットをクリックすると、シミュレートされたクリックバックバックバックに戻る必要があります、ここでは特定のコードを書いていませんが、非常に簡単です、対応するチャットリストを見つけた後、すべてのチャットオブジェクトを探索し始め、Xpathによると、我々は条件付きのテキストを見つけました。このテキストには我々の時間、内容、ニックネームが含まれています。Xpathについて、構造に不慣れな人は我々のC\Program Files(x 86)\Windows Kits\10\b in\10.0.19041.0\x 64パスの下で、あるBinの中のバージョンは私のバージョンではないかもしれませんが、あなたは自分のシステムバージョンに応じて64または32ビットの中に対応するプログラムinspect.exeというインターフェイスのUI構造を見ることができます。これに従ってXpathを書き、これらのコンテンツを取得した後、インターフェイスに追加し、シミュレーションしてチャットリストを取得します。
private void button2_Click(object sender, EventArgs e)
{
if (!IsInit)
{
InitWechat();
}
if (wxWindow != null)
{
if (wxWindow.AsWindow().Patterns.Window.PatternOrDefault != null)
{
//将微信窗体设置为默认焦点状态
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("//*/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();
}
次に、送信ボタンのイベントがあります。主な機能は、選択された友人のリストに基づいて、RichText Boxのメッセージを送信することです。メインコードブロックでは、PC WeChatの検索ボックスを取得し、フォーカスを設定し、シミュレーションクリック後に選択した友人の名前を検索ボックスに入力し、500ミリ秒待った後、インターフェイスのサブ要素を再取得するので、検索結果がインターフェイスに表示されるように、待っていなければ取得できませんが、見つけた後、デフォルトの最初のものを取得してクリックをシミュレートし、チャットインターフェイスに行き、チャットインターフェイスを取得し、入力情報のテキストボックス、つまりコードのMsg Boxを取得し、彼のTextの値をRichtext boxに入力した値に設定し、送信ボタンを見つけて送信をシミュレートし、自動送信を実現します。
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)
{
//将微信窗体设置为默认焦点状态
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 == "发送(S)").FirstOrDefault();
button?.Click();
}
}
}
以下は、私が取得した友人リスト、友人サークルリスト、チャットリストの情報です。

下面是使用 c#调用 win api 模拟鼠标滚动的代码。有关 SendInput 的讲解,详情请看官网 SendInput function (winuser.h)。
#region Scroll Event
void Scroll(int scroll)
{
INPUT[] inputs = new INPUT[1];
// 设置鼠标滚动事件
inputs[0].type = InputType.INPUT_MOUSE;
inputs[0].mi.dwFlags = MouseEventFlags.MOUSEEVENTF_WHEEL;
inputs[0].mi.mouseData = (uint)scroll;
// 发送输入事件
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
}
// 鼠标输入结构体
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
終わり方
このライブラリを使用して、もちろん自動返信ボットを実装することができ、メッセージの友人のサークルの誰かがサブスクリプション、メッセージのサブスクリプションなどを更新し、公開番号やいくつかの情報を収集します。
上記はFlaUiシミュレーションWeChat自動化の簡単なデモです。覚えておいてください。QQをシミュレートすることもできるようです。簡単に試す前に、いくつかのものを取得することができます。コードアドレス:https//gite.com/cxd199645/we-chat-auto.git。ビッグ·ディスカッションへようこそ