UML软件工程组织

开发启用了 SMS 的业务流程应用程序
Christian Forsberg
businessanyplace.net

2003 年 5 月

适用于:
    Microsoft® .NET Compact Framework
    Microsoft Visual Studio® .NET
    Microsoft Windows® Powered Pocket PC Phone

摘要:作为移动电话家族的高级成员,具有内置电话功能的 Windows Powered Pocket PC Phone 有多个连接选项。核心功能为可连接至 Internet,除此之外,还有其他可用选项。在所有的全球移动通信系统 (GSM) 网络中,一种常用的通信方式便是使用短消息服务器 (SMS) 消息。在设计连接移动电话应用程序时,这是一个不容忽视的选项,本文将对其原因进行解释。(本文还包含英文链接。请注意,在示例文件中,程序员的注释使用的是英文,本文中将其译为中文是为了便于读者理解。)

下载 sms_apps.exe

目录

什么是 SMS?
为什么使用 SMS?
SMS API
SMS Anyplace 示例
代码演练
小结

什么是 SMS?

短消息服务器使移动电话(包括 Pocket PC Phone)能够使用 GSM 网络发送短消息,它具有许多有趣的功能:

  • 一个 SMS 消息最长可包括 160 个字符(偶数二进制)。
  • SMS 是一种存储和转发服务。也就是说,短消息并不是直接从发送人发送到接收人,而始终通过 SMS 中心进行转发。如果接收人处于未连接状态(可能电话已关闭),则消息将在接收人再次连接时发送。
  • SMS 具有消息发送确认的功能。这意味着 SMS 与寻呼不同,用户不是简单地发出短消息然后相信消息已发送成功;而是短消息发送人可以收到返回消息,通知他们短消息是否已发送成功。
  • SMS 消息的发送和接收可以和 GSM 语音同步进行。
  • SMS 消息按消息收费,因此要比通过基于 IP 的网络(例如,使用 GPRS [通用分组无线业务])发送的数据昂贵得多(每字节)。

要使用 SMS,用户需要预订支持 SMS 的移动网络,并且必须为该用户启用 SMS 的使用。用户需要有发送短消息或接收短消息的目的地。该目的地通常是其他的移动电话,但也可以是服务器。最后,用户还需要有支持 SMS 的移动电话,并需要了解如何使用其特定型号的移动电话发送或阅读短消息。

为什么使用 SMS?

对于应用程序开发人员来说,将 Pocket PC Phone 连接至服务器的即时解决方案可能是通过基于 IP 的网络,例如 Internet。这种做法的优势包括:已经存在公用标准协议,而且通信工具也是现成的。

但是,在某些情况下,基于 IP 的网络并非是最有效的传输方式。主动甚至自动地进行连接需要占用宝贵的时间,而且在通话时不能进行连接。如果存在一种即使是在通话时也能即时连接至服务器的方法,那会是什么呢?既然 Pocket PC Phone 可以发送 SMS 消息,它也许是一种实现办法。

通常在四处走动的工作人员需要向业务流程应用程序通知重要事件时,即时连接非常有用。可能是通知服务器有新订单的推销员,也可能是刚刚送完货的卡车司机。

在服务器方面,有多种解决方案可用于接收 SMS 消息并将其转发给其他系统。例如 MobileSys, Inc.Smartserv Online, Inc. 的产品。在本文中,我们将进一步探讨 Pocket PC Phone 的 SMS 性能。

SMS API

在 Pocket PC Phone 上,您可以使用大量的 Microsoft® Windows® CE API 调用来访问 SMS 消息功能:

函数 说明
SmsOpen 打开用于发送和/或接收访问的 SMS 消息组件。
SmsSendMessage 发送 SMS 消息。
SmsGetMessageStatus 检索已发送消息的状态报告。
SmsReadMessage 阅读先前收到的 SMS 消息。
SmsGetMessageSize 确定缓冲区大小的上限。
SmsGetSMSC 读取默认的短消息服务中心 (SMSC) 地址。
SmsSetSMSC 设置默认的 SMSC。
SmsGetPhoneNumber 获取与 SMS 持有者相关联的设备的电话号码。
SmsGetTime 获取由 SMSC 时钟粗略估计的当前时间。
SmsGetBroadcastMsgRanges 获取移动电话可以侦听的广播消息的范围。
SmsSetMessageNotification 在 SMS 消息到达时启动应用程序。
SmsClearMessageNotification 取消对 SMS 通知的注册。
SmsSetBroadcastMsgRanges 设置移动电话可以侦听的广播消息的范围。
SmsClose 关闭现有的 SMS 消息句柄。

会话以调用 SmsOpen 开始,这将返回一个 SMS 句柄,此后,在调用 SMS API 函数时均需要此句柄。通过将此句柄传递给 SmsClose 来终止会话。本文将着重说明使用 .NET CF 中平台调用功能的 SmsSendMessage API,类似的技术也可用于访问其他 API。

SMS Anyplace 示例

该示例是使用 Microsoft Visual Studio® .NET、C# 和 .NET Compact Framework 创建的 Pocket PC Phone 的示例应用程序。它演示了如何使用 SMS API 发送 SMS 消息。该应用程序包含一个表单:

图 1:SMS Anyplace 示例

本示例的构思为:送货的卡车司机无需连接至任何基于 IP 的网络(例如 Internet)即可将通知即时发送给服务器上的业务流程应用程序。

