本文はネットユーザー
藍創精鋭チームより寄稿されました。転載・共有は自由です原文著者:藍創精鋭チーム
原文リンク:https://kesshei.blog.csdn.net/article/details/124955177
目的
ずっとWeChatの一斉送信機能を実現したいと思っていましたが、実現できていませんでした。理由の一つは法律違反を恐れていたからです。ある会社がWeChatのインターフェースを解析したことで何やかんやあって訴えられ、多額の罰金を科された記憶があります。
そこで考えました。もしRPA技術をベースにすれば、WeChatに一切影響を与えません。なぜなら、単に自分のPC操作をシミュレートしているだけであり、WeChat社も私の操作が違法であると認識できないからです。
したがって、このアプローチはおそらく安全な技術に基づくものになります。
そこで試してみました。同時にネット上の資料も参考にし、いくつかのヒントを得ました。
一、FlaUIとは?
FlaUIは、MicrosoftのUIAutomation技術に基づいており、Windows Vistaから導入された全く新しいUI自動化テスト技術です。略してUIAと呼ばれます。最新のWindows SDKでは、UIAはMSAAなど他のUI自動化技術をサポートするコンポーネントと共にリリースされており、Windows Automation APIと呼ばれています。
UIAは、UI自動化のための全く新しいインターフェースとパターンを定義しています。UI要素を走査し条件付きで検索するTreeWalker/FindAll、UI要素の属性(Name、ID、Type、ClassName、Location、Visibilityなど)を読み書きするUIA Property、UI要素の動作(Select、Expand、Resize、Check、Valueなど)を定義するUIA Pattern、そしてテストプログラムが新しいウィンドウのオープンなど特定のイベント発生時に通知を受け取れるUIA Eventインターフェースなどです。
現在、FlaUIが使用する技術はUIA2とUIA3の2つです。 ここでは主にUIA3を使用します。
二、使用手順
1. NuGetパッケージの導入
Install-Package FlaUI.UIA3 -Version 3.2.0
2. 特定の相手にメッセージを送信する簡単な実装
コード(例):
Process[] processes = Process.GetProcessesByName("WeChat");
if (processes.Count() != 1)
{
Console.WriteLine("WeChatが起動していないか、複数のWeChatが起動しています");
}
else
{
//1. WeChatプロセスにアタッチ
using (var app = Application.Attach(processes.First().Id))
{
using (var automation = new UIA3Automation())
{
//2. メインウィンドウを取得
var mainWindow = app.GetMainWindow(automation);
Console.WriteLine("メインウィンドウを取得しました");
//3. アドレス帳に切り替え
var elements = mainWindow.FindAll(FlaUI.Core.Definitions.TreeScope.Subtree, TrueCondition.Default);
var addressBook = mainWindow.FindFirstDescendant(cf => cf.ByName("アドレス帳"));
addressBook.DrawHighlight(System.Drawing.Color.Red);
var path = Debug.GetXPathToElement(addressBook);
Console.WriteLine("アドレス帳をクリック");
addressBook.Click();
//4. 検索
string target = "ファイル転送アシスタント";
var searchTextBox = mainWindow.FindFirstDescendant(cf => cf.ByName("検索")).AsTextBox();
searchTextBox.Click();
Keyboard.Type(target);
Keyboard.Type(VirtualKeyShort.RETURN);
Console.WriteLine("対象を検索中");
//5. ダイアログに切り替え
Thread.Sleep(500);
var searchList = mainWindow.FindFirstDescendant(cf => cf.ByName("検索結果"));
if (searchList != null)
{
var searchItem = searchList.FindAllDescendants().FirstOrDefault(cf => cf.Name == target && cf.ControlType == FlaUI.Core.Definitions.ControlType.ListItem);
searchItem?.DrawHighlight(System.Drawing.Color.Red);
searchItem?.AsListBoxItem().Click();
}
else
{
Console.WriteLine("検索結果が見つかりませんでした");
}
Thread.Sleep(500);
//6. テキストを入力
string sendMsg = "これは私のWeChatからの入力情報です:" + DateTime.Now.ToString();
var msgInput = mainWindow.FindFirstDescendant(cf => cf.ByName("入力")).AsTextBox();
msgInput?.Click();
System.Windows.Forms.Clipboard.SetText(sendMsg);
Keyboard.TypeSimultaneously(new[] { VirtualKeyShort.CONTROL, VirtualKeyShort.KEY_V });
var sendBtn = mainWindow.FindFirstDescendant(cf => cf.ByName("sendBtn"));
sendBtn?.DrawHighlight(System.Drawing.Color.Red);
sendBtn?.Click();
}
}
}
コードにはコメントがあり理解しやすいです。
指定した人を検索し、指定した情報を送信するだけです。完了です。
図示効果は以下の通り:

