您的位置:首页 > 编程语言 > C#

短信猫软件的实现(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();

}

}


仅完成单条信息发送,而且还会阻塞主线程,这个可以做进一步改进。

读取信息部分完成,这一部分完成的质量最不好,希望大家不要介意,这里只为大家提供了一个使用示例。



帮助:帮助部分仅有一个窗体,未加入其他内容,在这里就不做讲解了。


短信猫软件的实现(C#)系列文章到此全部结束了,类库如有错误或是严重Bug还会更新,更新时会更新相应文件并在文章最后予以说明,或是添加其他文章对其说明,关于程序的bug或是不足之处,欢迎拍砖。


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

附件:工程项目文件
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: