没发过短信的年轻人肯定是属于那种受保护的稀有动物,通讯发达的今天短信已经成为人们交流的重要手段,其中也蕴含着巨大的市场和经济利益,掌握短信技术的人才也受到各大公司的追捧是目前职场上耀眼的明星。本文介绍了短信的原理和实现方法,重点说明了短信的编码方式、AT指令以及用C#实现串口通讯的方法。
前言
目前,主有三种发送短信的方式:
1、 网关方式:就是向当地的电信部门申请,不需要额外的设备,适用于大型的通信公司,像华为、傲天、中兴、亚信等。
2、 终端方式:就是借助像GSM MODEM之类的设置(支持AT指令的手机也行),通过数据线连接电脑,来发送短信,用这种方法比较适用于小型及个人。要实现这种方式必须理解串口通信、AT指令、短信编码、解码,这也是本文讨论的重点。
3、 利用一些网站来实现,方式简单,不过对网站依赖性太高,对网络的要求也比较高,非常不适于进行项目开发
阅读导航
| |
终端短信连接示意图 | |
原理篇 | 短信编码 |
AT指令 | |
串口通讯 | |
实践篇 | |
常见问题 | |
下载 | 本文源代码 |
编码测试文件 |
<script type="text/javascript">zmbbs=1;</script>
<script type="text/javascript">zmbbs=1;</script> 原理篇
短信编码
在收发短信方面,按时间产生先后,共产生了三种模式:Block Mode、基于AT指令的Text Mode、基于AT指令的PDU Modem, Text Mode比较简单,多款诺基亚手机均支持该模式。西门子的手机大多只支持PDU模式,PDU模式是发送或接收手机SMS信息的一种方法,短信息正文经过十六进制编码后被传送。目前,PDU已取代Block Mode,因我们主要探讨PDU模式的发送。以西门子3508手机为例。
SMS是由Etsi所制定的一个规范(GSM 03.40 和 GSM 03.38)。当使用7-bits编码时,它可以发送最多160个字符;但用8-bit编码,最多可以发送140个字符,通常无法直接通过手机显示;还有用16-bit编码时,最多70个字符,被用来显示Unicode(UCS2)文本信息,可以被大多数的手机所显示。我们今天讨论的是UCS2编码,也就是说,最多只能发送70个字符,不管英文还是中文。
现例如我们现在要发送如下信息,向我的手机13715342642发送"你好,Hello!"。在没有发送之前,你要清楚,手机SIM卡所在地的短信中心号,并不是你现在所在地方的短信中心号,像我在深圳,深圳的短信中心号是:8613800755000,即使我现在到外地,短信中心号仍是深圳。从上面我们得到了下面的信息:
接收的手机号:13715342642
短信中心号:8613800755000
短信内容:你好,Hello!
在实际使用中,上面这些信息并不为手机所执行,要进行编码手机才会执行,先不管,看看编码后的信息:
0891683108705500F011000D91683117352446F2000800124F60597DFF0C00480065006C006C006F0021
看不懂吧,我来解释一下:
08 - 指的是短信中心号的长度,也就是指(91)+( 683108705500F0)的长度
91 - 指的是短信息中心号码类型。91是TON/NPI遵守International/E.164标准,指在号码前需加'+'号;此外还有其它数值,但91最常用。
683108705500F0 - 短信息中心号码。由于位置上略有处理,实际号码应为:8613800731500(字母F是指长度减1)。这需要根据不同的地域作相应的修改。前面的(08)+(91)+( 683108705500F0)实际上就构成了整个短信的一部份,通称短消息中心地址(Address of the SMSC)。
11 - 文件头字节
00 - 信息类型(TP-Message-Reference)
0D - 被叫号码长度
91 - 被叫号码类型
其实在实际处理中,我们通常把11000D91写死在程序中,因为在国内,这些数据都是不会改变的。
683117352446F2 -被叫号码,经过了位移处理,实际号码为"8613715342642"。上面的(00)+(0D)+(91)+( 683117352446F2),构成了整个短信的第二部份目的地址(TP-Destination-Address)。
00 - 协议标识TP-PID,这里一般为00
08 - 数据编码方案TP-DCS(TP-Data-Coding-Scheme),采用前面说的USC2(16bit)数据编码
00 - 有效期TP-VP(TP-Valid-Period)
12-长度TP-UDL(TP-User-Data-Length),也就是4F60597DFF0C00480065006C006C的长度 36 / 2 = 18 的十六进 12
4F60597DFF0C00480065006C006C 006F0021- 这里就是短信内容了,实际内容为:"你好,Hello!"程序实现,请参考本文章所带源程序的PDUdecoding.cs。
<script type="text/javascript">zmbbs=1;</script> AT指令
说到AT指令可多了,有厚厚的一本书,不属于我们今天讨论的范围,在这里我仅讨论在发送短信中必须要用的几个AT指令。
与SMS有关的GSM AT指令(from GSM07.05)如表1所示:
AT 指令 | 功 能 |
AT+CMGC | Send an SMS command(发出一条短消息命令) |
AT+CMGD | Delete SMS message(删除SIM卡内存的短消息) |
AT+CMGF | Select SMS message formate(选择短消息信息格式:0-PDU;1-文本) |
AT+CMGL | List SMS message from preferred store(列出SIM卡中的短消息PDU/text: 0/"REC UNREAD"-未读,1/"REC READ"-已读,2/"STO UNSENT"-待发,3/"STO SENT"-已发,4/"ALL"-全部的) |
AT+CMGR | Read SMS message(读短消息) |
AT+CMGS | Send SMS message(发送短消息) |
AT+CMGW | Write SMS message to memory(向SIM内存中写入待发的短消息) |
AT+CMSS | Send SMS message from storage(从SIN|M内存中发送短消息) |
AT+CNMI | New SMS message indications(显示新收到的短消息) |
AT+CPMS | Preferred SMS message storage(选择短消息内存) |
AT+CSCA | SMS service center address(短消息中心地址) |
AT+CSCB | Select cell broadcast messages(选择蜂窝广播消息) |
AT+CSMP | Set SMS text mode parameters(设置短消息文本模式参数) |
AT+CSMS | Select Message Service(选择短消息服务) |
我现在以实例来说明这些指令的使用方法:
先用手机数据线将手机连接到电脑串口,并将串口的波特率设置为19200,可以开始了。
1、首先测试你的连接及手机是否支持AT指令,请在你的串口调试程序中输入:
AT<回车>
屏幕上返回"OK"表明计算机与手机连接正常,那样我们就可以进行其它的AT指令测试了
2、设置短信发送格式
AT+CMGF=1<回车>
屏幕上返回"OK"表明现在短信的发送方式为PDU方式,如果是设置为TEXT方式,则,AT+CMGF=0<回车>
3、 发送短信
发送内容及手要号仍旧同上面在编码中的一样,编码后,得到要发送的数据如下
0891683108705505F011000D91683117352446F2000800124F60597D002C00480065006C006C006F0021
我们用如下指令来发送
AT+CMGS=33<回车>
如果返回">",就把上面编码数据输入,并以CTRL+Z结尾,稍等一下,你就可以看到返回OK啦。
说明一下,为什么AT+CMGS=33呢,是这样得来的:
11000D91683117352446F2000800124F60597D002C00480065006C006C006F0021
这一段字符串的长度除以2得到的结果,上面的字符串,短信中心号加上短信内容得到的,怎么得到的,请回顾一下解码部份
在我们前面的讨论中,一条完整的短信发送,只要执行三条AT指令,AT、AT+CMGS=?、AT+CMGS=?就可以了。由于篇幅,我只能在这里提到这么多,大家要是想了解更多,可以向各手机厂商索取AT指令白皮书,里面很详细的。
上面讲到的,只能为我们实际中作准备,我们还必须要一个发送途径,根据我们的需要,我们选择投资最少,实现比较方便的串口通信。注意,串口通过数据线跟手机相连,用AT指令来实现发送短信,在我们选择数据线时,建议购买原厂所配,非原厂所配,在使用过程中,经常出现一些莫明其妙的问题,比如,手机屏幕黑了,手机老是提示电池电量不足之类的。
<script type="text/javascript">zmbbs=1;</script> 串口通信
在C#中要实现串口通信,很多人都不知所措,在论坛上经常可以看到"怎么用MSCOMM实现串口通信"、"怎样能过串口与设备相连"诸如此类的问题。其实国外的网友早就把这些列入FAQ中了。
通常,在C#中实现串口通信,我们有四种方法:
第一:通过MSCOMM控件这是最简单的,最方便的方法。可功能上很难做到控制自如,同时这个控件并不是系统本身所带,所以还得注册,不在本文讨论范围。可以访问http://www.devhood.com/tutorials/tutorial_details.aspx?tutorial_id=320 ,一个国外网友的写的教程,作者很热心,我曾有发邮件给他,很快就回复了。
第二:微软在.NET新推出了一个串口控件,基于.NET的P/Invoke调用方法实现,详细的大家可以访问微软网站http://msdn.microsoft.com/msdnmag/issues/02/10/NETSerialComm/default.aspx,方便得到更多资料。
第三:就是用第三方控件啦,可一般都要付费的,不太合实际,不作考虑
第四:自己用API写串口通信,这样难度高点,但对于我们来说,可以方便实现自己想要的各种功能
在本文,我们采用第四种方法来实现串口通信,不过不是自己写,用一个国外网友现成的已经封装好的类库,不过功能简单点,相对我们来说已经够用了。
在整个终端短信的操作过程中,与串口的通信,只用到了四个功能,打开、写、读、关闭串口。下面是类库对这四个功能的定义:
打开串口:
函数原型:public void Open()
说明:打开事先设置好的端口
示例:
using JustinIO; static JustinIO.CommPort ss_port = new JustinIO.CommPort(); ss_port.PortNum = COM1; //端口号 ss_port.BaudRate = 19200; //串口通信波特率 ss_port.ByteSize = 8; //数据位 ss_port.Parity = 0; //奇偶校验 ss_port.StopBits = 1;//停止位 ss_port.ReadTimeout = 1000; //读超时 try { if (ss_port.Opened) { ss_port.Close(); ss_port.Open(); //打开串口 } else { ss_port.Open();//打开串口 } return true; } catch(Exception e) { MessageBox.Show("错误:" + e.Message); return false; } |
写串口:
函数原型:public void Write(byte[] WriteBytes)
WriteBytes 就是你的写入的字节,注意,字符串要转换成字节数组才能进行通信
示例:
ss_port.Write(Encoding.ASCII.GetBytes("AT+CGMI/r")); //获取手机品牌
读串口:
函数原型:public byte[] Read(int NumBytes)
NumBytes 读入缓存数,注意读取来的是字节数组,要实际应用中要进行字符转换
示例:
string response = Encoding.ASCII.GetString(ss_port.Read(128)); //读取128个字节缓存
关闭串口:
函数原型:ss_port.Close()
示例:
ss_port.Close();
由于篇幅,以及串口通信涉及内容广泛,我在这里只讲这些。
在上面我们已经把终端短信所需的各种原始技术有所了解,是可以小试牛刀的时候了。
<script type="text/javascript">zmbbs=1;</script> 实践篇
在整个开始的时候,你要准备以下软硬件:
硬件:西门子3508或C35系列手机一个
西门子手机通信数据线一条
软件:VS.NET(C#)
短信编码类库(PDUdecoding.cs)
串口通信类库(JustinIO.cs)
当所要求的软硬件都准备好后,我们就可以正式开始了。下面以我自己的测试用例为大家详细介绍。
做什么事情都应该有计划,虽然我们的测试用例很简单,但还是画个简单的流程图:
有了流程图,还只是明白了程序怎么运行,再看看界面,会让你更心动的了。
图二、短信终端C#版界面图
再不开始,就有人骂我了。下在我讲的开发环境是在VS.NET(C#)中。COME GO,GO…
步骤一、打开VS.NET,新建项目->Visual C#项目->Windows应用程序,名称中输入你的工程名就行啦,我的是smsForCsharp
步骤二、参照上面的界面图,设计你的程序界面,下面是我程序中各控件的主要属性
控件名称 | 控件Name属性 | 说明 |
TextBox | targetNumber | 接收手机号码 |
TextBox | CenterNumber | 短信中心号 |
TextBox | smsState | 发送短信后,返回的信息。注意设置控件为多行 |
TextBox | smsContent | 短信内容,同样,注意设置为多行 |
ComboBox | ConnectPort | 连接手机的端口,例:COM1/COM2 |
ComboBox | ConnectBaudRate | 串口连接的波特率,在串口通信中很重要的 |
Button | btnSend | 发送按钮 |
Button | btnConnect | 连接按钮,主要用于程序的初始化 |
Button | btnExit | 退出按钮 |
步骤三、将PDUdecoding.cs与JustinIO.cs拷入刚刚新建工程目录,并打开解决方案资源管理器,右键添加现有项,选中两个文件就行了,这里再打开类视图,里面是不是多了两个类,JustinIO与SMS类啊,如图三,要是没有,那你再试。
图三,添加类后的类视图
步骤四、引用命名空间,用代码查看方式打开Form1.cs(这里以我电脑为准,如果你自己更改过,请以你电脑为准),在代码前面加上
using JustinIO; using SMS; using System.IO; using System.Text; |
步骤五、在smsFormCsharp类中,添加两个字段ss_port、sms,分别为JustinIO及SMS的对象,如下
步骤六、添加串口初始化代码,如下:
/// <summary> /// 初始化串口 /// </summary> public bool InitCom(string m_port, int m_baudrate) { ss_port.PortNum = m_port;//串口号 ss_port.BaudRate = m_baudrate;//波特率 ss_port.ByteSize = 8;//数据位 ss_port.Parity = 0;// ss_port.StopBits = 1;//停止位 ss_port.ReadTimeout = 1000;//读超时 try { if (ss_port.Opened) { ss_port.Close(); ss_port.Open(); } else { ss_port.Open();//打开串口 } return true; } catch(Exception e) { MessageBox.Show("错误:" + e.Message); return false; } } |
将上述代码直接拷入你的程序中,并确保添加在Main主函数的后面,按F5,调试应该没什么问题,不过上面还没有实际任何看得见的功能,仅仅是打开了串口而以。
步骤七、打开串口后,我们就应该初始化程序,取得手机的名牌,型号,以及短信中心号,双击连接按钮,并把下面代码拷入程序中:
/// <summary> /// 初始化代码,并获取手机相关信息 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnConnect_Click(object sender, System.EventArgs e) { bool opened = InitCom(ConnectPort.SelectedItem.ToString(),Convert.ToInt32(ConnectBaudRate.SelectedItem.ToString()));//打开并初始化串口 bool Connected = false; if (opened) { ss_port.Write(Encoding.ASCII.GetBytes("AT+CGMI/r")); //获取手机品牌 string response = Encoding.ASCII.GetString(ss_port.Read(128)); if (response.Length > 0) { ConnectState.Text = response.Substring(10,7); Connected = true; } else { ConnectState.Text = "与手机连接不成功"; Connected = false; } ss_port.Write(Encoding.ASCII.GetBytes("AT+CGMM/r"));//获取手机型号 response = Encoding.ASCII.GetString(ss_port.Read(128)); if(response.Length > 0) { ConnectState.Text =ConnectState.Text+ " " + response.Substring(10,5) + " 连接中......"; Connected = true; } else { ConnectState.Text = "与手机连接不成功"; Connected = false; } ss_port.Write(Encoding.ASCII.GetBytes("AT+CSCA?/r"));//获取手机短信中心号 response = Encoding.ASCII.GetString(ss_port.Read(128)); if(response.Length > 0) { CenterNumber.Text = response.Substring(20,13); Connected = true; } else { Connected = false; } if (Connected == true) { btnConnect.Enabled = false; btnSend.Enabled = true; } else { btnConnect.Enabled = true; btnSend.Enabled = false; } } } |
到这里,你可以按F5,编译调试,通过,在确保你的手机与电脑连接正常下,点击连接按钮看看,是不是像我的一样,手机型号及短信中心号者正常显示出来了。
图四、连接后程序界面
步骤八、看到上在的结果,是不是感觉到离成功发送短信很近啦,看这么长的文章,费了大家不少时间,再不亮出发短信部份,对不起大家了。
双击发送按钮,将下面代码拷入程序中。
/// <summary> /// 发送短信 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnSend_Click(object sender, System.EventArgs e) { string decodedSMS = sms.smsDecodedsms(CenterNumber.Text,targetNumber.Text,smsContent.Text); byte[] buf =Encoding.ASCII.GetBytes(String.Format("AT+CMGS={0}/r",sms.nLength)); ss_port.Write(buf); string response = Encoding.ASCII.GetString(ss_port.Read(128)); string SendState = ""; if( response.Length > 0 && response.EndsWith("> ")) { ss_port.Write(Encoding.ASCII.GetBytes(String.Format("{0}/x01a",decodedSMS))); SendState = "发送成功!"; } else { SendState = "发送失败"; } string Result = String.Format("{0},{1},{2},/n/r",targetNumber.Text,smsContent.Text,SendState); smsState.Text += Result; } |
快按F5吧!神啊,快通过吧!不用求神了,已经通过了,现在你就可以发短信了,请确保手机可以正常连接电脑。按连接,然后填入你要的发送的目标手机号,并在内容中添入你要发送的内容,发送吧!成功了!成功了是这样子的!看你的跟我的一样吗?
图五、发送成功
还有一些事 不要忘了,记得添加退出代码。双击退出,添加下面代码:
/// <summary> /// 关闭串口,退出程序 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnExit_Click(object sender, System.EventArgs e) { ss_port.Close(); Application.Exit(); } |
到这里都告一个段落了,所有的功能都完成了!不过由于这仅仅是一个演示用例,还有很多没有考虑,像串口通信中的,在实际操作不可这样操作的,应该用多线程来处理,一个专门用来读串口,一个专门用来写串口。还有程序中很多防出错代码没有添加进去,希望有心有朋友添加,并公布出来,这也是我写这篇文章希望看到的结果。请勿将本程序直接用于实际中,真诚提醒你!
终于写完了,我也放松了许多,本来很早就应该完成了,因为一些个人原因,没有及时写完,向那些曾经问过我相关问题,没有及时回复的朋友,抱歉一声,希望你们继续支持我!
调试环境:
Windows 2000 Professional、Visual Studio.NET、西门子3508手机、西门子专用数据线。
<script type="text/javascript">zmbbs=1;</script> 常见问题:
第一, 手机品牌,因为不同产商的手机,对AT指令的支持不同,所以请选择适合你手机AT指令,像NOKIA的就只能用TEXT模式的AT指令。
第二, 数据线,问题出得最多的地方也就是数据,如果接上数据线后,你的手机显示为黑屏,建议你换数据线。
第三, 手机SIM卡上的短信中心号设置,请确保在你的手机上可以发送短信。
第四, 请你先用串口调试工具调试手机与电脑的连接,这样对你整个工作都是一个保证。
PDUdecoding.cs
using System;
using System.Threading;
using System.Text;
using System.Collections;
namespace SMS
{
public class SMS_DeliverMsg
{
static uint s_Seq = 0;
static Mutex S_MutexSeq = new Mutex();
public static long Get_Seq()
{
long ret = 0;
S_MutexSeq.WaitOne();
s_Seq++;
if (s_Seq > 9999)
{
s_Seq = 0;
}
long Tick = System.DateTime.Now.Ticks;
Tick = Tick << 14;
Tick = Tick >> 14;
ret = (
Tick * 10000 + s_Seq
);
S_MutexSeq.ReleaseMutex();
return ret;
}
static public bool EncodeHead(
byte[] vBuff,
int OffIndex,
int Len,
out ArrayList IndexList,
out ArrayList statList
)
{
string headStr =
CommonIO.Read_ASC_Str_FromBuff(vBuff, OffIndex, Len);
return EncodeHead(
headStr,
out IndexList,
out statList
);
}
static public bool EncodeHead(
string headStr,
out ArrayList IndexList,
out ArrayList statList
)
{
int vInDex = 0;
IndexList = new ArrayList();
statList = new ArrayList();
while (true)
{
vInDex = headStr.IndexOf(SMS_DeliverMsg.s_HeadStr, vInDex);
if (vInDex < 0)
{
break;
}
vInDex += SMS_DeliverMsg.s_HeadStr.Length;
//获取m_Index
int tempIndex = headStr.IndexOf(",", vInDex);
if (tempIndex < vInDex)
{
break;
}
int GetIndex =
Convert.ToInt32(
headStr.Substring(vInDex, tempIndex - vInDex).Trim());
vInDex = tempIndex;
vInDex++;
//获取m_Stat
tempIndex = headStr.IndexOf(",", vInDex);
if (tempIndex < vInDex)
{
break;
}
int get_Stat =
Convert.ToInt32(headStr.Substring(vInDex, tempIndex - vInDex).Trim());
vInDex = tempIndex;
vInDex++;
IndexList.Add(GetIndex);
statList.Add(get_Stat);
}
return true;
}
public long m_Seq = 0;
public static string s_HeadStr = "+CMGL:";
public int m_Index = 0;
public int m_Stat = 0;
public string m_alpha = "";
public int m_Length;
public Deliver_PDU_SMS m_PDU;
public bool Decode(byte[] vBuff, ref int OffIndex)
{
string headStr = CommonIO.Read_ASC_Str_FromBuff(vBuff, OffIndex, 256);
//获取headStr
int vInDex = headStr.IndexOf(SMS_DeliverMsg.s_HeadStr, 0);
if (vInDex < 0)
{
//头错误
return false;
}
vInDex += SMS_DeliverMsg.s_HeadStr.Length;
//获取m_Index
int tempIndex = headStr.IndexOf(",", vInDex);
if (tempIndex < vInDex)
{
return false;
}
this.m_Index =
Convert.ToInt32(headStr.Substring(vInDex, tempIndex - vInDex).Trim());
vInDex = tempIndex;
vInDex++;
//获取m_Stat
tempIndex = headStr.IndexOf(",", vInDex);
if (tempIndex < vInDex)
{
return false;
}
this.m_Stat =
Convert.ToInt32(headStr.Substring(vInDex, tempIndex - vInDex).Trim());
vInDex = tempIndex;
vInDex++;
//获取m_alpha
tempIndex = headStr.IndexOf(",", vInDex);
if (tempIndex < vInDex)
{
return false;
}
this.m_alpha =
headStr.Substring(vInDex, tempIndex - vInDex).Trim();
vInDex++;
//获取m_Length
tempIndex = headStr.IndexOf("/r", vInDex);
if (tempIndex < vInDex)
{
return false;
}
this.m_Length =
Convert.ToInt32(headStr.Substring(vInDex, tempIndex - vInDex).Trim());
vInDex = tempIndex;
vInDex += OffIndex;
m_PDU = new Deliver_PDU_SMS();
if (vBuff[vInDex] == 13)
{
vInDex++;
}
if (vBuff[vInDex] == 10)
{
vInDex++;
}
bool ret = m_PDU.Decode(vBuff, ref vInDex);
OffIndex = vInDex;
m_Seq = Get_Seq();
return ret;
}
}
public class SCA_SMS
{
int m_Len = 0;
string m_Type = "";
string m_Addr = "";
public bool Decode(byte[] vBuff, ref int OffIndex)
{
m_Len = CommonIO.Read_2H_Int_FromBuff(vBuff, ref OffIndex);
if (m_Len == 0)
{
m_Type = "";
m_Addr = "";
}
else
{
m_Type = CommonIO.Read_ASC_Str_FromBuff(
vBuff,
ref OffIndex,
2);
m_Addr = CommonIO.Read_ASC_Str_FromBuff(
vBuff,
ref OffIndex,
(m_Len - 1) * 2
);
}
return true;
}
}
public class DAOA_SMS
{
int m_Len = 0;
public string m_Type = "";
public string m_Addr = "";
public bool Decode(byte[] vBuff, ref int OffIndex)
{
m_Len = CommonIO.Read_2H_Int_FromBuff(vBuff, ref OffIndex);
if (m_Len == 0)
{
m_Type = "";
m_Addr = "";
}
else
{
m_Type = CommonIO.Read_ASC_Str_FromBuff(
vBuff,
ref OffIndex,
2);
int ActLen = m_Len;
if (ActLen % 2 != 0)
{
ActLen++;
}
string temps = m_Addr = CommonIO.Read_ASC_Str_FromBuff(
vBuff,
ref OffIndex,
ActLen
);
m_Addr = "";
for (int i = 0; i < temps.Length / 2; i++)
{
m_Addr += temps[2 * i + 1];
m_Addr += temps[2 * i];
}
if (m_Addr[m_Addr.Length - 1] == 'F')
{
m_Addr = m_Addr.Substring(0, m_Addr.Length - 1);
}
}
return true;
}
}
public class UD_SMS
{
public int m_UDL = 0;
public string m_DCS = null;
public string m_UD;
public bool Decode(byte[] vBuff, ref int OffIndex)
{
if (m_DCS == "08")
{
string OrgStr = System.Text.Encoding.ASCII.GetString(
vBuff,
OffIndex,
m_UDL * 2);
int EndStrIndex = OrgStr.IndexOf("/r/n");
if (EndStrIndex >= 0)
{
OrgStr = OrgStr.Substring(0, EndStrIndex);
}
int validLen = m_UDL * 2;
if (validLen * 2 > OrgStr.Length)
{
validLen = OrgStr.Length / 2;
}
byte[] buffDes = new byte[validLen];
for (int i = 0; i < validLen; i++)
{
string temps
= OrgStr.Substring(i * 2, 2);
int v = int.Parse(
temps, System.Globalization.NumberStyles.HexNumber);
buffDes[i] = (byte)v;
}
string desStr = System.Text.Encoding.BigEndianUnicode.
GetString(buffDes);
m_UD = desStr;
OffIndex += m_UDL * 2;
}
if (m_DCS == "00")
{
string OrgStr = System.Text.Encoding.ASCII.GetString(
vBuff,
OffIndex,
m_UDL * 2);
int EndStrIndex = OrgStr.IndexOf("/r/n");
if (EndStrIndex >= 0)
{
OrgStr = OrgStr.Substring(0, EndStrIndex);
}
int validLen = m_UDL * 2;
if (validLen * 2 > OrgStr.Length)
{
validLen = OrgStr.Length / 2;
}
byte[] buffDes = new byte[validLen];
for (int i = 0; i < validLen; i++)
{
string temps
= OrgStr.Substring(i * 2, 2);
int v = int.Parse(
temps, System.Globalization.NumberStyles.HexNumber);
buffDes[i] = (byte)v;
}
byte[] Asc8Buff = CommonIO.Read_Asc8_Buff_FromAsc7Buff(buffDes);
string desStr = System.Text.Encoding.ASCII.GetString(Asc8Buff);
m_UD = desStr;
OffIndex += m_UDL * 2;
}
return true;
}
}
public class UD_SCTS
{
public string m_OrgStr = "";
public DateTime m_DateTime = DateTime.Now;
/// <summary>
///
/// </summary>
/// <param name="OrgStr">原始字符串</param>
/// <param name="EncodeMethord">编码格式 1为绝对 2为相对</param>
/// <returns></returns>
public bool Decode(string OrgStr, int EncodeMethord)
{
m_OrgStr = OrgStr;
if (EncodeMethord != 2)
{
return true;
}
try
{
//Year
string s = OrgStr[1].ToString();
s += OrgStr[0].ToString();
int vYear = Convert.ToInt32(s) + 2000;
//yue
s = OrgStr[3].ToString();
s += OrgStr[2].ToString();
int vMonth = Convert.ToInt32(s);
//日
s = OrgStr[5].ToString();
s += OrgStr[4].ToString();
int vDay = Convert.ToInt32(s);
//小时
s = OrgStr[7].ToString();
s += OrgStr[6].ToString();
int vHour = Convert.ToInt32(s);
//分
s = OrgStr[9].ToString();
s += OrgStr[8].ToString();
int vMinute = Convert.ToInt32(s);
//秒
s = OrgStr[11].ToString();
s += OrgStr[10].ToString();
int vSecond = Convert.ToInt32(s);
//时区
s = OrgStr[13].ToString();
s += OrgStr[12].ToString();
int AreaCode = Convert.ToInt32(s);
m_DateTime = new DateTime(
vYear,
vMonth,
vDay,
vHour,
vMinute,
vSecond,
0);
}
catch
{
return false;
}
return true;
}
}
public class Deliver_PDU_SMS
{
public SCA_SMS m_SCA = new SCA_SMS();
public string m_PDUType = "24";
public DAOA_SMS m_OA = new DAOA_SMS();
public string m_PID = "";
public string m_DCS = "";
public UD_SCTS m_UD_SCTS;
public int m_UDL = 0;
public UD_SMS m_UD = new UD_SMS();
public bool Decode(byte[] vBuff, ref int OffIndex)
{
m_SCA = new SCA_SMS();
if (m_SCA.Decode(vBuff, ref OffIndex) == false)
{
return false;
}
m_PDUType = CommonIO.Read_ASC_Str_FromBuff(vBuff, ref OffIndex, 2);
if (m_PDUType == null)
{
return false;
}
m_OA = new DAOA_SMS();
if (m_OA.Decode(vBuff, ref OffIndex) == false)
{
return false;
}
m_PID = CommonIO.Read_ASC_Str_FromBuff(vBuff, ref OffIndex, 2);
m_DCS = CommonIO.Read_ASC_Str_FromBuff(vBuff, ref OffIndex, 2);
string StrSCTS = CommonIO.Read_ASC_Str_FromBuff(vBuff, ref OffIndex, 14);
m_UD_SCTS = new UD_SCTS();
m_UD_SCTS.Decode(StrSCTS, 2);
m_UDL = CommonIO.Read_2H_Int_FromBuff(vBuff, ref OffIndex);
m_UD = new UD_SMS();
m_UD.m_DCS = m_DCS;
m_UD.m_UDL = m_UDL;
return m_UD.Decode(vBuff, ref OffIndex);
}
}
public class CommonIO
{
//读取一个整数值
public static int Read_2H_Int_FromBuff(
byte[] vBuff,
ref int Index)
{
//出错
string NumStr =
Read_ASC_Str_FromBuff(
vBuff,
ref Index,
2);
return int.Parse(NumStr, System.Globalization.NumberStyles.HexNumber);
}
//读取ASC值
public static string Read_ASC_Str_FromBuff(
byte[] vBuff,
int Index,
int Len)
{
if (vBuff == null || Index > vBuff.Length - 1)
{
return null;
}
if (vBuff.Length < Index + Len)
{
Len = vBuff.Length - Index;
}
return System.Text.Encoding.ASCII.GetString(vBuff, Index, Len);
}
//读取ASC值
public static string Read_ASC_Str_FromBuff(
byte[] vBuff,
ref int Index,
int Len)
{
string ret = Read_ASC_Str_FromBuff(
vBuff,
Index,
Len);
if (ret == null)
{
return null;
}
else
{
Index += ret.Length;
return ret;
}
}
public static byte[] Read_Asc8_Buff_FromAsc7Buff(
byte[] vBuff
)
{
int DesLen = (vBuff.Length / 7 + 1) * 8;
byte[] desBuff = new byte[DesLen];
byte vLeft = 0;
byte vRight = 0;
byte vNew = 0;
int OrgIndex = 0;
int DesIndex = 0;
while (OrgIndex < vBuff.Length)
{
//取vRight 组建一个新byte
int off = (OrgIndex + 1) % 7;
if (off > 0)
{
vRight = vBuff[OrgIndex];
vRight = (byte)(vRight << (off));
vRight = (byte)(vRight >> (off));
vNew = vRight;
if (off > 1)//不够7位 总共off位
{
//取剩下的vLeft补充
vNew = (byte)(vNew << (off - 1));
vNew += vLeft;
}
//获取新的值
desBuff[DesIndex] = vNew;
DesIndex++;
//获取新的vLeft
vLeft = vBuff[OrgIndex];
vLeft = (byte)(vLeft >> (8 - off));
//原始序号增加1
OrgIndex++;
}
else
{
vRight = vBuff[OrgIndex];
vRight = (byte)(vRight << (7));
vRight = (byte)(vRight >> (7));
vNew = vRight;
if (1 > off)//不够7位 总共off位
{
//取剩下的vLeft补充
vNew += vLeft;
}
//获取新的值
desBuff[DesIndex] = vNew;
DesIndex++;
//获取新的值
vNew = (byte)(vBuff[OrgIndex] >> 1);
desBuff[DesIndex] = vNew;
DesIndex++;
//获取新的vLeft
vLeft = 0;
//原始序号增加1
OrgIndex++;
}
}
byte[] ret = new byte[DesIndex];
Array.Copy(desBuff, 0, ret, 0, DesIndex);
return ret;
}
}
public class PDUdecoding
{
public string nLength; //要发送内容的长度,由两部分组成,接收手机号加上要发送的内容
/// <summary>
/// 函数功能:短信内容编码
/// 函数名称:smsPDUEncoded(string srvContent)
/// 参 数:srvContent 要进行转换的短信内容,string类型
/// 返 回 值:编码后的短信内容,string类型
/// 程 序 员:sillnet@163.net
/// 编制日期:2003-10-15
/// 函数说明:
/// 1,采用Big-Endian 字节顺序的 Unicode 格式编码,也就说把高低位的互换在这里完成了
/// 2,将转换后的短信内容存进字节数组
/// 3,去掉在进行Unicode格式编码中,两个字节中的"-",例如:00-21,变成0021
/// 4,将整条短信内容的长度除2,保留两位16进制数
/// </summary>
public string smsPDUEncoded(string srvContent)
{
Encoding encodingUTF = System.Text.Encoding.BigEndianUnicode;
string s = null;
byte[] encodedBytes = encodingUTF.GetBytes(srvContent);
for (int i = 0; i < encodedBytes.Length; i++)
{
s += BitConverter.ToString(encodedBytes, i, 1);
}
System.Diagnostics.Debug.WriteLine(s);
string s2 = String.Format("{0:X2}{1}", s.Length / 2, s);
System.Diagnostics.Debug.WriteLine(s);
return s2;
}
/// <summary>
/// 函数功能:短信中心号编码
/// 函数名称:smsDecodedCenterNumber(string srvCenterNumber)
/// 参 数:srvCenterNumber 要进行转换的短信中心号,string类型
/// 返 回 值:编码后的短信中心号,string类型
/// 程 序 员:sillnet@163.net
/// 编制日期:2003-10-15
/// 函数说明:
/// 1,将奇数位和偶数位交换。
/// 2,短信中心号奇偶数交换后,看看长度是否为偶数,如果不是,最后添加F
/// 3,加上短信中心号类型,91为国际化
/// 4,计算编码后的短信中心号长度,并格化成二位的十六进制
/// </summary>
public string smsDecodedCenterNumber(string srvCenterNumber)
{
string s = null;
int nLength = srvCenterNumber.Length;
for (int i = 1; i < nLength; i += 2) //奇偶互换
{
s += srvCenterNumber[i];
s += srvCenterNumber[i - 1];
}
if (!(nLength % 2 == 0)) //是否为偶数,不是就加上F,并对最后一位与加上的F位互换
{
s += 'F';
s += srvCenterNumber[nLength - 1];
}
System.Diagnostics.Debug.WriteLine(s);
s = String.Format("91{0}", s);
System.Diagnostics.Debug.WriteLine(s);
//加上91,代表短信中心类型为国际化
s = String.Format("{0:X2}{1}", s.Length / 2, s); //编码后短信中心号长度,并格式化成二位十六制
System.Diagnostics.Debug.WriteLine(s);
return s;
}
/// <summary>
/// 函数功能:接收短信手机号编码
/// 函数名称:smsDecodedNumber(string srvNumber)
/// 参 数:srvCenterNumber 要进行转换的短信中心号,string类型
/// 返 回 值:编码后的接收短信手机号,string类型
/// 程 序 员:sillnet@163.net
/// 编制日期:2003-10-15
/// 函数说明:
/// 1,检查当前接收手机号是否按标准格式书写,不是,就补上“86”
/// 1,将奇数位和偶数位交换。
/// 2,短信中心号奇偶数交换后,看看长度是否为偶数,如果不是,最后添加F
/// </summary>
public string smsDecodedNumber(string srvNumber)
{
string s = null;
if (!(srvNumber.Substring(0, 2) == "86"))
{
srvNumber = String.Format("86{0}", srvNumber); //检查当前接收手机号是否按标准格式书写,不是,就补上“86”
}
int nLength = srvNumber.Length;
for (int i = 1; i < nLength; i += 2) //将奇数位和偶数位交换
{
s += srvNumber[i];
s += srvNumber[i - 1];
}
if (!(nLength % 2 == 0)) //是否为偶数,不是就加上F,并对最后一位与加上的F位互换
{
s += 'F';
s += srvNumber[nLength - 1];
}
return s;
}
/// <summary>
/// 函数功能:整个短信的编码
/// 函数名称:smsDecodedsms(string strCenterNumber, string strNumber, string strSMScontent)
/// 参 数:strCenterNumber 要进行转换的短信中心号,string类型
/// strNumber 接收手机号码,string类型
/// strSMScontent 短信内容
/// 返 回 值:完整的短信编码,可以在AT指令中执行,string类型
/// 程 序 员:sillnet@163.net
/// 编制日期:2003-10-15
/// 函数说明:
/// 11000D91和000800 在国内,根据PDU编码原则,我们写死在此,详细解释请看我的文章
/// </summary>
public string smsDecodedsms(string strCenterNumber, string strNumber, string strSMScontent)
{
string s = String.Format("{0}11000D91{1}000800{2}", smsDecodedCenterNumber(strCenterNumber), smsDecodedNumber(strNumber), smsPDUEncoded(strSMScontent));
nLength = String.Format("{0:D2}", (s.Length - smsDecodedCenterNumber(strCenterNumber).Length) / 2); //获取短信内容加上手机号码长度
return s;
}
}
}
JustinIO.cs
using System;
using System.Runtime.InteropServices;
//串口通信类库(JustinIO.cs)
namespace JustinIO
{
class CommPort
{
public string PortNum;
public int BaudRate;
public byte ByteSize;
public byte Parity; // 0-4=no,odd,even,mark,space
public byte StopBits; // 0,1,2 = 1, 1.5, 2
public int ReadTimeout;
//comm port win32 file handle
private int hComm = -1;
public bool Opened = false;
public string Writeinfo(DCB vDCB)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.Append("DCBlength=" + vDCB.DCBlength.ToString() + "/r/n");
sb.Append("BaudRate=" + vDCB.BaudRate.ToString() + "/r/n");
sb.Append("fBinary=" + vDCB.fBinary.ToString() + "/r/n");
/*
sb.Append("fOutxCtsFlow="+vDCB.fOutxCtsFlow.ToString()+"/r/n");
sb.Append("fOutxDsrFlow="+vDCB.fOutxDsrFlow.ToString()+"/r/n");
sb.Append("fDtrControl="+vDCB.fDtrControl.ToString()+"/r/n");
sb.Append("fDsrSensitivity="+vDCB.fDsrSensitivity.ToString()+"/r/n");
sb.Append("fTXContinueOnXoff="+vDCB.fTXContinueOnXoff.ToString()+"/r/n");
sb.Append("fOutX="+vDCB.fOutX.ToString()+"/r/n");
sb.Append("fInX="+vDCB.fInX.ToString()+"/r/n");
sb.Append("fErrorChar="+vDCB.fErrorChar.ToString()+"/r/n");
sb.Append("fNull="+vDCB.fNull.ToString()+"/r/n");
sb.Append("fRtsControl="+vDCB.fRtsControl.ToString()+"/r/n");
sb.Append("fAbortOnError="+vDCB.fAbortOnError.ToString()+"/r/n");
sb.Append("fDummy2="+vDCB.fDummy2.ToString()+"/r/n");
*/
// sb.Append("flags="+vDCB.flags.ToString()+"/r/n");
sb.Append("wReserved=" + vDCB.wReserved.ToString() + "/r/n");
sb.Append("XonLim=" + vDCB.XonLim.ToString() + "/r/n");
sb.Append("XoffLim=" + vDCB.XoffLim.ToString() + "/r/n");
sb.Append("ByteSize=" + vDCB.ByteSize.ToString() + "/r/n");
sb.Append("Parity=" + vDCB.Parity.ToString() + "/r/n");
sb.Append("StopBits=" + vDCB.StopBits.ToString() + "/r/n");
sb.Append("XonChar=" + vDCB.XonChar.ToString() + "/r/n");
sb.Append("XoffChar=" + vDCB.XoffChar.ToString() + "/r/n");
sb.Append("ErrorChar=" + vDCB.ErrorChar.ToString() + "/r/n");
sb.Append("EofChar=" + vDCB.EofChar.ToString() + "/r/n");
sb.Append("EvtChar=" + vDCB.EvtChar.ToString() + "/r/n");
sb.Append("wReserved1=" + vDCB.wReserved1.ToString() + "/r/n");
string ret = sb.ToString();
System.Diagnostics.Debug.WriteLine(ret);
return ret;
}
//win32 api constants
private const uint GENERIC_READ = 0x80000000;
private const uint GENERIC_WRITE = 0x40000000;
private const int OPEN_EXISTING = 3;
private const int INVALID_HANDLE_VALUE = -1;
[StructLayout(LayoutKind.Explicit)]
public struct DCB
{
[FieldOffset(0)]
public uint DCBlength; // sizeof(DCB)
[FieldOffset(4)]
public uint BaudRate; // 指定当前波特率 current baud rate
[FieldOffset(8)]
public uint fBinary; // 指定是否允许二进制模式,在windows95中必须主TRUE binary mode, no EOF check
/*
[FieldOffset(12)]public uint fParity; // 指定是否允许奇偶校验 enable parity checking
[FieldOffset(16)]public uint fOutxCtsFlow; // 指定CTS是否用于检测发送控制,当为TRUE是CTS为OFF,发送将被挂起。 CTS output flow control
[FieldOffset(20)]public uint fOutxDsrFlow; // 指定CTS是否用于检测发送控制 DSR output flow control
[FieldOffset(24)]public uint fDtrControl; // DTR_CONTROL_DISABLE值将DTR置为OFF, DTR_CONTROL_ENABLE值将DTR置为ON, DTR_CONTROL_HANDSHAKE允许DTR"握手" DTR flow control type
[FieldOffset(28)]public uint fDsrSensitivity; // 当该值为TRUE时DSR为OFF时接收的字节被忽略 DSR sensitivity
[FieldOffset(32)]public uint fTXContinueOnXoff; // 指定当接收缓冲区已满,并且驱动程序已经发送出XoffChar字符时发送是否停止。TRUE时,在接收缓冲区接收到缓冲区已满的字节XoffLim且驱动程序已经发送出XoffChar字符中止接收字节之后,发送继续进行。 FALSE时,在接收缓冲区接收到代表缓冲区已空的字节XonChar且驱动程序已经发送出恢复发送的XonChar之后,发送继续进行。XOFF continues Tx
[FieldOffset(36)]public uint fOutX; // TRUE时,接收到XoffChar之后便停止发送接收到XonChar之后将重新开始 XON/XOFF out flow control
[FieldOffset(40)]public uint fInX; // TRUE时,接收缓冲区接收到代表缓冲区满的XoffLim之后,XoffChar发送出去接收缓冲区接收到代表缓冲区空的XonLim之后,XonChar发送出去 XON/XOFF in flow control
[FieldOffset(44)]public uint fErrorChar; // 该值为TRUE且fParity为TRUE时,用ErrorChar 成员指定的字符代替奇偶校验错误的接收字符 enable error replacement
[FieldOffset(48)]public uint fNull; // eTRUE时,接收时去掉空(0值)字节 enable null stripping
[FieldOffset(52)]public uint fRtsControl; // RTS flow control
[FieldOffset(56)]public uint fAbortOnError; // TRUE时,有错误发生时中止读和写操作 abort on error
[FieldOffset(60)]public uint fDummy2; // 未使用 reserved
*/
[FieldOffset(12)]
public ushort wReserved; // 未使用,必须为0 not currently used
[FieldOffset(14)]
public ushort XonLim; // 指定在XON字符发送这前接收缓冲区中可允许的最小字节数 transmit XON threshold
[FieldOffset(16)]
public ushort XoffLim; // 指定在XOFF字符发送这前接收缓冲区中可允许的最小字节数 transmit XOFF threshold
[FieldOffset(18)]
public byte ByteSize; // 指定端口当前使用的数据位 number of bits/byte, 4-8
[FieldOffset(19)]
public byte Parity; // 指定端口当前使用的奇偶校验方法,可能为:EVENPARITY,MARKPARITY,NOPARITY,ODDPARITY 0-4=no,odd,even,mark,space
[FieldOffset(20)]
public byte StopBits; // 指定端口当前使用的停止位数,可能为:ONESTOPBIT,ONE5STOPBITS,TWOSTOPBITS 0,1,2 = 1, 1.5, 2
[FieldOffset(21)]
public char XonChar; // 指定用于发送和接收字符XON的值 Tx and Rx XON character
[FieldOffset(22)]
public char XoffChar; // 指定用于发送和接收字符XOFF值 Tx and Rx XOFF character
[FieldOffset(23)]
public char ErrorChar; // 本字符用来代替接收到的奇偶校验发生错误时的值 error replacement character
[FieldOffset(24)]
public char EofChar; // 当没有使用二进制模式时,本字符可用来指示数据的结束 end of input character
[FieldOffset(25)]
public char EvtChar; // 当接收到此字符时,会产生一个事件 received event character
[FieldOffset(26)]
public ushort wReserved1; // 未使用 reserved; do not use
}
[StructLayout(LayoutKind.Sequential)]
private struct COMMTIMEOUTS
{
public int ReadIntervalTimeout;
public int ReadTotalTimeoutMultiplier;
public int ReadTotalTimeoutConstant;
public int WriteTotalTimeoutMultiplier;
public int WriteTotalTimeoutConstant;
}
[StructLayout(LayoutKind.Sequential)]
private struct OVERLAPPED
{
public int Internal;
public int InternalHigh;
public int Offset;
public int OffsetHigh;
public int hEvent;
}
[DllImport("kernel32.dll")]
private static extern int CreateFile(
string lpFileName, // 要打开的串口名称
uint dwDesiredAccess, // 指定串口的访问方式,一般设置为可读可写方式
int dwShareMode, // 指定串口的共享模式,串口不能共享,所以设置为0
int lpSecurityAttributes, // 设置串口的安全属性,WIN9X下不支持,应设为NULL
int dwCreationDisposition, // 对于串口通信,创建方式只能为OPEN_EXISTING
int dwFlagsAndAttributes, // 指定串口属性与标志,设置为FILE_FLAG_OVERLAPPED(重叠I/O操作),指定串口以异步方式通信
int hTemplateFile // 对于串口通信必须设置为NULL
);
[DllImport("kernel32.dll")]
private static extern bool GetCommState(
int hFile, //通信设备句柄
ref DCB lpDCB // 设备控制块DCB
);
[DllImport("kernel32.dll")]
private static extern bool BuildCommDCB(
string lpDef, // 设备控制字符串
ref DCB lpDCB // 设备控制块
);
[DllImport("kernel32.dll")]
private static extern bool SetCommState(
int hFile, // 通信设备句柄
ref DCB lpDCB // 设备控制块
);
[DllImport("kernel32.dll")]
private static extern bool GetCommTimeouts(
int hFile, // 通信设备句柄 handle to comm device
ref COMMTIMEOUTS lpCommTimeouts // 超时时间 time-out values
);
[DllImport("kernel32.dll")]
private static extern bool SetCommTimeouts(
int hFile, // 通信设备句柄 handle to comm device
ref COMMTIMEOUTS lpCommTimeouts // 超时时间 time-out values
);
[DllImport("kernel32.dll")]
private static extern bool ReadFile(
int hFile, // 通信设备句柄 handle to file
byte[] lpBuffer, // 数据缓冲区 data buffer
int nNumberOfBytesToRead, // 多少字节等待读取 number of bytes to read
ref int lpNumberOfBytesRead, // 读取多少字节 number of bytes read
ref OVERLAPPED lpOverlapped // 溢出缓冲区 overlapped buffer
);
[DllImport("kernel32.dll")]
private static extern bool WriteFile(
int hFile, // 通信设备句柄 handle to file
byte[] lpBuffer, // 数据缓冲区 data buffer
int nNumberOfBytesToWrite, // 多少字节等待写入 number of bytes to write
ref int lpNumberOfBytesWritten, // 已经写入多少字节 number of bytes written
ref OVERLAPPED lpOverlapped // 溢出缓冲区 overlapped buffer
);
[DllImport("kernel32.dll")]
private static extern bool CloseHandle(
int hObject // handle to object
);
[DllImport("kernel32.dll")]
private static extern uint GetLastError();
public void Open()
{
DCB dcbCommPort = new DCB();
COMMTIMEOUTS ctoCommPort = new COMMTIMEOUTS();
// 打开串口 OPEN THE COMM PORT.
hComm = CreateFile(PortNum,
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
0,
0);
// 如果串口没有打开,就打开 IF THE PORT CANNOT BE OPENED, BAIL OUT.
if (hComm == INVALID_HANDLE_VALUE)
{
throw (new ApplicationException("非法操作,不能打开串口!"));
}
// 设置通信超时时间 SET THE COMM TIMEOUTS.
GetCommTimeouts(hComm, ref ctoCommPort);
ctoCommPort.ReadTotalTimeoutConstant = ReadTimeout;
ctoCommPort.ReadTotalTimeoutMultiplier = 0;
ctoCommPort.WriteTotalTimeoutMultiplier = 0;
ctoCommPort.WriteTotalTimeoutConstant = ReadTimeout;
SetCommTimeouts(hComm, ref ctoCommPort);
// 设置串口 SET BAUD RATE, PARITY, WORD SIZE, AND STOP BITS.
GetCommState(hComm, ref dcbCommPort);
dcbCommPort.BaudRate = (uint)BaudRate;
dcbCommPort.Parity = Parity;
dcbCommPort.ByteSize = ByteSize;
dcbCommPort.StopBits = StopBits;
if (!SetCommState(hComm, ref dcbCommPort))
{
//uint ErrorNum=GetLastError();
throw (new ApplicationException("非法操作,不能打开串口!"));
}
Writeinfo(dcbCommPort);
Opened = true;
}
public void Close()
{
if (hComm != INVALID_HANDLE_VALUE)
{
CloseHandle(hComm);
}
}
public byte[] Read(int NumBytes)
{
byte[] BufBytes;
byte[] OutBytes;
BufBytes = new byte[NumBytes];
if (hComm != INVALID_HANDLE_VALUE)
{
OVERLAPPED ovlCommPort = new OVERLAPPED();
int BytesRead = 0;
ReadFile(hComm, BufBytes, NumBytes, ref BytesRead, ref ovlCommPort);
OutBytes = new byte[BytesRead];
Array.Copy(BufBytes, OutBytes, BytesRead);
}
else
{
throw (new ApplicationException("串口未打开!"));
}
return OutBytes;
}
public void Write(byte[] WriteBytes)
{
if (hComm != INVALID_HANDLE_VALUE)
{
OVERLAPPED ovlCommPort = new OVERLAPPED();
int BytesWritten = 0;
WriteFile(hComm, WriteBytes, WriteBytes.Length, ref BytesWritten, ref ovlCommPort);
}
else
{
throw (new ApplicationException("串口未打开!"));
}
}
}
class HexCon
{
// 把十六进制字符串转换成字节型和把字节型转换成十六进制字符串 converter hex string to byte and byte to hex string
public static string ByteToString(byte[] InBytes)
{
string StringOut = "";
foreach (byte InByte in InBytes)
{
StringOut = StringOut + String.Format("{0:X2} ", InByte);
}
return StringOut;
}
public static byte[] StringToByte(string InString)
{
string[] ByteStrings;
ByteStrings = InString.Split(" ".ToCharArray());
byte[] ByteOut;
ByteOut = new byte[ByteStrings.Length - 1];
for (int i = 0; i == ByteStrings.Length - 1; i++)
{
ByteOut[i] = Convert.ToByte(("0x" + ByteStrings[i]));
}
return ByteOut;
}
}
}