AppleSeeker's Tech Blog
Welcome to AppleSeeker's space

本篇文章主要讲述短信拦截的一些方法,虽然该方法不是最理想的方法,但仍然能够达到相同的功效。
作为高级篇的第一篇文章,希望文中的一些API的使用方法,能够给更多人予以帮助。

在.net 2.0中,MS就提供了Microsoft.WindowsMobile.PocketOutlook.MessageInterception命名空间,通过使用该命名空间,可以截获短信和邮件的到达消息。
该命名空间中的一些类及属性:
1.MessageInterceptor:监控对象类,一旦添加监控事件后,就会对所有消息进行监控。
2.MessageCondition:监控的过滤筛选条件类,通过设置需要过滤某个字段。
3.MessageInterceptorEventHandler:监控事件,一旦截获某消息,就立即执行该方法。
4.InterceptionAction(枚举):
Notify(提示,但此时系统仍会第一时间处理,自定义处理虽然有效,但肯定不是大家想要的。)
NotifyAndDelete(系统不会做任何处理,直接交给用户自己处理,同时达到的消息,如果用户不处理,则会删除。)
5.MessageProperty(枚举):按照某个属性进行对比
6.MessagePropertyComparisonType(枚举):对比方法

运行程序效果如下:

在Menu菜单下,点击Message Intercepter事件,开始监听。在此,我设置了,当发短消息的用户为Test Man时,将短消息过滤,不会在短消息的收件箱中出现,截获的内容这会在界面上显示。

测试:运行Cellular Emulator程序,输入电话号码及短信内容,发送到模拟器上。

模拟器效果如下:

可以看到消息已经被截获了,且消息内容一致。

如果发送号码为123456788,则不是添加的联系人(Test Man)。则不会截获。

代码如下:

 1        private void menuItem4_Click(object sender, EventArgs e)
 2        {
 3            MessageInterceptor msgInterceptor = new MessageInterceptor();
 4            msgInterceptor.InterceptionAction = InterceptionAction.NotifyAndDelete;
 5
 6            MessageCondition msgCondition = new MessageCondition();
 7            msgCondition.ComparisonType = MessagePropertyComparisonType.Contains;
 8            msgCondition.Property = MessageProperty.Sender;
 9            msgCondition.ComparisonValue = "Test Man";
10
11            msgInterceptor.MessageCondition = msgCondition;
12
13            msgInterceptor.MessageReceived += new MessageInterceptorEventHandler(msgInterceptor_MessageReceived);
14        }

15
16        void msgInterceptor_MessageReceived(object sender, MessageInterceptorEventArgs e)
17        {
18            this.listBox1.Items.Add("Type Name: " + e.Message.GetType().Name);
19
20            if (e.Message.GetType() == typeof(SmsMessage))
21            {
22                SmsMessage sms = (SmsMessage)e.Message;
23                this.listBox1.Items.Add("From: " + sms.From.Name);
24                this.listBox1.Items.Add("Body: " + sms.Body);
25                this.listBox1.Items.Add("Received Tiem: " + sms.Received.ToString("yyyy-MM-dd"));
26            }

27            if (e.Message.GetType() == typeof(EmailMessage))
28            {
29                EmailMessage mail = (EmailMessage)e.Message;
30
31                this.listBox1.Items.Add("ItemId: " + mail.ItemId.ToString());
32            }

33        }

Tips:
1.使用MessageInterceptor能监视的只是一个属性,如果根据不同用户,指定不同过滤操作的话,就无法实现。
2.一旦将InterceptionAction设为NotifyAndDelete,则系统就不做处理了,接收到的消息,就不会在收件箱中出现,需要自己添加进去(下文会介绍如何实现)
3.可以不设置MessageInterceptorMessageCondition,这样使得所有消息都截获,然后自己处理,这时,就可以设置更广泛的过滤。

一旦将消息全部截获后,必不可少的会遇到一些不需要过滤的消息,此时,还要将消息重新放入收件箱中。很遗憾,暂时在托管环境中没有相应的操作类,相信MS会在以后的CF版本中会对此进行扩展的。

下面将介绍如何SIM卡的短消息的API。
涉及的API:
SimInitialize方法:初始化SIM卡的对象指针。
SimDeinitialize方法:释放SIM卡指针,类似于Ado里的连接对象,操作完后,关闭对象。
SimGetSmsStorageStatus方法:得到
SIM卡里的可以存放的最大消息个数及已存放的消息个数。
SimReadMessage方法:从SIM卡中读取短信(如果SIM卡有短信)。
SimWriteMessage方法:向SIM卡中写入或修改短信。
SimDeleteMessage方法:删除SIM卡中某条短信。
涉及的结构体:
SimMessageTag:定义某条消息中的所有信息。
SystemTime:自定义的一个时间结构体。

在运行的程序中点击Test事件,则会读取SIM卡中所有短信,并且向SIM卡中添加一条短信。

运行该事件前,短信收件箱。

运行结果,读取SIM卡中的短信内容(只有1条)。

查看短信收件箱,该短信已写入。

