本篇文章主要讲述短信拦截的一些方法,虽然该方法不是最理想的方法,但仍然能够达到相同的功效。
作为高级篇的第一篇文章,希望文中的一些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.可以不设置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,而在过滤事件中过滤不需要的。
代码如下:
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)]
2
public 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)]
31
public struct SimMessageTag
32

{
33
public int cbSize; // Size of the structure in bytes
34
public int dwParams; //Indicates valid parameter values
35
36
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
37
public string lpszAddress; //An array that contains the actual phone number
38
39
public int dwAddressType; //A SIM_ADDRTYPE constant
40
/**//*
41
SIM_ADDRTYPE_UNKNOWN = Unknown.
42
SIM_ADDRTYPE_INTERNATIONAL = International number.
43
SIM_ADDRTYPE_NATIONAL 0ne National = number.
44
SIM_ADDRTYPE_NETWKSPECIFIC Network = specific number.
45
SIM_ADDRTYPE_SUBSCRIBER Subscriber = number
46
(protocol-specific).
47
SIM_ADDRTYPE_ALPHANUM Alphanumeric = address.
48
SIM_ADDRTYPE_ABBREV Abbreviated = number.
49
*/
50
51
public int dwNumPlan; //A SIM_NUMPLAN constant
52
/**//*
53
SIM_NUMPLAN_UNKNOWN = Unknown.
54
SIM_NUMPLAN_TELEPHONE = ISDN/telephone numbering plan
55
(E.164/E.163).
56
SIM_NUMPLAN_DATA = Data numbering plan (X.121).
57
SIM_NUMPLAN_TELEX = Telex numbering plan.
58
SIM_NUMPLAN_NATIONAL = National numbering plan.
59
SIM_NUMPLAN_PRIVATE = Private numbering plan.
60
SIM_NUMPLAN_ERMES ERMES = numbering plan (ETSI DE/PS 3 01-3).
61
*/
62
63
public SystemTime stReceiveTime; //Timestamp for the incoming message
64
65
public int cbHdrLength; //Header length in bytes
66
67
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
68
public byte[] rgbHeader; //An array containing the actual header data
69
70
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
71
public 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