请想象一下,在货物列表中作出选择,随后的屏幕将显示选中的货物标识以及当前的日期和时间。只需敲击“发送”按钮即可。

敲击“发送”按钮后,发送信息将被编译成 XML 消息字符串,您可以在屏幕下半部分看到该字符串。该信息可能不会显示在实际应用程序中,在此处显示只是为了便于说明。XML 格式可以确保接收服务器能够以标准方式访问信息。然后无需任何连接,甚至无须打断正在进行的语音对话,便可使用 SMS 将 XML 发送至服务器。

代码演练

让我们从“发送”按钮所表示的代码开始:

// 创建 XML 消息
MemoryStream ms = new MemoryStream();
XmlTextWriter xmlw = new XmlTextWriter(ms, System.Text.Encoding.UTF8);
xmlw.WriteStartDocument();
xmlw.WriteStartElement("Delivery");
xmlw.WriteElementString("PackageID", lblPackageID.Text);
xmlw.WriteElementString("Delivered", txtDelivered.Text);
xmlw.WriteEndElement();
xmlw.WriteEndDocument();
xmlw.Flush();
ms.Seek(0, SeekOrigin.Begin);
StreamReader sr = new StreamReader(ms);
txtMessage.Text = sr.ReadLine();
lblMessage.Text = "Message (" + txtMessage.Text.Length.ToString() + " 
  chars):";
this.Refresh();
xmlw.Close();
sr.Close();

// 发送消息
if (0 == SMSHelper.SendSMS("+15555550123", txtMessage.Text))
  MessageBox.Show("Message sent!", this.Text, MessageBoxButtons.OK,
    MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1);
else
  MessageBox.Show("Could not send message!", this.Text);

货物标识和发送时间将被转换成 XML。.NET CF 具有对 XML 的本地支持,并且具有若干可以使用的类。在本示例中,选择了 XMLTextWriter 类。使用简单的方法将 XML 写入到内存流。然后,该流被重置至开始,并由 StreamReader 读入到消息文本控制。消息标签也随消息长度而更新。创建消息后,消息将被发送(第二个参数)至 SMSHelper 类的静态 SendSMS 方法。第一个参数为国际格式的目标地址(电话号码)。

SMSHelper 类以多个声明开始:

private const string SMS_MSGTYPE_TEXT = "Microsoft Text SMS Protocol";
private const int SMS_MODE_SEND = 2;          // 以发送模式打开
private const int SMSDE_GSM = 1;              // 使用 GSM 编码
private const int SMSAT_INTERNATIONAL = 1;    // 国际格式
private const int PS_MESSAGE_OPTION_NONE = 0; // 没有消息选项
private const int PS_MESSAGE_CLASS0 = 0;      // 显示但不存储
private const int PS_MESSAGE_CLASS1 = 1;      // 存储并显示
private const int PSRO_NONE = 0;              // 没有替换项
private const int SMS_OPTION_DELIVERY_NONE = 0; // 没有发送选项

[DllImport("sms.dll")]
private static extern int SmsOpen(
  string ptsMessageProtocol,
  int dwMessageModes,
  ref IntPtr psmshHandle,
  int phMessageAvailableEvent);

[DllImport("sms.dll")]
private static extern int SmsSendMessage(
  IntPtr smshHandle,
  int psmsaSMSCAddress,
  IntPtr psmsaDestinationAddress,
  int pstValidityPeriod,
  IntPtr pbData,
  int dwDataSize,
  IntPtr pbProviderSpecificData,
  int dwProviderSpecificDataSize,
  int smsdeDataEncoding,
  int dwOptions,
  int psmsmidMessageID);

[DllImport("sms.dll")]
private static extern int SmsClose(IntPtr smshHandle);

首先,声明某些 SMS API 特定常数。这只是可用常数的一部分,要参考所有常数,请参阅 Windows CE API 文档。然后从 System.Runtime.InteropServices 命名空间中使用指向在其中执行函数的 DLL(动态链接库)的 DllImport 属性(在此示例中为 sms.dll)来声明每个 SMS API。

SendSMS 方法的核心内容如下所示:

// 获得句柄
if (0 != SmsOpen(SMS_MSGTYPE_TEXT, SMS_MODE_SEND, ref smsHandle, 0))
  returnValue = -1; // 无法打开

// 发送消息
if (0 != SmsSendMessage(smsHandle, 0, smsAddress, 0,
                 smsMessage, smsMessageTag.Length,
                 smsProviderData, smsProviderDataTag.Length,
                 SMSDE_GSM, SMS_OPTION_DELIVERY_NONE, 0))
  returnValue = -2;

// 释放句柄
if (0 != SmsClose(smsHandle))
  returnValue = -3; // 无法关闭

对 SmsOpen API 的调用将设置 SMS API 句柄 (smsHandle),并指定消息的类型以及发送模式。句柄及其他一些必需参数(smsAddress、smsMessage 和 smsProviderData)作为 IntPtr 进行声明。IntPtr 是一个结构,可以确保指针的长度(32 位或 64 位)与当前平台的本地指针大小相适应。最后,通过调用 SmsClose API 释放 SMS API 句柄。

小结

由于 Pocket PC Phone 是移动电话家族的重要成员,因此我们可以利用其功能,例如 SMS 消息。在设计连接应用程序时,不要忘了这个有趣的选项。

 

版权所有:UML软件工程组织