QQ消息群发器实现原理及核心代码

时间:2024-02-23 10:16:54

    春节前采用C#做了个《实用通讯录》的小软件,群发邮件能正常执行,群发QQ消息不是很理想,在采用进程打开qq消息界面后复制到QQ消息框的字符串消息被升级后的QQ给屏蔽掉了,真是郁闷~~~

       解决这个问题我想了很多办法,记得以前采用模拟键盘消息的方式可以发送消息给另一个程序,这次也是采用这种方法顺利的解决的这个问题。

      现将主要的实现原理介绍一下:

1,命令打开要发送QQ号码的消息窗体,这个是腾讯软件的一部分,若这个人没有在好友列表,则打开的是临时聊天窗口界面。

2,枚举已经打开的所有windows窗口,寻找QQ聊天的窗体并获得它的句柄。

3,发送键盘消息将要传送的消息字符串传送给这个窗体,

4,发送键盘消息模拟单击发送键,发出消息。

5,关闭这个发送消息窗体。

6,打开下一个聊天的QQ,循环继续。。。。。

 

 

防止腾讯的服务器的过滤功能,这个连续给不同的QQ发送一些相同的信息(被认为广告)或敏感的关键字(非法内容)就会出现发送失败的现象。这个原因是腾讯服务器对消息中继中进行了一些处理。

原因如下:

QQ中"你刚才发送的消息没有发送成功(服务器超时)"的原因  悬赏分:0 - 提问时间2006-10-14 07:43

QQ中“你刚才发送的消息没有发送成功(服务器超时)”的原因 各网友用QQ隐身聊天时,是否经常遇到这种情况:“你刚才发送的消息:没有发送成功(服务器超时)。”? 很多人误以为真的是网络问题,其实不是网络问题,而是你发送的消息中含有敏感字眼时,QQ的网络监控服务器对你友善的提醒。也许你认为你发送的消息中没有敏感字眼,但是,有没有不是你说了算的,当前的网络监控是宁可错杀一千,也不可放过一个。 下面是我在网上找的一个很老的文章: http://tech.sina.com.cn/roll/2003-10-13/1048243193.shtml

2003年10月13日 10:48 ChinaByte 张翼轸/(ChinaByte) 在使用QQ的过程中,腾讯会针对那些因为隐身而通过服务器中转消息的用户监控,这样的一个事实并不是什么大新闻,在几个月前记者谷龙已经将这个事件揭露出来了。当时笔者也曾经就通信*的角度来谈论这个问题。 所以,本文不会再次从通信*的角度来讨论类似腾讯这样对于用户通讯进行监控和过滤的行为,而是会从一个更实际的层面,也就是在回避有关部门或者服务商这样这样的监控是否有法律支持的前提下,讨论一个更加技术层面的问题,就是在实施网络监控和网络过滤的前提下如何尽可能的保护用户合法的通信*。 之所以会想到这个问题,其实还是拜QQ所赐。在几日前偶尔隐身和一个朋友讨论起一些学术问题,所以使用到了一个叫“方法论”的词,相信任何一个对于学术特别是科学有些了解的人都会明白,在讨论许多学术问题的时候,这都是一个很正常而且无法回避的内容。可是奇怪的事情就发生了,每当我的句子中带有“方法论”这个词汇的时候,QQ都会很“友善”的告诉我:“你刚才发送的消息:方法论没有发送成功(服务器超时)。” 刚开始我也以为是自己的网络故障,可是重新连接重新登陆QQ发现情况依旧的时候,笔者开始有些怀疑了,于是打入了一些无关紧要的英文字母和汉字,消息很顺利地被发送的出去,然后故意打入了一些“敏感”的词组,果不其然,熟悉的“你刚才发送的消息:XXX(此处隐去笔者测试使用的敏感词组)没有发送成功(服务器超时)。”再次出现。 此时此刻,笔者终于明白了,原来QQ不仅像谷龙前文报道的那样在对消息进行监控,而且对于一些包含“敏感”字符的消息还进行了过滤,阻止其发送。 对此,笔者不得不有两个疑问: 其一:即使有些词汇真的属于敏感词汇,不适合或者说安全部门不希望看到他们在网络上传播,那么究竟谁来决定哪些是敏感词汇?比如就以笔者碰到的这个例子来说,“方法论”这样一个很严肃的学术概念,既没有色情的成分,当然也不可能包括任何涉及反动或者反对现政权的概念,为什么竟然成为了QQ过滤的对象? 其二:即使真的是过滤,为什么QQ对于那些通过服务器中转的信息的过滤并不是像QQ短信那样正大光明的告诉你其中含有“敏感或者不文明的词语,不能发送”,反而是偷偷摸摸的说什么“服务器超时”,让大多数用户不明就理,还真以为是自己的网络故障呢!

 


7.采用可选择加入扰码的方法,就是给要发送的字符串中间随机加入不规则的符号来混淆,来干扰服务器的过滤监控信息功能,不但实现的消息的有效传输,同时也使对方明白传送消息的内容。

   例如:

原消息文本::免费提供IC卡智能表改造方案,欢迎咨询

                 

    加扰码后::免,费、提♂供′I!C’卡‘智°能、表﹕改‘造♀方″案;,、欢ˊ迎′咨‵询,


8.同时也可以选择随机读取文本消息的方法,就是随机从读取QQMsg.txt文件的一行,作为发送的信息发送。使服务器很难监控到时自动发送的消息。

 

 核心代码片段:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Threading;

