使用VB.NET实现微信自动化(转译版)

在一个群里看到了一个老哥分享的一个微信自动化的一个类库,便下载了他的Demo,其本质就是模拟鼠标来操作UI,实现UI自动化;然后自己在瞎琢磨研究,写了一个简单的例子,用来获取好友列表,获取聊天列表,以及最后一次接收或者发送消息的时间,以及最后一次聊天的内容,还有自动刷朋友圈,获取朋友圈谁发的,发的什么文字,以及配的图片是什么,什么时候发的,再就是一个根据获取的好友列表,来实现给指定好友发送消息的功能。

引自 https://www.cnblogs.com/1996-Chinese-Chen/p/17663064.html

话不多说,直接进入主题。界面布局清晰,左侧列出好友列表供选择,右侧则是RichTextBox控件,用于向选中的好友发送消息。中间部分展示聊天列表,详细记录好友名称、最近一次聊天的内容及时间。最右侧则是朋友圈的展示区域,可浏览好友发布的动态,包括图片或视频等媒体内容,以及发布时间。整体设计旨在提供便捷、高效的社交体验。 

首先需要在Nuget下载两个包,FlaUI.Core和FlaUI.UIA3,用这两个包,来实现鼠标模拟,UI自动化的,接下来,咱们看代码。

上面展示的是整个界面的截图,接下来,我们来详细探讨一下代码的实现。在界面创建之初,程序会尝试获取微信的进程ID。随后,为获取好友列表、聊天列表和朋友圈的功能分别分配CancelTokenSource和相应的CancelToken,以实现中断取消的功能。这样做可以确保在需要时能够迅速停止数据的获取过程。在代码中,List用于存储朋友圈的详细信息,而Content则用于存储聊天列表的内容。其中,Key对应聊天的用户昵称,Value则是最后一次的聊天内容。为了模拟鼠标滚动以获取更多聊天列表、朋友圈内容和好友列表,我们使用了SendInput方法。此外,FindWindow和GetWindowThreadProcessID函数被用来根据界面名称找到对应的进程ID。这在处理双击朋友圈后弹出的新界面时尤为有用,因为直接使用Process类进行查找可能不太方便。通过这两个函数,我们可以更精确地定位到朋友圈弹出界面的进程,从而进行后续的操作。

