利用RIL代理接口实现短信的操作之短信的接收

http://blog.csdn.net/depraved_survival/archive/2009/06/17/4277725.aspx

    作者:吴春雷

QQ:819543772

Email:[email protected]

提到在Windows Mobile客户端对短信进行操作,几乎所有人都会在第一时间想起CEMAPI接口,诚然cemapi接口是目前为止使用最多,也是最为成熟的技术,利用Cemapi接口可以很方便的实现短信的发送、接收、删除等相关操作,而无需关注繁琐的编码解码问题,但Cemapi也有自己的缺点,比如cemapi中接口完全基于tmail实现,短信截获、发送、到达通知等操作最终也是由tmail来实现的,这就意味着,程序中短信操作的模块需要完全依赖于tmail的执行状态,例如,当tmail程序没有启动时,客户端执行submitmessage操作后短信并不会被马上发出,必须要等到tmail启动后,短信才会被发送,由于该接口的返回值仅仅指明与短信数据库进行操作是否成功,因此在程序中无法获知短信是否已经成功发送。

那么,除了Cemapi接口以外,还有没办法在更加底层的位置对短信进行操作呢?答案是直接访问RIL层。但是很不幸,RIL层的驱动程序每个厂家的实现都有可能不同,并且作为操作系统的核心程序,被烧到了ROM中,由于不知道手机厂家具体是怎么实现的RIL层驱动,因此重新编译了rilgsm.dll模块替换原有模块的是很难实现的。但是,微软提供了标准的RIL层的代理接口,通过一系列的代理接口可以使客户端程序在有限的条件下对RIL层进行控制(不过这部分源代码微软没有公开,所以说是有条件的对RIL层进行控制)。

本文利用RIL代理接口实现了对到达短信的接收,并将到达的短信内容通知给客户端程序,由于RIL层所接收到的数据会同时通知给所有注册过RIL代理接口的程序,因此,即便系统已经有实现IMailRuleClient接口的拦截程序,你的程序仍然可以通过RIL代理接口接收到该短信。

RIL层代理使用非常简单,通过RIL_Initialize函数进行初始化,然后就可以通过初始化时设置的回调函数得到RIL端口获取到的数据了。在初始化的时候,有两个回调函数需要被设置,其一是数据回调函数,当RIL端口接收到数据后,会通过该回调将数据通知给客户端程序,其二是结果回调函数,当客户端像RIL端口请求某些数据和操作后,RIL代理会通过该回调将处理和请求结果通知给客户端,个人感觉,这种数据和结果分别用回调函数通知的技术还是很值得学习的。

RIL_Initialize函数的原型如下:

HRESULT RIL_Initialize(DWORD dwIndex,

    RILRESULTCALLBACK pfnResult,

    RILNOTIFYCALLBACK pfnNotify,

    DWORD dwNotificationClasses,

    DWORD dwParam,

    HRIL* lphRil);

函数返回S_OK表示初始化成功,否则失败。参数意义如下:

dwIndex:RIL端口号,例如,如果端口为RIL1: 则dwIndex=1

pfnResult:请求和处理结果的回调

pfnNotify:数据到达通知的回调

dwNotifycationClasses:用于确定将那些类型的通知发送给本客户端

dwParam: 某种约定的标志,一般为55AA55AA,搞低层的朋友应该了解

 

RILRESULTCALLBACK回调定义如下:

void CALLBACK ResultCallback(DWORD dwCode, HRESULT hrCmdID, const void *lpData, DWORD cbData, DWORD dwParam)

参数的定义:

dwCode:结果代码

hrCmdID:产生该回调的命令的ID

lpData:结果数据的字符串

dwParam:与初始化时的dwParam参数相同,一般为55AA55AA

 

RILNOTIFYCALLBACK定义如下:

typedef void (CALLBACK *RILNOTIFYCALLBACK)(

DWORD dwCode,          

const void* lpData,

    DWORD cbData,      

    DWORD dwParam      

);

 

参数定义为:

dwCode:通知的代码

lpData:到达的数据

cbData:lpData指向的结构体对象的size

dwParam:55AA55AA

 

注册成功后,当有数据到达RIL端口的时候,RILNOTIFYCALLBACK回调就会被触发,参数dwCode指示了当前通知的类型,lpData中的内容到为RIL端口接收到的原始数据。对GSM编码有了解的朋友应该知道,到达RIL端口的数据是不能直接被阅读的,因为这些数据在发送时被编了码。常见的编码方式有两种,第一种被称作7位码,原理是将8位的ASCII码中没有用到的最高位去掉,然后按照某种规则将8位ASCII码的字符串流转换为7位ASCII码的字符串流,这样对于每个字符能够节省1个bit的空间,对于GSM这种大数据量(指服务器端)的交换有着重要的意义,但是这种编码方式的缺点显而易见,那就是无法编码中文,于是出现了第二种编码方式,也就双字节编码,这种方式把字符流编码成unicode格式,其优点是编解码简单,缺点也很明显,任何一个非中文字符也被编码为2个字节,相同长度的数据将增加一倍的Size.一般来讲手机会对这两种编码方式同时支持,其原则是,只要短信中存在中文字符,哪怕只有一个,该短信的编码方式就是双字节编码,否则编码方式就采用7位码的方式。

简单的介绍一下两种编码方式的算法。

一.双字节编码(UCS2):

这种编码方式比较简单,编码时,我们先把要发送的字符串转换成unicode格式的数据流,然后,将数据流中偶数字节和奇数字节所对应的数据相交换即可,也就是说,将每个unicode码的字符前后两个字节交换。解码时,只需再对数据流做一次奇偶字节的交换,就是unicode码的字符串。

例如:

源字符串:我是一个兵

