MOBILE开发中的短信拦截
在.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)。则不会截获。
代码如下:
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.可以不设置MessageInterceptor的MessageCondition,这样使得所有消息都截获,然后自己处理,这时,就可以设置更广泛的过滤。
一旦将消息全部截获后,必不可少的会遇到一些不需要过滤的消息,此时,还要将消息重新放入收件箱中。很遗憾,暂时在托管环境中没有相应的操作类,相信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,而在过滤事件中过滤不需要的。
代码如下:
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)
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卡会重新加载。实际的设备应该不会这样。