通过这些API,我们可以很方便的将自己需要的短信写入SIM,而在过滤事件中过滤不需要的。

代码如下:
  1        [DllImport("cellcore.dll", SetLastError=true)]
  2        private static extern int SimInitialize(int dwFlags, int lpfnCallBack, int dwParam, out int lphSim);
  3
  4        [DllImport("cellcore.dll", SetLastError = true)]
  5        private static extern int SimDeinitialize(int hSim);
  6
  7        [DllImport("cellcore.dll", SetLastError = true)]
  8        public static extern int SimGetSmsStorageStatus(int hSim, int dwStorage, ref int lpdwUsed, ref int lpdwTotal);
  9
 10        [DllImport("cellcore.dll", SetLastError = true)]
 11        private static extern int SimWriteMessage(int hSim, int dwStorage, ref int lpdwIndex, ref SimMessageTag SmsStructType);
 12
 13        [DllImport("cellcore.dll", SetLastError = true)]
 14        private static extern int SimReadMessage(int hSim, int dwStorage, int lpdwIndex, ref SimMessageTag SmsStructType);
 15
 16        [DllImport("cellcore.dll", SetLastError = true)]
 17        private static extern int SimDeleteMessage(int hSim, int dwStorage, ref int lpdwIndex);
 18
 19        private void menuItem6_Click(object sender, EventArgs e)
 20        {
 21            int hSim = 0, res = 0;
 22
 23            try
 24            {
 25                res = SimInitialize((int)SIM_INIT_NONE, 0, (int)SIM_PARAM_MSG_ALL, out hSim);
 26
 27                if (res != 0)
 28                    throw new Exception("Could not initialize SIM");
 29
 30                int used = 0, total = 0;
 31
 32                res = SimGetSmsStorageStatus(hSim, (int)SIM_NUMSMSSTORAGES, ref used, ref total);
 33
 34                if (res == 0)
 35                {
 36                    this.listBox1.Items.Add("Used: " + used.ToString());
 37                    this.listBox1.Items.Add("Total: " + total.ToString());
 38                }

 39                else
 40                {
 41                    this.listBox1.Items.Add("Last Error: " + Marshal.GetLastWin32Error().ToString());
 42                }

 43
 44                SimMessageTag message = new SimMessageTag();
 45
 46                int index = 1;
 47
 48                //res = SimDeleteMessage(hSim, (int)SIM_SMSSTORAGE_BROADCAST, ref index);
 49
 50                //if (res != 0)
 51                //{
 52                //    this.listBox1.Items.Add("Last Error: " + Marshal.GetLastWin32Error().ToString());
 53                //}
 54                //this.listBox1.Items.Add("Index: " + index.ToString());
 55
 56                for (int j = 1; j <= used; j++)
 57                {
 58                    res = SimReadMessage(hSim, (int)SIM_NUMSMSSTORAGES, j, ref message);
 59                    if (res == 0)
 60                    {
 61                        this.listBox1.Items.Add("From: " + message.lpszAddress);
 62                        this.listBox1.Items.Add("CBSize: " + message.cbSize.ToString());
 63                        this.listBox1.Items.Add("Address Type: " + message.dwAddressType.ToString());
 64                        this.listBox1.Items.Add("NumPlan: " + message.dwNumPlan.ToString());
 65                        this.listBox1.Items.Add("Params: " + message.dwParams.ToString());
 66                        char[] header = new char[message.rgbHeader.Length];
 67                        string msg = "";
 68                        for (int i = 0; i < message.rgbHeader.Length; i++)
 69                        {
 70                            header[i] = (char)message.rgbHeader[i];
 71                            msg += header[i].ToString();
 72                        }

 73                        this.listBox1.Items.Add("Header: " + msg);
 74                        this.listBox1.Items.Add("Receive Time: " + message.stReceiveTime.ToString());
 75                        this.listBox1.Items.Add("Message: " + message.lpszMessage);
 76                        this.listBox1.Items.Add("");
 77                    }

 78                    else
 79                    {
 80                        this.listBox1.Items.Add("Last Error: " + Marshal.GetLastWin32Error().ToString());
 81                    }

 82                }

 83
 84                SimMessageTag msg1 = new SimMessageTag();
 85                msg1.cbSize = message.cbSize;
 86                msg1.dwAddressType =1;
 87                msg1.dwNumPlan = 1;
 88                msg1.dwParams = 111;
 89                msg1.lpszAddress = "123456789";
 90                msg1.stReceiveTime = new global::SystemTime(System.DateTime.Now);
 91                msg1.lpszMessage = "It is a test mail!";
 92                msg1.cbHdrLength = 0;
 93                msg1.rgbHeader = new byte[256];
 94
 95                index = used + 1;
 96
 97                res = SimWriteMessage(hSim, (int)SIM_NUMSMSSTORAGES, ref index, ref msg1);
 98                if (res != 0)
 99                {
100                    this.listBox1.Items.Add("Last Error: " + Marshal.GetLastWin32Error().ToString());
101                }

102            }

103            catch (Exception ex)
104            {
105                MessageBox.Show(ex.Message);
106            }

107            finally
108            {
109                this.listBox1.Items.Add("Result: " + res.ToString());
110                SimDeinitialize(hSim);
111            }

112        }
DllImport中定义SetLastError,这样在调用API出错时,可以通过Marshal.GetLastWin32Error()来取得ErrorCode。
SimInitialize方法的第一个参数,网上很多直接写0,为什么是0,因为常量SIM_INIT_NONE定义为0。
在操作SIM卡消息时的所有方法都需要dwStorage参数,该参数常量定义为:
SIM_NUMSMSSTORAGES = 2,从SIM卡里读取
SIM_SMSSTORAGE_BROADCAST = 1,从设备本地读取
添加一条消息时,该消息结构体的赋值中
dwAddressType = 1 (SIM_ADDRTYPE_INTERNATIONAL)
dwNumPlan = 1 (SIM_NUMPLAN_TELEPHONE)
dwParams = 111 必须设为111,否则添加不了,该数字是属性累加起来的
cbHdrLength = 0 (短信头的长度通常为0)
 1[StructLayout(LayoutKind.Sequential)]
 2public struct SystemTime
 3{
 4    public short wYear;
 5    public short wMonth;
 6    public short wDayOfWeek;
 7    public short wDay;
 8    public short wHour;
 9    public short wMinute;
10    public short wSecond;
11    public short wMilliseconds;
12
13    public SystemTime(System.DateTime now)
14    {
15        wYear = (short)now.Year;
16        wMonth = (short)now.Month;
17        wDayOfWeek = (short)now.DayOfWeek;
18        wDay = (short)now.Day;
19        wHour = (short)now.Hour;
20        wMinute = (short)now.Minute;
21        wSecond = (short)now.Second;
22        wMilliseconds = (short)now.Millisecond;
23    }

24    public override string ToString()
25    {
26        return string.Format("{0}/{1}/{2}", wYear, wMonth, wDay);
27    }

28}

