短信设备软件实现篇短信收发

分享

短信设备软件终于完成,虽然不很完善,但已可以完成所需的大多功能。在软件完成期间有很多的支持,在这里感谢大家的支持,谢谢大家。

运行主界面: 

183605laq5m8thlejfia4t

界面实现:Form1,主界面 上方pictureBox控件,下方用splitContainer控件分为两个部分,左边嵌套菜单窗体,右边嵌套对应每个菜单项要显示的窗体;Form2,菜单窗体;其他Form,对应菜单项窗体。

Form1:设置MaxmizeBox属性为False,使最大化按钮无效;设置AutoSizeMode属性为GrowAndShrink,不能手动调整窗体的大小;设置Start Position属性为CenterScreen。

构造函数完成窗体的嵌套

public Form1()
   {
       InitializeComponent();
    
       //菜单窗体
       f2 = new Form2();
       f2.TopLevel = false;
       f2.FormBorderStyle = FormBorderStyle.None;
       f2.Parent = this.splitContainer1.Panel1;
      f2.Show();
      f2.F2Event += new Form2.F2EventHandler(f2_F2Event);         //绑定Form2事件
   
      //首页窗体
      f3 = new Form3();
      f3.TopLevel = false;
      f3.FormBorderStyle = FormBorderStyle.None;
      f3.Parent = this.splitContainer1.Panel2;
      f3.Show();
      f3.BringToFront();
   
      //发送短信窗体
      f4 = new Form4();
      f4.TopLevel = false;
      f4.FormBorderStyle = FormBorderStyle.None;
      f4.Parent = this.splitContainer1.Panel2;
      f4.Show();
   
      //读取短信窗体
     f5 = new Form5();
      f5.TopLevel = false;
      f5.FormBorderStyle = FormBorderStyle.None;
      f5.Parent = this.splitContainer1.Panel2;
      f5.Show();
   
      //设备管理窗体
      f6 = new Form6();
      f6.TopLevel = false;
      f6.FormBorderStyle = FormBorderStyle.None;
      f6.Parent = this.splitContainer1.Panel2;
      f6.Show();
   
      //帮助窗体
      f7 = new Form7();
      f7.TopLevel = false;
      f7.FormBorderStyle = FormBorderStyle.None;
      f7.Parent = this.splitContainer1.Panel2;
      f7.Show();
  }


Form2:菜单窗体,仅有6个菜单按钮,单击时引发相应事件(退出按钮除外,退出按钮单击后只运行Application.Exit();即退出程序)

private void button_Click(object sender, EventArgs e)
  {
      GSMEventArgs ge = new GSMEventArgs();
      ge.Message = ((Button)sender).Text;
      if (F2Event != null)
      {
         F2Event(this, ge);
      }
  }


ge传递按钮信息到主窗体,由主窗体处理相关信息

  void f2_F2Event(object sender, GSMEventArgs e)
   {
       switch (e.Message)
       {
           case "首页":
               f3.BringToFront();          //首页窗体移至最前
               break;
    
           case "发送短信":
              f4.BringToFront();          //发送短信窗体移至最前
              break;
   
          case "读取短信":
              f5.BringToFront();          //读取短信窗体移至最前
              break;
   
          case "设备管理":
              f6.BringToFront();          //设备管理窗体移至最前
              break;
   
          case "帮助":
              f7.BringToFront();          //帮助窗体移至最前
              break;
   
         default: break;
   
      }
  }


事件F2Event对应代码

  //委托
   public delegate void F2EventHandler(object sender, GSMEventArgs e);
   
  //事件
  public event F2EventHandler F2Event;


类GSMEventArgs 代码(位于Form1代码的下面)

  public class GSMEventArgs : EventArgs
  {
      public string Message;
  }


类 GSMEventArgs用于在不同窗体的事件传递有关信息

然后每个窗体上放上对应控件,界面设计完成!

首页和退出功能上面已经完成,下面不再讲解