namespace QQSendMessage
{
    public partial class QQSendMessage : Form
    {
        [DllImport("user32.dll")]
        static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
        [DllImport("user32.dll")]
        static extern IntPtr GetDlgItem(IntPtr hDlg, int nIDDlgItem);
        [DllImport("user32.dll", SetLastError = true)]
        public static extern IntPtr FindWindowEx(IntPtr parentHandle, int childAfter, string className, int windowTitle);
        [DllImport("user32.dll", EntryPoint = "SendMessage")]
        static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, int wParam, string lParam);
        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        public static extern IntPtr GetModuleHandle(string lpModuleName);

        IntPtr hwndQQ;
        IntPtr hwnd1;
        IntPtr hwnd2;
        IntPtr hwnd3;
        IntPtr hwnd4;
        private DateTime qqTimer = DateTime.Now;
        private DateTime qqqTimer = DateTime.Now;

        public QQSendMessage()
        {
            InitializeComponent();
        }

        private void MySendMessage()
        {
            string machinename = System.Environment.MachineName;//获得计算机名
            Process[] processlist = Process.GetProcesses(machinename);//得到所有进程
            foreach (Process p in processlist)//列举每个进程
            {
                if (p.MainWindowTitle != "")//标题是否为空,不为空执行下面代码
                {
                    if (p.MainWindowTitle.ToString().Substring(0, 1) == "与" || p.MainWindowTitle.ToString().Substring(0, 1) == "临")//查看窗口标题第一个字是否是“与”。如果是的,说明是QQ窗口
                    {
                        hwndQQ = FindWindow("#32770", p.MainWindowTitle.ToString());
                        hwnd1 = GetDlgItem(hwndQQ, 0);
                        hwnd2 = GetDlgItem(hwnd1, 0);
                        hwnd3 = GetDlgItem(hwnd2, 894);
                        SendMessage(hwnd3, 194, 0, this.txtInput.Text);//向QQ输入框粘贴字符,this.textBox1.Text是要发送的文字信息
                        hwnd4 = GetDlgItem(hwnd1, 1);
                        SendMessage(hwnd4, 245, 0, Convert.ToString(0));
                    }
                }
            }
        }

        /// <summary>
        /// 单个用户发送消息
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSend_Click(object sender, EventArgs e)
        {
            string sQQNumber = "";

            if (listQQs.SelectedItem == null)
            {
                return;
            }

            sQQNumber = listQQs.SelectedItem.ToString();

            string s = "tencent://message/?uin=" + sQQNumber + "&Site=im.qq.com&Menu=yes";//是对方的QQ号
            Process.Start(s);
            Thread.Sleep(300);
            this.MySendMessage();
            Thread.Sleep(100);
            btnClose_Click(null, null);
        }

        /// <summary>
        /// 循环发送消息
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSSend_Click(object sender, EventArgs e)
        {
            if (txtInput.Text.Length == 0)
            {
                string strMessage = "请输入要发送的消息";
                MessageBox.Show(strMessage, "Message", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }
            i = 0;
           
            this.timer1.Interval =  System .Convert .ToInt16(  txtMs.Text);
            this.timer1.Enabled = true;
           
        }

        /// <summary>
        /// 停止发送消息
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSStop_Click(object sender, EventArgs e)
        {
            this.timer1.Enabled = false;
        }

        /// <summary>
        /// 计时器开始发送消息
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        ///
        private int i = 0;
        private void Qtimer_Tick(object sender, EventArgs e)
        {

            string sQQNumber = "";
            if (i  >= listQQs.Items.Count)
            {
                //MessageBox.Show(i.ToString());
                timer1.Enabled = false;
                btnClose_Click(null, null);
                return;
            }
            listQQs.SelectedIndex = i;
            sQQNumber = listQQs.Items[i].ToString();
            i++;

            string s = "tencent://message/?uin=" + sQQNumber + "&Site=im.qq.com&Menu=yes";//是对方的QQ号
            Process.Start(s);
            Thread.Sleep(300);
            this.MySendMessage();
            Thread.Sleep(100);
            btnClose_Click(null, null);


            if (DateTime.Now > qqTimer)
            {
                this.MySendMessage();
                //qqTimer.AddMilliseconds(Convert.ToInt32(this.txtMs.Text.Trim()));
                qqTimer = DateTime.Now.AddMilliseconds(Convert.ToInt32(this.txtMs.Text.Trim()));
            }
        }


       /// <summary>
       /// 向群组发消息
       /// </summary>
       /// <param name="sender"></param>
       /// <param name="e"></param>


        private void btnClose_Click(object sender, EventArgs e)
        {
            string machinename = System.Environment.MachineName;//获得计算机名
            Process[] processlist = Process.GetProcesses(machinename);//得到所有进程
            foreach (Process p in processlist)//列举每个进程
            {
                if (p.MainWindowTitle != "")//标题是否为空,不为空执行下面代码
                {
                    if (p.MainWindowTitle.ToString().Substring(0, 1) == "与")//查看窗口标题第一个字是否是“与”。如果是的,说明是QQ窗口
                    {
                        p.CloseMainWindow();
                    }
                }
            }
        }

        private void QQSendMessage_Load(object sender, EventArgs e)
        {
            txtInput.Text = "一卡通水电暖  www.duanliqing.kudo.cn";
            this.Text = this.Text + "  Group = " + butQQin.Tag;
        }

        private void butQQin_Click(object sender, EventArgs e)
        {
            MyAddrList.数据访问层.DBPersonal objData = new MyAddrList.数据访问层.DBPersonal();
            objData.GetQQs(butQQin .Tag.ToString () , listQQs);
        }

    }
}

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/leek/archive/2010/02/22/5315862.aspx