短信猫软件的实现(C#)<十一>软件实现(完结篇)
2011-09-17 11:47
387 查看
短信猫软件终于完成,虽然不很完善,但已可以完成所需的大多功能。在软件完成期间有很多的支持,在这里感谢大家的支持,谢谢大家。
运行主界面:
界面实现:Form1,主界面上方pictureBox控件,下方用splitContainer控件分为两个部分,左边嵌套菜单窗体,右边嵌套对应每个菜单项要显示的窗体;Form2,菜单窗体;其他Form,对应菜单项窗体。
Form1:设置MaxmizeBox属性为False,使最大化按钮无效;设置AutoSizeMode属性为GrowAndShrink,不能手动调整窗体的大小;设置StartPosition属性为CenterScreen。
构造函数完成窗体的嵌套
publicForm1()
{
InitializeComponent();
//菜单窗体
f2=newForm2();
f2.TopLevel=false;
f2.FormBorderStyle=FormBorderStyle.None;
f2.Parent=this.splitContainer1.Panel1;
f2.Show();
f2.F2Event+=newForm2.F2EventHandler(f2_F2Event);//绑定Form2事件
//首页窗体
f3=newForm3();
f3.TopLevel=false;
f3.FormBorderStyle=FormBorderStyle.None;
f3.Parent=this.splitContainer1.Panel2;
f3.Show();
f3.BringToFront();
//发送短信窗体
f4=newForm4();
f4.TopLevel=false;
f4.FormBorderStyle=FormBorderStyle.None;
f4.Parent=this.splitContainer1.Panel2;
f4.Show();
//读取短信窗体
f5=newForm5();
f5.TopLevel=false;
f5.FormBorderStyle=FormBorderStyle.None;
f5.Parent=this.splitContainer1.Panel2;
f5.Show();
//设备管理窗体
f6=newForm6();
f6.TopLevel=false;
f6.FormBorderStyle=FormBorderStyle.None;
f6.Parent=this.splitContainer1.Panel2;
f6.Show();
//帮助窗体
f7=newForm7();
f7.TopLevel=false;
f7.FormBorderStyle=FormBorderStyle.None;
f7.Parent=this.splitContainer1.Panel2;
f7.Show();
}
Form2:菜单窗体,仅有6个菜单按钮,单击时引发相应事件(退出按钮除外,退出按钮单击后只运行Application.Exit();即退出程序)
privatevoidbutton_Click(objectsender,EventArgse)
{
GSMEventArgsge=newGSMEventArgs();
ge.Message=((Button)sender).Text;
if(F2Event!=null)
{
F2Event(this,ge);
}
}
ge传递按钮信息到主窗体,由主窗体处理相关信息
voidf2_F2Event(objectsender,GSMEventArgse)
{
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对应代码
//委托
publicdelegatevoidF2EventHandler(objectsender,GSMEventArgse);
//事件
publiceventF2EventHandlerF2Event;
类GSMEventArgs代码(位于Form1代码的下面)
publicclassGSMEventArgs:EventArgs
{
publicstringMessage;
}
类GSMEventArgs用于在不同窗体的事件传递有关信息
然后每个窗体上放上对应控件,界面设计完成!
首页和退出功能上面已经完成,下面不再讲解
设备类实例化:使用设备类前要添加对GSMMODEM.Dll的引用并:
usingGSMMODEM;
设备类完成短信的发送、接收、和读取;要允许多个窗体访问;C#中没有全局变量的概念,所有变量都必须在类中定义;所以本程序采用了一个替代方案
在类Program(位于文件Program.cs中)中定义并实例化静态设备对象,代码如下
staticclassProgram
{
publicstaticGSMModemgm=newGSMModem();//设备类,全局变量,方便不同窗体使用添加代码,定义并实例化设备类
///<summary>
///应用程序的主入口点。
///</summary>
[STAThread]
staticvoidMain()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(newForm1());
}
}
这样,在所有窗体中均可访问这个设备对象。访问方式:Program.gm
设备管理:设备管理目前只支持单个猫的控制,需要可以自己添加。
设备管理完成设备端口、波特率设置及设备的打开与关闭。Form6为设备管理串口,可以完成设备的设置与管理,设置波特率和串口号,及是否自动打开设备
构造函数添加控件初始化代码:
//初始化控件comboBox1
foreach(stringsinSerialPort.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;用于程序设置;
privatevoidForm6_Load(objectsender,EventArgse)
{
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则打开设备
privatevoidbutton1_Click(objectsender,EventArgse)
{
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选项改变时,写入设置并保存设置
privatevoidcheckBox1_CheckedChanged(objectsender,EventArgse)
{
Properties.Settings.Default.AutoOpen=checkBox1.Checked;//更改设置文件
Properties.Settings.Default.Save();//保存设置文件
}
三个事件的代码一致,其他两个仅多出了设备类属性的改变,这里就不贴出来了,详见工程项目文件的源代码
发送短信:窗体Form4完成短信的发送。
发送短信目标号码部分:用listview控件实现,添加按钮完成单个号码的添加
privatevoidbutton6_Click(objectsender,EventArgse)
{
Form9adNum=newForm9();
adNum.Owner=this;
//事件传值
adNum.AddNumEvent+=newForm9.AddNumEventHandler(adNum_AddNumEvent);
adNum.ShowDialog();
}
Form9是添加号码窗体通过事件AddNumEvent完成号码等信息的传递
voidadNum_AddNumEvent(objectsender,GSMEventArgse)
{
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中对应代码:
privatevoidbutton1_Click(objectsender,EventArgse)
{
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])$"))
{
//引发事件
if(AddNumEvent!=null)
{
GSMEventArgsge=newGSMEventArgs();
ge.Message=textBox1.Text+","+textBox2.Text+","+textBox3.Text;
AddNumEvent(this,ge);
}
this.Close();
}
else
{
MessageBox.Show("手机号码不正确");
}
}
事件的定义:
//委托
publicdelegatevoidAddNumEventHandler(objectsender,GSMEventArgse);
//事件
publiceventAddNumEventHandlerAddNumEvent;
这样即完成了listview的号码添加。
删除号码:
privatevoidbutton7_Click(objectsender,EventArgse)
{
ListView.SelectedIndexCollectionnumToRemove=listView2.SelectedIndices;
try
{
intj=0;
foreach(intiinnumToRemove)
{
listView2.Items.RemoveAt(i-j);
j=j+1;
}
}
catch(Exceptionex)
{
MessageBox.Show(ex.Message);
}
}
这是从listview控件中删除对应条目,完成手机号码删除。
另外还有号码导入功能,完成多个号码的添加,目前只能从excel中导入;而且需要满足格式要求。代码如下:
privatevoidbutton8_Click(objectsender,EventArgse)
{
//xPath指示excel文件路径
stringxPath=null;
OpenFileDialogopenFileDialog1=newOpenFileDialog();
openFileDialog1.Filter="ExcelFiles(*.xls)|*.xls";
openFileDialog1.Title="导入手机号码";
listView2.SelectedItems.Clear();
//ShowtheDialog.
//IftheuserclickedOKinthedialogand
//a.CURfilewasselected,openit.
if(openFileDialog1.ShowDialog()==DialogResult.OK)
{
xPath=openFileDialog1.FileName;
stringstrCon;
strCon="Provider=Microsoft.Jet.OLEDB.4.0;DataSource="+xPath+";ExtendedProperties='Excel8.0;HDR=False;IMEX=1'";
OleDbConnectionOleConn=newOleDbConnection(strCon);
stringsql="SELECT*FROM[Sheet1$]";
DataSetds=newDataSet();
try
{
OleConn.Open();
OleDbDataAdapterda=newOleDbDataAdapter(sql,strCon);
da.Fill(ds,"sheet1");
OleConn.Close();
}
catch
{
MessageBox.Show("无法打开!\n请关闭其他打开此文件的程序后再试");
}
DataTabledt=ds.Tables[0];
try
{
foreach(DataRowrowindt.Rows)
{
stringstr=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])$"))
{
ListViewItemlvi=newListViewItem(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每次发送创建对象,开启另一线程根据此类的数据发送短信
classMsgList
{
publicMsgList(string[]phonelist,stringmsg)
{
PhoneList=phonelist;
Msg=msg;
}
//号码列表
publicstring[]PhoneList;
//短信内容
publicstringMsg;
}
下面是发送按钮的函数,首先判断字数及号码是否符合规范,上一条是否发送完成(群发多个号码一条短信)如果完成,则准备发送:更改进度条和前面标签控件的相应属性,以给用户指示发送的状态。最后创建另外线程发送短信。
privatevoidbutton9_Click(objectsender,EventArgse)
{
if(textBox1.Text.Length>70)
{
MessageBox.Show("短信字数太长");
return;
}
elseif(textBox1.Text.Length==0)
{
MessageBox.Show("不允许发送空短信");
}
elseif(listView2.Items.Count==0||listView2.Items.Count>255)
{
MessageBox.Show("没有号码或是号码超过255个");
}
elseif(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=newstring[255];//最多255条号码
for(inti=0;i<listView2.Items.Count;i++)
{
s[i]=listView2.Items[i].Text;
}
msgList=newMsgList(s,textBox1.Text);//赋值
SendOneMsg=newSendEventHandler(OnSendOneMsg);//实例化SendOneMsg,以每次发送完一条短信回调OnSendOneMsg函数
ThreadthreadSendMsg=newThread(SendMsg);
threadSendMsg.Start();//创建并运行新线程
}
}
新建线程对应的函数:
privatevoidSendMsg()
{
foreach(stringsinmsgList.PhoneList)
{
if(s!=null&&s.Length!=0)
{
try
{
Program.gm.SendMsg(s,msgList.Msg);
GSMEventArgsge=newGSMEventArgs();
ge.Message=s;
//SendOneMsg(this,ge);
Invoke(SendOneMsg,this,ge);
}
catch
{
GSMEventArgsge=newGSMEventArgs();
ge.Message="发送失败"+s;
Invoke(SendOneMsg,this,ge);
return;
}
}
else
{
GSMEventArgsge=newGSMEventArgs();
ge.Message="发送完成";
Invoke(SendOneMsg,this,ge);
}
}
}
发送一条或是失败、完成均调用SendOneMsg指向的函数OnSendOneMsg
OnSendOneMsg函数:
voidOnSendOneMsg(objectsender,GSMEventArgsge)
{
if(ge.Message.Substring(0,4)=="发送失败")
{
label5.Text="发送失败";
label5.ForeColor=Color.Red;
}
elseif(ge.Message.Substring(0,4)=="发送完成")
{
//发送完成,控件隐藏
label5.Visible=false;
progressBar1.Visible=false;
label5.Text="发送完成";
label5.ForeColor=Color.Green;
msgList=null;
}
else
{
progressBar1.Value+=1;
}
}
判断发送一条,或者失败,或者完成而改变控件属性,通知用户短信发送的当前状态。
//委托完成窗体的变化事件
delegatevoidSendEventHandler(objectsender,GSMEventArgsge);
//异步调用每发送一条调用一次
SendEventHandlerSendOneMsg=null;
这是对应委托和回调的声明,两个参数objectsender,
GSMEventArgsge传递相关信息。
至此发送窗体部分完成。
读取短信:读取信息窗体Form5
读取信收到短信:收到短信,在设备OnRecieved事件中调用ReadNewMsg方法读取信息,回调主线程函数完成相应控件的更改
回调声明和实例化:
//委托收到短信时回调
delegatevoidRecievedMsgHandler(strings);
RecievedMsgHandlerRecievedMsg=null;
在Form5_Load中完成实例化和事件OnRecieved的绑定。
privatevoidForm5_Load(objectsender,EventArgse)
{
//添加事件,收到短信
Program.gm.OnRecieved+=newGSMMODEM.GSMModem.OnRecievedHandler(gm_OnRecieved);
//实例化回调函数
RecievedMsg=newRecievedMsgHandler(UpdateCtrols);
}
在事件OnRecieved中完成已读短信的保存和回调相应函数:
voidgm_OnRecieved(objectsender,EventArgse)
{
strings=Program.gm.ReadNewMsg();
//把收到短信内容写入文件
using(StreamWritersw=File.AppendText("已收短信.txt"))
{
sw.WriteLine(s);
sw.Close();
}
//回调
Invoke(RecievedMsg,s);
}
RecievedMsg指向的方法是UpdateCtrols,其完成listview控件属性更改,在界面显示短信内容。
privatevoidUpdateCtrols(strings)
{
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中。
读取设备中已收到,但未读短信:设备打开时可能已有未读信息,这里解决这一问题,设备打开时从设备管理窗体引发一个事件,通知本窗体设备被打开,相关代码如下:
//委托事件通知其他窗体,设备打开
publicdelegatevoidGSMOpenHandler(objectsender,GSMEventArgsge);
publiceventGSMOpenHandlerGSMOpen=null;
委托和事件用来通知其他窗体设备打开。事件引发代码:
//新线程事件引发
Threadthread=newThread(GSMOpened);
thread.Start();
这两句代码添加在设备打开的下方,设备打开后即开启另一个线程(事件要读取未读信息,可能要较长时间),引发事件。私有方法GSMOpened代码
//新线程函数事件引发执行
privatevoidGSMOpened()
{
if(GSMOpen!=null)
{
GSMOpen(this,newGSMEventArgs("设备成功打开"));
}
}
引发事件,通知其他窗体。
本窗体相关内容:响应事件,在其中读取并保存未读信息,回调相关函数显示未读信息
voidf6_GSMOpen(objectsender,GSMEventArgsge)
{
string[]strs=null;
try
{
strs=Program.gm.GetUnreadMsg();
if(strs==null)return;
}
catch(Exceptionex)
{
MessageBox.Show(ex.Message);
}
foreach(stringsinstrs)
{
if(s==null||s.Length==0)//未读信息已读完
{
return;
}
//把收到短信内容写入文件
using(StreamWritersw=File.AppendText("已收短信.txt"))
{
sw.WriteLine(s);
sw.Close();
}
Invoke(HaveUnreadMsg,s);
}
}
事件代码:HaveUnreadMsg指向的是UpdateCtrols方法,让listview做出相关显示。
详细信息、回复:详细信息和回复功能相同,显示详细信息窗体(其中包含回复所需控件)
//详细信息
privatevoidbutton19_Click(objectsender,EventArgse)
{
if(listView3.SelectedItems.Count==0)
{
MessageBox.Show("没有选择项");
return;
}
Form8f8=newForm8(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>
///<paramname="msgCenter">短信中心</param>
///<paramname="time">时间字符串</param>
///<paramname="phone">手机号码</param>
///<paramname="msg">短信内容</param>
publicForm8(stringmsgCenter,stringtime,stringphone,stringmsg)
{
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中的回复按钮
privatevoidbutton1_Click(objectsender,EventArgse)
{
if(textBox5.Text==null||textBox5.Text.Length==0)
{
MessageBox.Show("不允许发送空短信");
}
elseif(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();
}
}
仅完成单条信息发送,而且还会阻塞主线程,这个可以做进一步改进。
读取信息部分完成,这一部分完成的质量最不好,希望大家不要介意,这里只为大家提供了一个使用示例。