设备类实例化:使用设备类前要添加对GSMMODEM.Dll的引用并:

using GSMMODEM;


设备类完成短信的发送、接收、和读取;要允许多个窗体访问;C#中没有全局变量的概念,所有变量都必须在类中定义;所以本程序采用了一个替代方案
     在类Program(位于文件Program.cs中)中定义并实例化静态设备对象,代码如下

   static class Program
   {
       public static GSMModem gm = new GSMModem();         //设备类,全局变量,方便不同窗体使用 添加代码,定义并实例化设备类
   
       /// <summary>
       /// 应用程序的主入口点。
       /// </summary>
       [STAThread]
       static void Main()
      {
          Application.EnableVisualStyles();
          Application.SetCompatibleTextRenderingDefault(false);
         Application.Run(new Form1());
      }
  }


这样,在所有窗体中均可访问这个设备对象。访问方式:Program.gm


  • 设备管理:设备管理目前只支持单个猫的控制,需要可以自己添加。

    设备管理完成设备端口、波特率设置及设备的打开与关闭。Form6为设备管理串口,可以完成设备的设置与管理,设置波特率和串口号,及是否自动打开设备

    构造函数添加控件初始化代码:

  //初始化控件comboBox1
   foreach (string s in SerialPort.GetPortNames())
   {
       comboBox1.Items.Add(s);
   }
   if (comboBox1.Items.Count > 0)
   {
       //comboBox1.SelectedIndex = 0;
   }
  else
  {
      label3.Text = "未检测到串口";
  }
   
  //初始化控件comboBox2
  //comboBox2.SelectedIndex = 0;

串口号用静态方法SerialPort.GetPortNames()扫描得到,是当前电脑注册表里的串口号。

设置文件位于Properties文件夹下,双击Settings.settings文件进入设置的管理窗口,添加ComPort、BaudRate、AutoOpen属性,类型分别为:string、string、bool;用于程序设置;

   private void Form6_Load(object sender, EventArgs e)
   {
       comboBox1.SelectedText = Properties.Settings.Default.ComPort;               //读取设置
   
       comboBox2.SelectedText = Properties.Settings.Default.BaudRate;
    
       checkBox1.Checked = Properties.Settings.Default.AutoOpen;
    
       //设备属性初始化
      Program.gm.ComPort = Properties.Settings.Default.ComPort;
      Program.gm.BaudRate = Convert.ToInt32(Properties.Settings.Default.BaudRate);
      Program.gm.AutoDelMsg = true;
   
      if (Properties.Settings.Default.AutoOpen)                                   //根据设置决定是否打开设备
      {
          button1_Click(sender, e);                                               //调用button1的事件,完成设备的打开
      }
  }

窗体加载时写入上次程序关闭时的(设置文件中的)端口号、波特率、是否自动打开设备;若自动打开为true则打开设备

   private void button1_Click(object sender, EventArgs e)
   {
       if (Program.gm.IsOpen == false)
       {
           try
           {
               Program.gm.Open();
               label3.Text = "打开成功";
               label3.ForeColor = Color.Green;
   
              button1.Text = "关闭设备";
          }
          catch
          {
              label3.Text = "打开失败";
             label3.ForeColor = Color.Red;
          }
      }
      else
      {
          try
          {
              Program.gm.Close();
              label3.Text = "关闭成功";
              label3.ForeColor = Color.Red;
   
              button1.Text = "打开设备";
          }
          catch
          {
              label3.Text = "关闭失败";
              label3.ForeColor = Color.Yellow;
          }
      }
  }


button1_Click事件完成设备的打开、关闭。

当comboBox1、comboBox2、checkBox1选项改变时,写入设置并保存设置

  private void checkBox1_CheckedChanged(object sender, EventArgs e)
  {
      Properties.Settings.Default.AutoOpen = checkBox1.Checked;                   //更改设置文件
  
      Properties.Settings.Default.Save();                                         //保存设置文件
  }


