C# 实现微信PC版通讯录自动化群发小工具 支持多条图片与文字消息 支持快捷键

该代码示例展示了如何使用FlaUI自动化库控制微信进程,实现发送文本、图片消息,以及模拟键盘操作进行聊天窗口滚动和消息发送。程序还包含中断和处理消息间隔的功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

成品截图

在这里插入图片描述

使用自动化库

在这里插入图片描述


发送逻辑*sender.cs*

using FlaUI.Core.AutomationElements;
using FlaUI.UIA3;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using WindowsInput;
using WindowsInput.Native;

namespace WechatSend
{

    public class WindowUtil
    {
        static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
        static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);
        const UInt32 SWP_NOSIZE = 0x0001;
        const UInt32 SWP_NOMOVE = 0x0002;
        const UInt32 SWP_SHOWWINDOW = 0x0040;

        [DllImport("user32.dll")]
        static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);

        public static bool SetWindowTop(IntPtr hWnd, bool isTop)
        {
            if (isTop)
            {
                return SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
            }
            else
            {
                return SetWindowPos(hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
            }
        }

    }
    public class Sender
    {
        static int ScreenHeight = Screen.PrimaryScreen.Bounds.Height;
        static int ScreenWidth = Screen.PrimaryScreen.Bounds.Width;

        static FlaUI.Core.AutomationElements.Window wechatWindow;
        static FlaUI.Core.AutomationElements.ListBox listBox;
        static FlaUI.Core.Application app;
        static int scrollDelta = 3;
        static Process attachWechatProcess;
        static string content = "";
        static bool EndingFlag = false;

        static volatile bool InterruptFlag = false;

        static HashSet<String> wechatScanHistory = new HashSet<String>();
        static InputSimulator sim = new InputSimulator();

        public static String[] SplitFlag = { "\n----------------\n" };

        public static void SendImage(string path)
        {

            CS.RunInMainContext(() =>
            {
                var img = Image.FromFile(path);
                Clipboard.SetImage(img);
            });
            Thread.Sleep(1500);
            sim.Keyboard.KeyDown(VirtualKeyCode.CONTROL);
            sim.Keyboard.KeyPress(VirtualKeyCode.VK_V);
            sim.Keyboard.KeyUp(VirtualKeyCode.CONTROL);
            SubmitSend();
        }

        public static void SendText(string text)
        {
            wechatWindow.FindFirstDescendant(cf => cf.ByName("输入"))?.AsTextBox().Enter(text);
            Thread.Sleep(3000);
            SubmitSend();
        }

        public static void EnterEditView()
        {
            wechatWindow.FindFirstDescendant(cf => cf.ByName("发消息"))?.Click();
        }

        public static void BackContactView()
        {
            wechatWindow.FindFirstDescendant(cf => cf.ByName("通讯录"))?.Click();
        }

        public static void SubmitSend()
        {
            Thread.Sleep(600);
            wechatWindow.FindFirstDescendant(cf => cf.ByName("sendBtn"))?.Click();
        }

        public static bool HandleInterval(string text)
        {
            if (text.StartsWith("间隔") && text.EndsWith("秒"))
            {
                var retPattern = new Regex(@"间隔(\d+)秒", RegexOptions.Compiled);
                string s = retPattern.Match(text).Groups[1].Captures[0].Value;
                if (int.Parse(s) > 0)
                {
                    Thread.Sleep(int.Parse(s) * 1000);
                    Debug.WriteLine(@"等待间隔秒数:" + int.Parse(s));
                    return true;
                }
            }
            return false;
        }

        public static void Scan()
        {
            // 扫描
            foreach (ListBoxItem item in listBox.Items)
            {
                if (InterruptFlag)
                {
                    Debug.WriteLine("中止发送==");
                    break;
                }
                else
                {
                    Debug.WriteLine("继续发送==");
                }

                if (item.Name.Length == 0 || item.Name == "新的朋友" || item.Name == "公众号" || item.Name == "微信团队" || item.Name == "文件传输助手") continue;
                item.Click();
                Thread.Sleep(2000);
                var childs = wechatWindow.FindFirstDescendant(cf => cf.ByName("微信号:"))?.Parent.FindAllChildren();
                var wechatNumItem = childs[childs.Length - 1];
                var wechatNumText = wechatNumItem.Name;
                if (wechatScanHistory.Contains(wechatNumText))
                {
                    if (EndingFlag)
                    {
                        continue;
                    }
                    break;
                }
                else
                {
                    scrollDelta = 3;
                    wechatScanHistory.Add(wechatNumText);
                    // 执行发送
                    Debug.WriteLine(item.Name + "\t" + wechatNumText);
                    EnterEditView();
                    SplitMsgContent(content);
                    Thread.Sleep(1500);
                    BackContactView();
                    Thread.Sleep(1000);
                }
            }

            if (EndingFlag)
            {
                EndingFlag = false;
            }
        }

        public static void InitApp()
        {
            var wechatProcess = Process.GetProcessesByName("WeChat");
            if (wechatProcess.Length != 1)
            {
                MessageBox.Show("未找到微信或者微信在多开环境运行 " + wechatProcess.Length, "提示",
                     MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }
            Debug.WriteLine($"寻找进程:{wechatProcess.Length}");
            attachWechatProcess = wechatProcess.First();
            app = FlaUI.Core.Application.Attach(attachWechatProcess.Id);
        }

        public static void NotifyStop()
        {
            InterruptFlag = true;
        }


        public static void StartSend(string richText)
        {
            wechatScanHistory.Clear();
            content = richText;
            InitApp();
            WindowUtil.SetWindowTop(attachWechatProcess.MainWindowHandle, true);
            InterruptFlag = false;
            using (var automation = new UIA3Automation())
            {
                wechatWindow = app.GetMainWindow(automation);
                wechatWindow.FocusNative();
                Debug.WriteLine("附加到进程:" + wechatWindow.Title);
                BackContactView();
                Thread.Sleep(2000);
                var i = wechatWindow.FindFirstDescendant(cf => cf.ByName("联系人"));
                i.Click();
                i.FocusNative();

                // kApp.MouseMove(i.BoundingRectangle.X + 50, i.BoundingRectangle.Y + 100);
                var x = i.BoundingRectangle.X;
                var y = i.BoundingRectangle.Y;
                var w = i.BoundingRectangle.Width;
                var h = i.BoundingRectangle.Height;

                listBox = i.AsListBox();

                Debug.WriteLine($"Screen: {Screen.PrimaryScreen.Bounds.Height}");
                while (true)
                {
                    if (InterruptFlag) break;
                    Scan();
                    if (scrollDelta < -10)
                    {
                        EndingFlag = true;
                        // 扫描最后一列
                        Scan();
                        break;
                    }
                    else
                    {
                        if (InterruptFlag) break;
                        BackContactView();
                        Debug.WriteLine($"滑动~~~{scrollDelta}");
                        // 循环滑动,直到最上面一个微信不出现在  
                        sim.Mouse.MoveMouseTo((x + w / 2) * 65536 / ScreenWidth, (y + h / 2) * 65536 / ScreenHeight);
                        sim.Mouse.VerticalScroll(-(Math.Max(scrollDelta--, 1)));
                        Thread.Sleep(1500);
                    }
                }

            }

            CS.RunInMainContext(() =>
            {
                if (InterruptFlag)
                {
                    MessageBox.Show("执行中断", "微信群发 - 提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
                else
                {
                    MessageBox.Show("执行完毕", "微信群发 - 提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
            });
            WindowUtil.SetWindowTop(attachWechatProcess.MainWindowHandle, false);
            app.Dispose();
            Debug.WriteLine("-----执行结束");
        }

        public static void TestSend(string content)
        {
            InitApp();
            using (var automation = new UIA3Automation())
            {
                wechatWindow = app.GetMainWindow(automation);
                EnterEditView();
                SplitMsgContent(content);
                BackContactView();
            }
        }

        public static void SplitMsgContent(string text)
        {
            Debug.WriteLine("输入文本:" + text);
            // 处理开始与结尾的分隔符
            if (text.EndsWith(SplitFlag[0]))
            {
                text = text.Substring(0, text.Length - SplitFlag[0].Length);
            }
            if (text.StartsWith(SplitFlag[0]))
            {
                text = text.Substring(SplitFlag[0].Length);
            }
            // 分隔消息
            String[] lines = text.Split(SplitFlag, StringSplitOptions.RemoveEmptyEntries);
            foreach (string line in lines)
            {
                if (HandleInterval(line))
                {
                    continue;
                }
                if (File.Exists(line))
                {
                    Debug.WriteLine("发送图片:" + line);
                    SendImage(line);
                    continue;
                }
                Debug.WriteLine("发送文本:" + line);
                SendText(line);
            }
        }
    }
}


界面逻辑 Form1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WechatSend
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Sender.StartSend(this.richTextBoxContent.Text);
        }

        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {

        }
         
        private void Form1_Load(object sender, EventArgs e)
        {

            CS.InitMainContext(SynchronizationContext.Current);

            GlobalHotKey.RegisterHotKey("Ctrl + Shift + Q", () => {
                string text = this.richTextBoxContent.Text;
                CS.runInBackground(() =>
                {
                    Sender.StartSend(text);
                });
            });

            GlobalHotKey.RegisterHotKey("Ctrl + Shift + E", () => {
                System.Environment.Exit(0);
            });

            GlobalHotKey.RegisterHotKey("Ctrl + Shift + S", () => 
            {
                Debug.WriteLine("中断发送");
                Sender.NotifyStop();
                // Sender.SendImage(@"C:\Users\Administrator\AppData\Local\Google\Chrome\User Data\Default\Extensions\ibefaeehajgcpooopoegkifhgecigeeg\8.0.14_0\assets\imgs\template-panel\image-scroll-box\img-scroll-1.jpg");
            });

            Debug.WriteLine($"Screen: {Screen.PrimaryScreen.Bounds.Height}x{Screen.PrimaryScreen.Bounds.Width}");
        }

        private void buttonAddImage_Click(object sender, EventArgs e)
        {

        }

        private void Form1_DragEnter(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                e.Effect = DragDropEffects.All;
            }
            else
            {
                String[] strGetFormats = e.Data.GetFormats();
                e.Effect = DragDropEffects.None;
            }
        }

        private void Form1_DragDrop(object sender, DragEventArgs e)
        {
            string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
            foreach (string file in files)
            {
                this.richTextBoxContent.AppendText(file);
                this.richTextBoxContent.AppendText(Sender.SplitFlag[0]);
            }
        }

        private void richTextBoxContent_DoubleClick(object sender, EventArgs e)
        {
            if (!this.richTextBoxContent.Text.EndsWith("\n"))
            {
                this.richTextBoxContent.AppendText(Sender.SplitFlag[0]);
            }
        }
    }
}