29
30[StructLayout(LayoutKind.Sequential)]
31public struct SimMessageTag
32{
33public int cbSize; // Size of the structure in bytes
34public int dwParams; //Indicates valid parameter values
35
36[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
37public string lpszAddress; //An array that contains the actual phone number
38
39public int dwAddressType; //A SIM_ADDRTYPE constant
40/*
41SIM_ADDRTYPE_UNKNOWN = Unknown.
42SIM_ADDRTYPE_INTERNATIONAL = International number.
43SIM_ADDRTYPE_NATIONAL 0ne National = number.
44SIM_ADDRTYPE_NETWKSPECIFIC Network = specific number.
45SIM_ADDRTYPE_SUBSCRIBER Subscriber = number
46(protocol-specific).
47SIM_ADDRTYPE_ALPHANUM Alphanumeric = address.
48SIM_ADDRTYPE_ABBREV Abbreviated = number.
49*/

50
51public int dwNumPlan; //A SIM_NUMPLAN constant
52/*
53SIM_NUMPLAN_UNKNOWN = Unknown.
54SIM_NUMPLAN_TELEPHONE = ISDN/telephone numbering plan
55(E.164/E.163).
56SIM_NUMPLAN_DATA = Data numbering plan (X.121).
57SIM_NUMPLAN_TELEX = Telex numbering plan.
58SIM_NUMPLAN_NATIONAL = National numbering plan.
59SIM_NUMPLAN_PRIVATE = Private numbering plan.
60SIM_NUMPLAN_ERMES ERMES = numbering plan (ETSI DE/PS 3 01-3).
61*/

62
63public SystemTime stReceiveTime; //Timestamp for the incoming message
64
65public int cbHdrLength; //Header length in bytes
66
67[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
68public byte[] rgbHeader; //An array containing the actual header data
69
70[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
71public string lpszMessage; //An array containing the actual message data
72}
字段长度设置,根据参考该dll的头文件定义的,为256

Tips:
1.在调查dll的某些API的过程确实很复杂,需要不断尝试,这里推荐大家,一旦调用出错,可以通过Marshal.GetLastWin32Error()来取得ErrorCode,这样可以知道错误类型。在调用API时,尽量多参考其头文件,这样可以很方便了解这些方法中的一些结构体及一些常量。
2.如果运行模拟器的话,向SIM卡里添加短信后,在短信收件箱中不会显示出来,但实际是添加进去了。必须通过关闭电话,然后重新设置电话网络,这样SIM卡会重新加载。实际的设备应该不会这样。

总结:
本文提供给大家在.net cf下如何实现短信拦截的一个思路,该方法确实是可行的,希望大家能够掌握。短信拦截的方法不是唯一的,以后会介绍别的方法。

代码下载(附头文件以便大家能够理解):SmartDeviceOutlookDemo_2008_03_28.rar
cellcore.rar

Author:AppleSeeker
Date:2008-03-29
posted on 2008-03-29 17:35  AppleSeeker(冯峰)  阅读(7923)  评论(39编辑  收藏  举报