三个事件的代码一致,其他两个仅多出了设备类属性的改变,这里就不贴出来了,详见工程项目文件的源代码

  • 发送短信:窗体Form4完成短信的发送。
    发送短信 目标号码部分:用listview控件实现,添加按钮完成单个号码的添加

   private void button6_Click(object sender, EventArgs e)
   {
       Form9 adNum = new Form9();
       adNum.Owner = this;
    
       //事件 传值
       adNum.AddNumEvent += new Form9.AddNumEventHandler(adNum_AddNumEvent);
    
       adNum.ShowDialog();
  }

Form9是添加号码窗体 通过事件AddNumEvent完成号码等信息的传递

  void adNum_AddNumEvent(object sender, GSMEventArgs e)
  {
      string[] s = e.Message.Split(',');
   
      listView2.Items.Add(s[1]);
      listView2.Items
[listView2.Items.Count - 1].SubItems.Add(s[0]);
      listView2.Items[listView2.Items.Count - 1].SubItems.Add(s[2]);
  }


Form9中对应代码:

  private void button1_Click(object sender, EventArgs e)
   {
       if (Regex.IsMatch(textBox2.Text, @"^(1[3|5|8][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9])[        DISCUZ_CODE_13        ]quot;))
       {
           //引发事件
           if (AddNumEvent != null)
           {
               GSMEventArgs ge = new GSMEventArgs();
               ge.Message = textBox1.Text + "," + textBox2.Text + "," + textBox3.Text;
              AddNumEvent(this, ge);
          }
          this.Close();
      }
      else
      {
          MessageBox.Show("手机号码不正确");
      }
  }


事件的定义:

  //委托
  public delegate void AddNumEventHandler(object sender, GSMEventArgs e);
   
  //事件
  public event AddNumEventHandler AddNumEvent;


这样即完成了listview的号码添加。
删除号码:

   private void button7_Click(object sender, EventArgs e)
   {
       ListView.SelectedIndexCollection numToRemove = listView2.SelectedIndices;
       try
       {
           int j = 0;
           foreach (int i in numToRemove)
          {
               listView2.Items.RemoveAt(i - j);
              j = j + 1;
          }
      }
      catch (Exception ex)
      {
          MessageBox.Show(ex.Message);
      }
  }


这是从listview控件中删除对应条目,完成手机号码删除。

另外还有号码导入功能,完成多个号码的添加,目前只能从excel中导入;而且需要满足格式要求。代码如下:

   private void button8_Click(object sender, EventArgs e)
  {
       //xPath指示excel文件路径
       string xPath = null;
       OpenFileDialog openFileDialog1 = new OpenFileDialog();
      openFileDialog1.Filter = "Excel Files(*.xls)|*.xls";
       openFileDialog1.Title = "导入手机号码";
      listView2.SelectedItems.Clear();
       // Show the Dialog.
      // If the user clicked OK in the dialog and
      // a .CUR file was selected, open it.
      if (openFileDialog1.ShowDialog() == DialogResult.OK)
      {
          xPath = openFileDialog1.FileName;
          string strCon;
          strCon = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + xPath + ";Extended Properties='Excel 8.0;HDR=False;IMEX=1'";
          OleDbConnection OleConn = new OleDbConnection(strCon);
          string sql = "SELECT * FROM  [Sheet1$]";
          DataSet ds = new DataSet();
          try
          {
              OleConn.Open();
              OleDbDataAdapter da = new OleDbDataAdapter(sql, strCon);
              da.Fill(ds, "sheet1");
              OleConn.Close();
          }
          catch
          {
              MessageBox.Show("无法打开!请关闭其他打开此文件的程序后再试");
          }
          DataTable dt = ds.Tables[0];
          try
          {
              foreach (DataRow row in dt.Rows)
             {
                  string str = row["手机号码"].ToString();
                  if (Regex.IsMatch(str, @"^(1[3|5|8][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9])[        DISCUZ_CODE_16        ]quot;))
                  {
                      ListViewItem lvi = new ListViewItem(str);
                      lvi.SubItems.Add(row["姓名"].ToString());
                      lvi.SubItems.Add(row["备注"].ToString());
                      listView2.Items.Add(lvi);
                  }
                  else
                  {
                      MessageBox.Show("表格第二列不是手机号码或是有错误号码!\n请检查后再试");
                      break;
                  }
              }
          }
          catch
          {
              MessageBox.Show("程序支持文件为前三列都有内容的xls表格\n第一列“姓名”,第二列“手机号码”,第三列“备注”");
         }
      }
   
  }


excel文件第一行前三列内容:第一列“姓名”,第二列“手机号码”,第三列“备注”,这样才能正常导入号码。

发送部分,类 MsgList负责存储要发送的短信及目的号码,对多存储255个号码(同一条短信)

  //类 MsgList 每次发送创建对象,开启另一线程 根据此类的数据发送短信
   class MsgList
   {
      public MsgList(string[] phonelist, string msg)
       {
           PhoneList = phonelist;
           Msg = msg;
       }
    
      //号码列表
     public string[] PhoneList;
   
      //短信内容
      public string Msg;
  }


下面是发送按钮的函数,首先判断字数及号码是否符合规范,上一条是否发送完成(群发 多个号码一条短信)如果完成,则准备发送:更改进度条和前面标签控件的相应属性,以给用户指示发送的状态。最后创建另外线程发送短信。

   private void button9_Click(object sender, EventArgs e)
   {
       if (textBox1.Text.Length > 70)
       {
           MessageBox.Show("短信字数太长");
           return;
       }
       else if (textBox1.Text.Length == 0)
       {
          MessageBox.Show("不允许发送空短信");
      }
      else if (listView2.Items.Count == 0||listView2.Items.Count>255)
      {
          MessageBox.Show("没有号码或是号码超过255个");
      }
      else if (msgList != null)
      {
          MessageBox.Show("上一条尚未发送完成!无法发送");
      }
      else
      {
          //发送短信时 对应有控件显示
          progressBar1.Visible = true;
          progressBar1.Minimum = 0;
          progressBar1.Maximum = listView2.Items.Count;
          progressBar1.Value = 0;
   
          label5.Visible = true;
         label5.Text = "正在发送";
          label5.ForeColor = Color.Black;
   
          string[] s = new string[255];                     //最多255条号码
          for (int i = 0; i < listView2.Items.Count; i++)
          {
              s[i] = listView2.Items[i].Text;
          }
          msgList = new MsgList(s, textBox1.Text);         //赋值
          SendOneMsg = new SendEventHandler(OnSendOneMsg);  //实例化SendOneMsg,以每次发送完一条短信回调OnSendOneMsg函数
   
          Thread threadSendMsg = new Thread(SendMsg);
         threadSendMsg.Start();                          //创建并运行新线程
      }
  }


新建线程对应的函数:

   private void SendMsg()
   {
      foreach (string s in msgList.PhoneList)
      {
          if (s != null && s.Length != 0)
           {
              try
               {
                  Program.gm.SendMsg(s, msgList.Msg);
                  GSMEventArgs ge = new GSMEventArgs();
                 ge.Message = s;
                 //SendOneMsg(this, ge);
                  Invoke(SendOneMsg,this,ge);
              }
              catch
              {
                  GSMEventArgs ge = new GSMEventArgs();
                  ge.Message = "发送失败" + s;
                  Invoke(SendOneMsg, this, ge);
                  return;
              }
          }
          else
          {
              GSMEventArgs ge = new GSMEventArgs();
              ge.Message = "发送完成";
              Invoke(SendOneMsg, this, ge);
          }
      }


发送一条或是失败、完成均调用SendOneMsg指向的函数OnSendOneMsg

OnSendOneMsg函数:

  void OnSendOneMsg(object sender, GSMEventArgs ge)
   {
       if (ge.Message.Substring(0, 4) == "发送失败")
       {
           label5.Text = "发送失败";
           label5.ForeColor = Color.Red;
       }
       else if (ge.Message.Substring(0, 4) == "发送完成")
       {
          //发送完成,控件隐藏
          label5.Visible = false;
          progressBar1.Visible = false;
          label5.Text = "发送完成";
          label5.ForeColor = Color.Green;
   
          msgList = null;
      }
      else
      {
          progressBar1.Value += 1;
      }
  }


判断发送一条,或者失败,或者完成而改变控件属性,通知用户短信发送的当前状态。

  //委托 完成窗体的变化 事件
  delegate void SendEventHandler(object sender, GSMEventArgs ge);
   
  //异步调用 每发送一条调用一次
  SendEventHandler SendOneMsg = null;


这是对应委托和回调的声明,两个参数object sender, GSMEventArgs ge传递相关信息。

至此发送窗体部分完成。

  • 读取短信: 读取信息窗体Form5
    读取信收到短信: 收到短信,在设备OnRecieved事件中调用ReadNewMsg方法读取信息,回调主线程函数完成相应控件的更改
    回调声明和实例化:

//委托 收到短信时回调
  delegate void RecievedMsgHandler(string s);
  RecievedMsgHandler RecievedMsg = null;


在Form5_Load中完成实例化和事件OnRecieved的绑定。

  private void Form5_Load(object sender, EventArgs e)
  {
   
      //添加事件,收到短信
      Program.gm.OnRecieved += new GSMMODEM.GSMModem.OnRecievedHandler(gm_OnRecieved);
      //实例化回调函数
      RecievedMsg = new RecievedMsgHandler(UpdateCtrols);
   
  }


在事件OnRecieved中完成已读短信的保存和回调相应函数:

   void gm_OnRecieved(object sender, EventArgs e)
   {
       string s = Program.gm.ReadNewMsg();
    
       //把收到短信内容写入文件
      using (StreamWriter sw = File.AppendText("已收短信.txt"))
       {
           sw.WriteLine(s);
          sw.Close();
      }
   
     //回调
      Invoke(RecievedMsg, s);
  }


RecievedMsg指向的方法是UpdateCtrols,其完成listview控件属性更改,在界面显示短信内容。

private void UpdateCtrols(string s)
  {
      listView3.Items.Add(s.Split(',')[1]);
      listView3.Items
[listView3.Items.Count - 1].SubItems.Add(s.Split(',')[3]);
      listView3.Items[listView3.Items.Count - 1].SubItems.Add(s.Split(',')[0]);
      listView3.Items[listView3.Items.Count - 1].SubItems.Add(s.Split(',')[2]);
  }


控件listview3添加入手机号码列,短信内容列 和不显示的短信中心及时间字符串(用于详细信息显示)。这样新短息就能正常读取,保存并显示在listview中。

读取设备中已收到,但未读短信:设备打开时可能已有未读信息,这里解决这一问题,设备打开时从设备管理窗体引发一个事件,通知本窗体设备被打开,相关代码如下:

      //委托 事件 通知其他窗体,设备打开
  public delegate void GSMOpenHandler(object sender, GSMEventArgs ge);
   
  public event GSMOpenHandler GSMOpen = null;


委托和事件 用来通知其他窗体设备打开。事件引发代码:

  //新线程 事件引发
  Thread thread = new Thread(GSMOpened);
  thread.Start();


这两句代码添加在设备打开的下方,设备打开后即开启另一个线程(事件要读取未读信息,可能要较长时间),引发事件。私有方法GSMOpened代码

  //新线程函数 事件引发执行
  private void GSMOpened()
  {
      if (GSMOpen != null)
      {
         GSMOpen(this, new GSMEventArgs("设备成功打开"));
      }
  }


引发事件,通知其他窗体。

本窗体相关内容:响应事件,在其中读取并保存未读信息,回调相关函数显示未读信息

  void f6_GSMOpen(object sender, GSMEventArgs ge)
  {
       string[] strs = null;
      try
       {
           strs = Program.gm.GetUnreadMsg();
          if (strs == null) return;
       }
      catch(Exception ex)
      {
          MessageBox.Show(ex.Message);
      }
   
      foreach (string s in strs)
      {
          if (s == null || s.Length == 0)                 //未读信息已读完
          {
              return;
          }
   
          //把收到短信内容写入文件
          using (StreamWriter sw = File.AppendText("已收短信.txt"))
         {
              sw.WriteLine(s);
              sw.Close();
          }
   
          Invoke(HaveUnreadMsg,s);
      }
  }


事件代码:HaveUnreadMsg指向的是UpdateCtrols方法,让listview做出相关显示。

详细信息、回复:详细信息和回复功能相同,显示详细信息窗体(其中包含回复所需控件)

   //详细信息
   private void button19_Click(object sender, EventArgs e)
   {
       if (listView3.SelectedItems.Count == 0)
       {
           MessageBox.Show("没有选择项");
           return;
       }
    
      Form8 f8 = new Form8(listView3.SelectedItems[0].SubItems[2].Text,
          listView3.SelectedItems[0].SubItems[3].Text,
          listView3.SelectedItems[0].SubItems[0].Text,
          listView3.SelectedItems[0].SubItems[1].Text);
      f8.TopLevel = false;
      f8.FormBorderStyle = FormBorderStyle.None;
      f8.Parent = this.Parent;
      f8.Show();
   
      f8.BringToFront();
  }


Form8即使是详细信息窗体 其通过构造函数完成参数的传递
Form8构造函数

   /// <summary>
   /// 构造函数 传递参数
   /// </summary>
   /// <param name="msgCenter">短信中心</param>
   /// <param name="time">时间字符串</param>
   /// <param name="phone">手机号码</param>
  /// <param name="msg">短信内容</param>
   public Form8(string msgCenter,string time,string phone,string msg)
   {
      InitializeComponent();
   
      //
      textBox1.Text = msgCenter;
      textBox2.Text = time.Substring(0, 4) + "-" + time.Substring(4, 2) + "-" + time.Substring(6, 2) + " " +
          time.Substring(8, 2) + ":" + time.Substring(10, 2) + ":" + time.Substring(12);
      textBox3.Text = phone;
      textBox4.Text = msg;
  }


Form8中的回复按钮

   private void button1_Click(object sender, EventArgs e)
   {
       if (textBox5.Text == null || textBox5.Text.Length == 0)
       {
           MessageBox.Show("不允许发送空短信");
       }
       else if (textBox5.Text.Length > 70)
       {
           MessageBox.Show("短信字数过长");
      }
      else
      {
          label6.Visible = true;
          try
          {
              Program.gm.SendMsg(textBox3.Text, textBox5.Text);
          }
          catch
          {
              MessageBox.Show("发送失败");
          }
          label6.Visible = false;
   
         this.Close();
      }
  }


仅完成单条信息发送,而且还会阻塞主线程,这个可以做进一步改进。
读取信息部分完成,这一部分完成的质量最不好,希望大家不要介意,这里只为大家提供了一个使用示例。

  • 帮助:帮助部分仅有一个窗体,未加入其他内容,在这里就不做讲解了。
    短信猫软件的实现(C#)系列文章到此全部结束了,类库如有错误或是严重Bug还会更新,更新时会更新相应文件并在文章最后予以说明,或是添加其他文章对其说明,关于程序的bug或是不足之处,欢迎拍砖。

    程序中肯定有不少错误或是Bug,欢迎大家提出宝贵意见,谢谢大家支持了啊。


附件:

短信猫软件项目工程.rar


短信设备二次开发 2022-04-05 0 0

admin

  • 注册时间 : 2022-03-30 04:08:47
  • 邮箱地址 : admin@tenghengkeji.com
  • 此页面用于展示用户的基本上资料信息

回帖 ( 0 )