快捷键注册 GlobakHotkey.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace WechatSend
{
    public class GlobalHotKey : IDisposable
    {
        /// <summary>
        /// Registers a global hotkey
        /// </summary>
        /// <param name="aKeyGesture">e.g. Alt + Shift + Control + Win + S</param>
        /// <param name="aAction">Action to be called when hotkey is pressed</param>
        /// <returns>true, if registration succeeded, otherwise false</returns>
        public static bool RegisterHotKey(string aKeyGestureString, Action aAction)
        {
            var c = new KeyGestureConverter();
            KeyGesture aKeyGesture = (KeyGesture)c.ConvertFrom(aKeyGestureString);
            return RegisterHotKey(aKeyGesture.Modifiers, aKeyGesture.Key, aAction);

        }

        public static bool RegisterHotKey(ModifierKeys aModifier, Key aKey, Action aAction)
        {
            if (aModifier == ModifierKeys.None)
            {
                throw new ArgumentException("Modifier must not be ModifierKeys.None");
            }
            if (aAction is null)
            {
                throw new ArgumentNullException(nameof(aAction));
            }

            System.Windows.Forms.Keys aVirtualKeyCode = (System.Windows.Forms.Keys)KeyInterop.VirtualKeyFromKey(aKey);
            currentID = currentID + 1;
            bool aRegistered = RegisterHotKey(window.Handle, currentID,
                                        (uint)aModifier | MOD_NOREPEAT,
                                        (uint)aVirtualKeyCode);

            if (aRegistered)
            {
                registeredHotKeys.Add(new HotKeyWithAction(aModifier, aKey, aAction));
            }
            return aRegistered;
        }

        public void Dispose()
        {
            // unregister all the registered hot keys.
            for (int i = currentID; i > 0; i--)
            {
                UnregisterHotKey(window.Handle, i);
            }

            // dispose the inner native window.
            window.Dispose();
        }

        static GlobalHotKey()
        {
            window.KeyPressed += (s, e) =>
            {
                registeredHotKeys.ForEach(x =>
                {
                    if (e.Modifier == x.Modifier && e.Key == x.Key)
                    {
                        x.Action();
                    }
                });
            };
        }

        private static readonly InvisibleWindowForMessages window = new InvisibleWindowForMessages();
        private static int currentID;
        private static uint MOD_NOREPEAT = 0x4000;
        private static List<HotKeyWithAction> registeredHotKeys = new List<HotKeyWithAction>();

        private class HotKeyWithAction
        {

            public HotKeyWithAction(ModifierKeys modifier, Key key, Action action)
            {
                Modifier = modifier;
                Key = key;
                Action = action;
            }

            public ModifierKeys Modifier { get; }
            public Key Key { get; }
            public Action Action { get; }
        }

        // Registers a hot key with Windows.
        [DllImport("user32.dll")]
        private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
        // Unregisters the hot key with Windows.
        [DllImport("user32.dll")]
        private static extern bool UnregisterHotKey(IntPtr hWnd, int id);

        private class InvisibleWindowForMessages : System.Windows.Forms.NativeWindow, IDisposable
        {
            public InvisibleWindowForMessages()
            {
                CreateHandle(new System.Windows.Forms.CreateParams());
            }

            private static int WM_HOTKEY = 0x0312;
            protected override void WndProc(ref System.Windows.Forms.Message m)
            {
                base.WndProc(ref m);

                if (m.Msg == WM_HOTKEY)
                {
                    var aWPFKey = KeyInterop.KeyFromVirtualKey(((int)m.LParam >> 16) & 0xFFFF);
                    ModifierKeys modifier = (ModifierKeys)((int)m.LParam & 0xFFFF);
                    if (KeyPressed != null)
                    {
                        KeyPressed(this, new HotKeyPressedEventArgs(modifier, aWPFKey));
                    }
                }
            }

            public class HotKeyPressedEventArgs : EventArgs
            {
                private ModifierKeys _modifier;
                private Key _key;

                internal HotKeyPressedEventArgs(ModifierKeys modifier, Key key)
                {
                    _modifier = modifier;
                    _key = key;
                }

                public ModifierKeys Modifier
                {
                    get { return _modifier; }
                }

                public Key Key
                {
                    get { return _key; }
                }
            }


            public event EventHandler<HotKeyPressedEventArgs> KeyPressed;

            #region IDisposable Members

            public void Dispose()
            {
                this.DestroyHandle();
            }

            #endregion
        }
    }
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值