|
短信收发设备软件终于完成,虽然不很完善,但已可以完成所需的大多功能。在软件完成期间有很多的支持,在这里感谢大家的支持,谢谢大家。运行主界面:
界面实现: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的引用并:
设备类完成短信的发送、接收、和读取;要允许多个窗体访问;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("无法打开!\n请关闭其他打开此文件的程序后再试");
- }
- 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);
- 14: }
复制代码
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
(360.49 KB, 下载次数: 2819)
|
|