3. 会話リストを取得して一括送信する実装
コード(例):
Process[] processes = Process.GetProcessesByName("WeChat");
if (processes.Count() != 1)
{
Console.WriteLine("WeChatが起動していないか、複数のWeChatが起動しています");
}
else
{
//1. WeChatプロセスにアタッチ
using (var app = Application.Attach(processes.First().Id))
{
using (var automation = new UIA3Automation())
{
//2. メインウィンドウを取得
var mainWindow = app.GetMainWindow(automation);
Console.WriteLine("メインウィンドウを取得しました");
//3. チャット一覧に切り替え
var elements = mainWindow.FindAll(TreeScope.Subtree, TrueCondition.Default);
var addressBook = mainWindow.FindFirstDescendant(cf => cf.ByName("チャット"));
addressBook.DrawHighlight(System.Drawing.Color.Red);
var path = Debug.GetXPathToElement(addressBook);
addressBook.Click();
Console.WriteLine("チャットに切り替えました");
Thread.Sleep(2000);
//4. チャットリストを取得
//先頭の6件のみ送信
var count = 0;
var searchTextBox = mainWindow.FindFirstDescendant(cf => cf.ByName("セッション")).AsListBoxItem();
while (searchTextBox != null)
{
var list = searchTextBox.FindAllChildren();
foreach (var item in list)
{
count++;
var name = item.Name;
item.Click();
var type = item.ControlType;
item.DrawHighlight(System.Drawing.Color.Red);
var MsgSend = mainWindow.FindFirstDescendant(cf => cf.ByName("入力")).AsTextBox();
var MsgSendButton = mainWindow.FindFirstDescendant(cf => cf.ByName("sendBtn"));
if (MsgSend != null && MsgSendButton != null)
{
MsgSend.Click();
System.Windows.Forms.Clipboard.SetText($"一斉送信メッセージです。無視してください:{DateTime.Now}");
Keyboard.TypeSimultaneously(new[] { VirtualKeyShort.CONTROL, VirtualKeyShort.KEY_V });
MsgSendButton.Click();
Console.WriteLine($"送信しました:{name}");
Thread.Sleep(500);
}
if (count == 6)
{
break;
}
}
if (count == 6)
{
break;
}
for (int i = 0; i < list.Length; i++)
{
searchTextBox.Focus();
Keyboard.Press(VirtualKeyShort.DOWN);
Thread.Sleep(100);
}
searchTextBox = mainWindow.FindFirstDescendant(cf => cf.ByName("セッション")).AsListBoxItem();
Thread.Sleep(2000);
}
}
}
}
このコードは主に先頭6人に一斉送信します。セッションに送信ボタンがない場合は送信しないようにし、影響を最小限に抑えています。
図示効果は以下の通り:

何度か録画しましたが、最後には誰かに削除されてしまいました。気まずいです。
4. FlaUIでページ情報を取得する方法
FlaUInspectツールを開きます。

以下のようにXPathアドレスを確認できます。


FlaUInspectプロジェクトはWPFプロジェクトです。詳細を調べたい方はソースコードを確認し、デバッグしてみてください。
ここでは主に以下の2つの方法で必要な内容を取得できます。
1つ目は以下のように、同じページ内でユニークな名前を使って取得する方法です。
var addressBook = mainWindow.FindFirstDescendant(cf => cf.ByName("チャット"));
2つ目は以下のように、図の2番のXPathアドレスを使って目的のコントロールを見つける方法です。
var infoData = automationElement.FindAllByXPath("/Pane/Pane[1]");
まとめ
全体的に、この技術は非常に便利ですが、QQのように自己描画技術を使用しているものや、QT、Javaベースのアプリケーションでは実現できない可能性があります。対象となるのはMicrosoftの技術製品、WinFormsやWPFなどです。
大まかに言えば、使用時の難易度を下げてくれています。
例えば、このWeChatメッセージ送信機能があれば、自分で拡張できます。指定した人に送る、一斉送信、タイマー送信、タグ送信など、実現すれば個人にとっては非常に役立ちます。
2022年5月30日 改訂
他のユーザーが実行できない場合の解決策
以下のようなエラーが発生する場合。

https://github.com/FlaUI/FlaUI からソースコードをダウンロードし、nuget経由ではなく直接参照することで解決できます。
原因については深く調査していません。