Class SurroundingClass
    Private list As List(Of dynamic) = New List(Of dynamic)()
    Private Content As Dictionary(Of String, String) = New Dictionary(Of String, String)()
    <DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
    Public Shared Function SendInput(ByVal nInputs As UInteger, ByVal pInputs As INPUT(), ByVal cbSize As Integer) As UInteger
    <DllImport("user32.dll", EntryPoint:="FindWindow")>
    Private Shared Function FindWindow(ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
    <DllImport("User32.dll", CharSet:=CharSet.Auto)>
    Public Shared Function GetWindowThreadProcessId(ByVal hwnd As IntPtr, <Out> ByRef ID As Integer) As Integer

    Public Sub New()
        InitializeComponent()
        GetWxHandle()
        GetFriendTokenSource = New CancellationTokenSource()
        GetFriendCancellationToken = GetFriendTokenSource.Token
        ChatListTokenSource = New CancellationTokenSource()
        ChatListCancellationToken = ChatListTokenSource.Token
        FriendTokenSource = New CancellationTokenSource()
        FriendCancellationToken = FriendTokenSource.Token
    End Sub

    Private Property FriendCancellationToken As CancellationToken
    Private Property FriendTokenSource As CancellationTokenSource
    Private Property ChatListCancellationToken As CancellationToken
    Private Property ChatListTokenSource As CancellationTokenSource
    Private Property GetFriendCancellationToken As CancellationToken
    Private Property GetFriendTokenSource As CancellationTokenSource
    Private Property ProcessId As Integer
    Private Property wxWindow As Window
    Private Property IsInit As Boolean = False

    Private Sub GetWxHandle()
        Dim process = Process.GetProcessesByName("Wechat").FirstOrDefault()

        If process IsNot Nothing Then
            ProcessId = process.Id
        End If
    End Sub
End Class

接下来则是使用获取的进程ID和Flaui绑定起来,然后获取到微信的主UI界面,

Private Sub InitWechat()
    IsInit = True
'根据微信进程ID绑定FLAUI
    Dim application = FlaUI.Core.Application.Attach(ProcessId)
    Dim automation = New UIA3Automation()
'获取微信window自动化操作对象,唤醒微信
    wxWindow = application.GetMainWindow(automation)
End Sub

接下来是获取好友列表,判断微信界面是否加载,如果没有,就调用InitWeChat方法,然后在下面判断主界面不为空,设置界面为活动界面,然后在主界面找到ui控件的name是通讯录的,然后模拟点击,这样就从聊天界面切换到了通讯录界面,默认的界面第一条都是新朋友,而我没有做就是说当前列表在哪里就从哪里获取,虽然你点击了获取好友列表哪怕没有在最顶部的新朋友那里,也依旧是可以模拟滚动来实现获取好友列表的,然后接下来调用FindAllDescendants,获取主界面的所有子节点,在里面找到所有父节点不为空并且父节点的Name是联系人的节点,之所以是Parent的Name是联系人, 是因为我们的好友列表,都是隶属于联系人这个父节点之下的,找到之后呢,我们去遍历找到的这些联系人,名字不为空的过滤掉了,如果存在同名的也可能会过滤掉,没有做处理,并且,找到的类型必须是ListItem,因为联系人本身就是一个列表,他的子类具体的联系人肯定就是一个列表项目,就需要这样过滤,就可以找到好友列表,同时添加到界面上,在最后我们调用了Scroll方法,模拟滚动700像素,这块可能有的电脑大小不一样或者是微信最大化,可以根据具体情况设置。最后在写了取消获取好友列表的事件。

Class SurroundingClass
    Private Sub button1_Click(ByVal sender As Object, ByVal e As EventArgs)
        If Not IsInit Then
            InitWechat()
        End If

        If wxWindow IsNot Nothing Then

'将微信窗体设置为默认焦点状态
            If wxWindow.AsWindow().Patterns.Window.PatternOrDefault IsNot Nothing Then
                wxWindow.AsWindow().Patterns.Window.Pattern.SetWindowVisualState(FlaUI.Core.Definitions.WindowVisualState.Normal)
            End If
        End If

        wxWindow.FindAllDescendants().Where(Function(s) s.Name = "通讯录").FirstOrDefault().Click(False)
        wxWindow.FindAllDescendants().Where(Function(s) s.Name = "新的朋友").FirstOrDefault()?.Click(False)
        Dim LastName As String = String.Empty
        Dim list = New List(Of AutomationElement)()
        Dim sync = SynchronizationContext.Current
        Task.Run(Function()

                     While True

                         If GetFriendCancellationToken.IsCancellationRequested Then
                             Exit While
                         End If

                         Dim all = wxWindow.FindAllDescendants()
                         Dim allItem = all.Where(Function(s) s.Parent IsNot Nothing AndAlso s.Parent.Name = "联系人").ToList()
                         Dim sss = all.Where(Function(s) s.ControlType = ControlType.Text AndAlso Not String.IsNullOrWhiteSpace(s.Name)).ToList()

                         For Each item In allItem

                             If item.Name IsNot Nothing AndAlso item.ControlType = ControlType.ListItem AndAlso Not String.IsNullOrWhiteSpace(item.Name) AndAlso Not listBox1.Items.Contains(item.Name.ToString()) Then
                                 sync.Post(Function(s)
                                               listBox1.Items.Add(s)
                                           End Function, item.Name.ToString())
                             End If
                         Next

                         Scroll(-700)
                     End While
                 End Function, GetFriendCancellationToken)
    End Sub

    Private Sub button4_Click(ByVal sender As Object, ByVal e As EventArgs)
        GetFriendTokenSource.Cancel()
    End Sub
End Class
Private Sub button4_Click(ByVal sender As Object, ByVal e As EventArgs)
    GetFriendTokenSource.Cancel()
End Sub

接下来是获取朋友圈的事件,找到了进程ID实际上和之前Process获取的一样,此处应该可以是不需要调用Finwindow也可以,找到之后获取Window的具体操作对象,即点击朋友圈弹出的朋友圈界面,然后找到第一个项目模拟点击一下,本意在将鼠标移动过去,不然后面不可以实现自动滚动,在循环里,获取这个界面的所有子元素,同时找到父类属于朋友圈,列表这个的ListItem,找到之后,开始遍历找到的集合,由于找到的朋友圈的昵称还有媒体类型,以及时间,还有具体的朋友圈文字内容都包含在了Name里面,所以就需要我们根据他的格式去进行拆分,获取对应的时间,昵称,还有朋友圈内容,媒体类型等,最后添加到DataGridView里面。

Class SurroundingClass
    Private Sub button3_Click(ByVal sender As Object, ByVal e As EventArgs)
        If Not IsInit Then
            InitWechat()
        End If

        If wxWindow IsNot Nothing Then

'将微信窗体设置为默认焦点状态
            If wxWindow.AsWindow().Patterns.Window.PatternOrDefault IsNot Nothing Then
                wxWindow.AsWindow().Patterns.Window.Pattern.SetWindowVisualState(FlaUI.Core.Definitions.WindowVisualState.Normal)
            End If
        End If

        Dim a = Process.GetProcesses().Where(Function(s) s.ProcessName = "朋友圈")
        wxWindow.FindAllDescendants().Where(Function(s) s.Name = "朋友圈").FirstOrDefault().Click(False)
        Dim handls = FindWindow(Nothing, "朋友圈")
        Dim FridId As Integer = Nothing

        If handls <> IntPtr.Zero Then
            GetWindowThreadProcessId(handls, FridId)
            Dim applicationFrid = FlaUI.Core.Application.Attach(FridId)
            Dim automationFrid = New UIA3Automation()
'获取微信window自动化操作对象
            Dim [Friend] = applicationFrid.GetMainWindow(automationFrid)
            [Friend].FindAllDescendants().FirstOrDefault(Function(s) s.ControlType = ControlType.List).Click(False)
            Dim sync = SynchronizationContext.Current
            Task.Run(Async Function()

                         While True

                             Try

                                 If FriendCancellationToken.IsCancellationRequested Then
                                     Exit While
                                 End If

                                 Dim allInfo = [Friend].FindAllDescendants()
                                 Dim itema = allInfo.Where(Function(s) s.ControlType = ControlType.ListItem AndAlso s.Parent.Name = "朋友圈" AndAlso s.Parent.ControlType = ControlType.List)

                                 If itema IsNot Nothing Then

                                     For Each item In itema
                                         Dim ass = item.FindAllDescendants().FirstOrDefault(Function(s) s.ControlType = ControlType.Text)
                                         Dim index = item.Name.IndexOf(":"c)
                                         Dim name = item.Name.Substring(0, index)
                                         Dim content = item.Name.Substring(index + 1)
                                         Dim split = content.Split(vbLf)

                                         If split.Length > 3 Then
                                             Dim time = split(split.Length - 2)
                                             Dim mediaType = split(split.Length - 3)
                                             Dim FriendContent = split(0._._(split.Length - 3))
                                             Dim con = String.Join(",", FriendContent)

                                             If list.Any(Function(s) s.Content = con) Then
                                                 Continue For
                                             End If

                                             sync.Post(Function(s)
                                                           dataGridView2.Rows.Add(name, s, mediaType, time)
                                                           Dim entity As dynamic = New With {Key
                                                               .Name = name, Key
                                                               .Content = s, Key
                                                               .MediaType = mediaType, Key
                                                               .Time = time
                                                           }
                                                           list.Add(entity)
                                                       End Function, con)
                                         End If
                                     Next

                                     Scroll(-500)
                                     Await Task.Delay(100)
                                 End If

                             Catch ex As Exception
                                 Continue While
                             End Try
                         End While
                     End Function)
        End If
    End Sub

    Private Sub button6_Click(ByVal sender As Object, ByVal e As EventArgs)
        FriendTokenSource.Cancel()
    End Sub
End Class

然后接下来就是获取聊天列表,以及给指定好友发送消息的功能了,在下面这段代码里,上面都是判断有没有设置为活动界面,然后找到所有的子元素,找到属于会话的子节点,并且子节点是ListItem,过滤掉折叠的群聊,如果点击倒折叠的群聊,就得在模拟点击回退回来,这里我没有写具体的代码,不过也很简单,找到对应的聊天列表之后,开始遍历每一个聊天对象,根据Xpath,我们找到了符合条件的Text,这Text包括我们的时间,内容,还有昵称,关于Xpath,不熟悉结构的可以看看我们的C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x64路径下,可能有的Bin里面的版本不是我这个版本,你们可以根据自己的系统版本去找对应64或者32位里面的有一个程序叫做inspect.exe这块可以看需要操作界面的UI结构,然后根据这个去写Xpath就行,在获取倒这些内容之后,我们添加到界面上面去,然后模拟滚动去获取聊天列表。

Class SurroundingClass
    Private Sub button2_Click(ByVal sender As Object, ByVal e As EventArgs)
        If Not IsInit Then
            InitWechat()
        End If

        If wxWindow IsNot Nothing Then

            If wxWindow.AsWindow().Patterns.Window.PatternOrDefault IsNot Nothing Then
                wxWindow.AsWindow().Patterns.Window.Pattern.SetWindowVisualState(FlaUI.Core.Definitions.WindowVisualState.Normal)
            End If
        End If

        wxWindow.FindAllDescendants().Where(Function(s) s.Name = "聊天").FirstOrDefault().Click(False)
        wxWindow.FindAllDescendants().Where(Function(s) s.Name = "妈妈").FirstOrDefault().Click(False)
        Dim sync = SynchronizationContext.Current
        Task.Run(Async Function()
                     Dim obj As Object

                     While True
                         Dim all = wxWindow.FindAllDescendants()

                         Try

                             If ChatListCancellationToken.IsCancellationRequested Then
                                 Exit While
                             End If

                             Dim allItem = all.Where(Function(s) s.ControlType = ControlType.ListItem AndAlso Not String.IsNullOrEmpty(s.Name) AndAlso s.Parent.Name = "会话" AndAlso s.Name <> "折叠的群聊")

                             For Each item In allItem
                                 Dim allText = item.FindAllByXPath("//*/Text")

                                 If allText IsNot Nothing AndAlso allText.Length >= 3 Then
                                     Dim name = allText(0).Name
                                     Dim time = allText(1).Name
                                     Dim content = allText(2).Name

                                     If Content.ContainsKey(name) Then
                                         Dim val = Content(name)

                                         If val <> content Then
                                             Content.Remove(name)
                                             Content.Add(name, content)
                                         End If
                                     Else
                                         Content.Add(name, content)
                                     End If

                                     sync.Post(Function(s)
                                                   dataGridView1.Rows.Add(item.Name, content, time)
                                               End Function, Nothing)
                                 End If
                             Next

                             Scroll(-700)
                             Await Task.Delay(100)
                         Catch __unusedException1__ As Exception
                             Continue While
                         End Try
                     End While
                 End Function, ChatListCancellationToken)
    End Sub

    Private Sub button5_Click(ByVal sender As Object, ByVal e As EventArgs)
        ChatListTokenSource.Cancel()
    End Sub
End Class

接下来有一个发送的按钮的事件,主要功能就是根据所选择的好友列表,去发送RichTextBox的消息,在主要代码块中,我们是获取了PC微信的搜索框,然后设置焦点,然后模拟点击,模拟点击之后将我们选择的好友名称输入到搜索框中,等待500毫秒之后,在重新获取界面的子元素,这样我们的查找结果才可以在界面上显示出来,不等待的话是获取不到的,找到了之后呢,我们拿到默认的第一个然后模拟点击,就到了聊天界面,获取到了聊天界面,然后获取输入信息的 文本框,也就是代码的MsgBox,将他的Text的值设置为我们在Richtextbox输入的值,然后找到发送的按钮,模拟点击发送,即可实现自动发送。

Private Async Sub button7_Click(ByVal sender As Object, ByVal e As EventArgs)
    Dim sendMsg = richTextBox1.Text.Trim()
    Dim itemName = listBox1.SelectedItem?.ToString()

    If Not IsInit Then
        InitWechat()
    End If

    If wxWindow IsNot Nothing Then

        If wxWindow.AsWindow().Patterns.Window.PatternOrDefault IsNot Nothing Then
            wxWindow.AsWindow().Patterns.Window.Pattern.SetWindowVisualState(FlaUI.Core.Definitions.WindowVisualState.Normal)
        End If
    End If

    Dim search = wxWindow.FindAllDescendants().FirstOrDefault(Function(s) s.Name = "搜索")
    search.FocusNative()
    search.Focus()
    search.Click()
    Await Task.Delay(500)
    Dim text = wxWindow.FindAllDescendants().FirstOrDefault(Function(s) s.Name = "搜索").Parent

    If text IsNot Nothing Then
        Await Task.Delay(500)
        Dim txt = text.FindAllChildren().FirstOrDefault(Function(s) s.ControlType = ControlType.Text).AsTextBox()
        txt.Text = itemName
        Await Task.Delay(500)
        Dim item = wxWindow.FindAllDescendants().Where(Function(s) s.Name = itemName AndAlso s.ControlType = ControlType.ListItem).ToList()
        wxWindow.FocusNative()

        If item IsNot Nothing AndAlso item.Count > 0 AndAlso Not String.IsNullOrWhiteSpace(sendMsg) Then

            If item.Count <= 1 Then
                item.FirstOrDefault().Click()
            Else
                item.FirstOrDefault(Function(s) s.Parent IsNot Nothing AndAlso s.Parent.Name.Contains("@str:IDS_FAV_SEARCH_RESULT")).Click()
            End If

            Dim msgBox = wxWindow.FindFirstDescendant(Function(x) x.ByControlType(FlaUI.Core.Definitions.ControlType.Text)).AsTextBox()
            msgBox.Text = sendMsg
            Dim button = wxWindow.FindAllDescendants().Where(Function(s) s.Name = "发送(S)").FirstOrDefault()
            button?.Click()
        End If
    End If
End Sub

下图是获取的好友列表,朋友圈列表,以及聊天列表的信息。

注意:下面是使用c#调用win api模拟鼠标滚动的代码(VB.NET的我没做)。有关SendInput的讲解,详情请看官网https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput

#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

使用这个类库当然可以实现一个自动回复机器人,以及消息朋友圈某人更新订阅,消息订阅等等,公众号等一些信息的收录。站长没有测试,只是转义为VB.NET。

以上是使用FlaUi模拟微信自动化的一个简单Demo,记得好像也可以模拟QQ的,之前简单的尝试了一下,可以获取一些东西.。

蓝奏云下载 Gitee开源地址
未经允许不得转载:桔子雨工作室 » 使用VB.NET实现微信自动化(转译版)
分享到: 生成海报
一个数字化服务提供商

承接外贸建站,软件APP开发

部分产品联系我们
切换注册

登录

忘记密码 ?

您也可以使用第三方帐号快捷登录

切换登录

注册

微信扫一扫关注
如已关注,请回复“登录”二字获取验证码