Unicode:11 62 2f 66 0 4e 2a 4e 75 51

双字节编码:62 11 66 2f 4e 00 4e 2a 51 75

二.7位码

这种编码方式略相对与双字节编码稍微复杂些。下面分别看一下7位码的编解码算法。

编码:将字符串转换成2进制流,每8位分为一组,去掉每组的最高位bit8。将第二组的最低位bit0移动到第一组的bit8位上形成一个字节,然后将第二组左移2位,将第三组的最低两位bit1,bit0移动到第二组的最高两位上,将第三组左移3位,将第四组的低三位bit2,bit1,bit0移动到第三组的高三位上,bit8,bit7,bit6,以此类推。最后一组如果不够8位,则在高位补零。

例如:1234

二进制流:00110001 00110010 00110011 00110100

编码后:00110001 11011001 10001100 00000110

 

解码:与编码的过程相反。将待解码的数据转换为二进制流,每8位编为一组。取第一组的最高为bit8,并将最高位置0。取第二组的高两位bit8,bit7,并将高两位置0,左移1位,将从第一组取出的最高位bit8放置在第二组末尾,在第二组的最高位补0凑齐一个字节(8位)。取第三组的高三位bit8,bit7,bit6,将高三位置0,左移2位,将第二组的高两位bit8,bit7放置在第三组的末尾,在第三组最高位补0凑齐一个字节(8位),以此类推。

例如:

接收到: 00110001 11011001 10001100 00000110

解码后:  00110001 00110010 00110011 00110100

本文中只需要对接收到的数据进行解码,因此这里只给出两种编码方式中解码的函数,有关编码的部分,有兴趣的朋友可以自己编写。

//将接受到的双字节码转换为Unicode

void ConvertDataToUnicode(BYTE *psData,int nLength,wchar_t *ppszRet)

{  

    BYTE btTemp=0;   

    int i=0;

 

    for(i=0;i<nLength;i++)      //将前后两位交换,组成UNICODE格式的BYTE流

    {

       btTemp=psData[i*2];

       psData[i*2]=psData[i*2+1];

       psData[i*2+1]=btTemp;

    }

 

    ppszRet=(wchar_t*)psData;

}

//将接收到的7位码转换为ASCII

void Convert7BitsToASCII(BYTE *psData, int nLength,char **ppsRet)

{

 

 

    int m=1;      //取字符前几位

    int n=0;      //左移多少位

    BYTE btPrev=0;       //用于保存从左侧取出的数据

    BYTE bt=0;

    BYTE *p=psData;

 

    //计算还原后的长度,每位原始数据还原为位数据

    int nBC=nLength/7;

    *ppsRet=new char[nBC+nLength+1];       //分配空间

    memset(*ppsRet,0x00,nBC+nLength+1);

    int nCount=0;    

    int i=0;

    while(i<nLength-1)      

    {

 

       if(m==8)

       {

           (*ppsRet)[nCount++]=(*(p-1))>>1;

           m=1;n=0;

           //p++;

       }     

       else

       {

           //当前字符将前n+1位置零然后左移n位

           //将上一个字符的前m位放置到上一步结果的后m位上

           int n1=0xff>>(n+1);

           int n2=*(p+1)&n1;

           int n3=n2<<n;

 

           if(m==1)      //每轮次的第一个字符

           {

              (*ppsRet)[nCount++]=*p & 0x7F;

           }

           else

           {

              bt=(*(p-1))>>(8-m+1);

              (*ppsRet)[nCount++]=bt | ((*p & (0xFF>>(n+1)))<<n);

 

           }

           m++;

           n++;

           p++;             

 

           i++;  

       }

    }

    (*ppsRet)[nCount]='\0';

    return ;

}

 

有了上面的内容做基础,我们就可以利用RILNOTIFYCALLBACK回调中的数据来获取的到达手机的短信了。当有短消息到达的时候,通知回调函数中的dwCode值与RIL_NCLASS_ALL做”与”操作如果为RIL_NCLASS_MESSAGE,则到达的数据将与短信相关。然后通过dwCode的值,可以判断当前接收到的消息的类型。当dwCode为RIL_NOTIFY_MESSAGE的时候表示接收到的消息为短信,这时lpData指向一个结构体RILMESSAGE,该结构体封装从RIL接收到的数据和数据的属性,RILMESSAGE由若干个结构体组成,本文只关注msgInDeliver,该结构体中封装了接收到的短消息数据,其中raOrigAddress中可以获取到来源电话,rmdDataCoding成员中的dwAlphabet字段可以用来判断当前数据的编码格式,dwAlphabet=3时为双字节编码(UCS2),dwAlphabet=1的时候为7位码。rgbMsg中保存了接收到的原始数据,ccbMsgLength为原始数据长度。将rgbMsg作为参数传递给前面给出的解码函数中,即可获得短消息正文。RILNOTIFYCALLBACK函数的源代码如下:

void CALLBACK Notify (DWORD dwCode, const void *lpData,

                                                  DWORD cbData, DWORD dwParam)

{

     if((dwCode & RIL_NCLASS_ALL)== RIL_NCLASS_MESSAGE) {

     {

 

if(dwCode==RIL_NOTIFY_MESSAGE)

         {

              RILMESSAGE *prm = (RILMESSAGE *)lpData;

              if(prm->dwType==RIL_MSGTYPE_IN_DELIVER)

{                     

                   char *psRet=NULL;

                   memset(psz,0x00,sizeof(wchar_t)*prmMsg->msgInDeliver.cchMsgLength);

Convert7BitsToASCII(prmMsg->msgInDeliver.rgbMsg, prmMsg->msgInDeliver.cchMsgLength,&psRet);

}

}                     

 

}

你可能感兴趣的:(接口)