The Short Message Service (SMS) enables a Pocket PC Phone Edition device (or any other mobile device, for that matter) to instantly send and receive small messages through a Short Message Service Center (SMSC) to another mobile device on the network. Each message can be up to 160 alphanumeric characters long (140 bytes), and may be separated into multipart messages if necessary. SMS, with the introduction of the Enhanced Messaging Service (EMS) and the Multimedia Messaging Service (MMS), also has support for small binary nontext messages.
Once a message is sent to the SMSC for delivery, it is the messaging center's responsibility to forward the message to the appropriate destination mobile device. If the destination address of the device is not immediately available, the SMSC will usually store the message and continue to attempt to send it until it receives a notification that the target device has successfully received the message. SMS Messaging is based on the GSM 3.40 specification ("Technical Realization of the Short Messaging Service"), which can be downloaded from http://www.etsi.org.
One of the main differences between SMS Messaging and sending regular e-mail is that you can send SMS messages instantaneously, similar in fashion to a pager (however, SMS does not guarantee a delivery time or a confirmation) and without connecting to the Internet and a mail server. While the Pocket PC Phone Edition has seamlessly integrated SMS messaging functionality into the Inbox (see Figure 8.5), using the SMS APIs will enable you to send and receive SMS messages from within your own applications.
In order to create applications that can send and receive messages over SMS, you should include the sms.h header file, and link with the sms.lib library.
All of the SMS API functions use the SMS_ADDRESS structure to define the device address to which messages can be sent, and from which messages are received. The structure defines both the type of address and the actual phone number used in conjunction with the message. The SMS_ADDRESS structure is defined as follows:
typedef struct sms_address_tag{ SMS_ADDRESS_TYPE smsatAddressType; TCHAR ptsAddress[SMS_MAX_ADDRESS_LENGTH]; } SMS_ADDRESS, *LPSMS_ADDRESS;
The smsatAddressType field is used to define the type of address, and can be one of the values listed in Table 8.11.
Value | Address Type |
---|---|
SMSAT_UNKNOWN | Unknown phone number type |
SMSAT_INTERNATIONAL | An international phone number |
SMSAT_NATIONAL | A national phone number |
SMSAT_NETWORKSPECIFIC | A network-specific phone number |
SMSAT_SUBSCRIBER | A subscriber phone number |
SMSAT_ALPHANUMERIC | An alphanumeric phone number |
SMSAT_ABBREVIATED | An abbreviated phone number |
The ptsAddress field contains a null-terminated string that contains the address, which can be up to 255 characters long.
An SMS Service Center (or SMSC) is the server that performs the delivery of an SMS message. When a new message is sent from a device, the SMSC is responsible for the storing, forwarding, and relaying of the message to its appropriate destination address.
The process for delivering an SMS message from one device to another works as follows:
A device sends the new SMS message to the SMSC.
When the SMSC receives a new message, it sends an SMS request to the home location register (HLR). This is used to find out the routing information for the destination address.
The HLR sends a notification back to the SMSC with the status of the subscriber address. The notification indicates whether the address is inactive or active, and whether or not the address is roaming.
If the HLR response indicates that the subscriber is inactive, then the SMSC will hold the message for a period of time. Once the device address comes online on the network, the HLR sends a notification to the SMSC that the device is active.
If the HLR response is active (or the SMSC receives an active notification), the SMSC will then transfer the message to the target device address using the Short Message Delivery Point to Point (SMPP) protocol. In essence, the SMSC pages the device with the message contents.
Once the SMSC receives a notification from the destination device that the message has been delivered and verified, the SMSC marks the message as sent, and does not attempt to continue delivery.
NOTE:
Every device must be configured with an SMSC phone number (or address) before it can send or receive text messages.
To get the SMS address for the current SMSC for the device, you can use the SmsGetSMSC() function, which is defined as follows:
HRESULT SmsGetSMSC(SMS_ADDRESS* const psmsaSMSCAddress);
As you might expect, the only parameter that the function needs is a pointer to an SMS_ADDRESS structure that will be filled in with the current SMSC information.
For example, if you wanted to get the address information for the current SMSC, your call to SmsGetSMSC() would look like the following:
// Get the current SMS Service Center HRESULT hr = S_OK; SMS_ADDRESS smscAddress; TCHAR tchSMSC[1024] = TEXT("\0"); memset(&smscAddress, 0, sizeof(SMS_ADDRESS)); hr = SmsGetSMSC(&smscAddress); if(FAILED(hr)) { OutputDebugString(TEXT("Could not get service center address.")); return FALSE; } wsprintf(tchSMSC, TEXT("Current SMSC: %s"), smscAddress.ptsAddress); MessageBox(NULL, tchSMSC, TEXT("SMSC"), MB_OK|MB_ICONINFORMATION);
To change the SMSC, you can pass an SMS_ADDRESS structure containing the new service center address to the following function:
HRESULT SmsSetSMSC(const SMS_ADDRESS* const psmsaSMSCAddress);
The following code fragment shows how to set a new SMSC address:
// Set a new SMS Service Center SMS_ADDRESS smscNewAddress; memset(&smscNewAddress, 0, sizeof(SMS_ADDRESS)); smscNewAddress.smsatAddressType = SMSAT_INTERNATIONAL; wsprintf(smscNewAddress.ptsAddress,TEXT("18005555555")); hr = SmsSetSMSC(&smscNewAddress); if(FAILED(hr)) { OutputDebugString(TEXT("Could not set service center address.")); return FALSE; }
In order to either send or receive messages over the SMS service, you must first obtain a handle that you will use for subsequent messaging calls. Be aware that although you may have multiple handles open simultaneously for sending a message over a particular SMS protocol, you can open only one handle at a time for receiving messages (the Pocket PC Inbox, \Windows\tmail.exe, will typically have the receive handle already open).
To get an SMS handle, call the SmsOpen() function:
HRESULT SmsOpen(const LPCTSTR ptsMessageProtocol, const DWORD dwMessageModes, SMS_HANDLE* const psmshHandle, HANDLE* const phMessageAvailableEvent);
The first parameter, ptsMessageProtocol, is a null-terminated string that specifies which SMS protocol to use. There are several predefined SMS provider types, each with its own set of structures that make up different types of messages. The SMS protocols that Pocket PC Phone Edition supports are listed in Table 8.12.
The dwMessageModes parameter sets the transfer mode of the open handle. This can be set to SMS_MODE_RECEIVE if you want to receive messages, and/or SMS_MODE_SEND if you want to send messages. Note that certain SMS protocols can support only receive mode, and SmsOpen() will return an error if you try to open the protocol with the send option.
The pshmshHandle should point to the address of a variable that will receive an SMS_HANDLE when the function returns. The last parameter, phMessageAvailableEvent, is a pointer to a Windows event handle that will be signaled when a new message arrives. Although you can use this handle for wait event functions such as WaitForSingleObject(), you should not use any other event APIs on the handle. Calling functions such as SetEvent() and ResetEvent() will confuse the SMS engine, and unpredictable results can occur. In addition, you do not need to call CloseHandle() on the handle returned to pshmshHandle, as the SMS engine will close it when you call SmsClose() to shut down SMS.
SMS Protocol Type | Send/Receive | Description |
---|---|---|
SMS_MSGTYPE_TEXT | Both | Text SMS Protocol. Uses the TEXT_PROVIDER_SPECIFIC_DATA structure. |
SMS_MSGTYPE_NOTIFICATION | Receive only | Notification SMS Protocol. Uses the NOTIFICATION_PROVIDER_SPECIFIC_DATA structure. |
SMS_MSGTYPE_WDP | Both | WDP SMS Message. Uses the WDP_PROVIDER_SPECIFIC_DATA structure. |
SMS_MSGTYPE_WCMP | Both | WCMP SMS Message. Uses the WCMP_PROVIDER_SPECIFIC_DATA structure. |
SMS_MSGTYPE_STATUS | Receive only | Status message SMS Protocol. Uses the STATUS_PROVIDER_SPECIFIC_DATA structure. |
SMS_MSGTYPE_BROADCAST | Receive only | Broadcast Message SMS Protocol. Uses the BROADCAST_PROVIDER_SPECIFIC_DATA structure. |
SMS_MSGTYPE_RAW | Receive only | Raw SMS Protocol. Uses the RAW_PROVIDER_SPECIFIC_DATA structure. |
Using the SmsOpen() function to initiate a SMS messaging session is fairly straightforward, as shown by the following example:
SMS_HANDLE hSms = NULL; HANDLE hSmsEvent = NULL; HRESULT hr = S_OK; // Open up an SMS handle for the text provider // Remember, tmail.exe will typically have the receive // handle already open, and needs to be shut down before opening. hr = SmsOpen(SMS_MSGTYPE_TEXT, SMS_MODE_RECEIVE|SMS_MODE_SEND, &hSms, &hSmsEvent); if(FAILED(hr)) { OutputDebugString(TEXT("Could not open a handle to the SMS text message service.")); return FALSE; }
Several different SMS protocols can be used when sending a message from one device to another. Each protocol, also known as an SMS provider, has its own specific data structure, which makes up the message associated with it. The Pocket PC Phone Edition supports the short messaging protocols described in Table 8.12. Note that some of the protocols can be used only to receive messages, not to send them.
Complete details about SMS provider functionality and terminology can be found in the GSM 3.40 specification (found at http://www.etsi.org).
The text SMS provider is the most commonly used protocol for sending and receiving messages. As the name implies, the content of a text message is just that?a string up to 160 characters long. When working with text SMS messages, you use the TEXT_PROVIDER_SPECIFIC_DATA structure for any additional protocol information. The structure is defined as follows:
typedef struct text_provider_specific_data_tag { DWORD dwMessageOptions; PROVIDER_SPECIFIC_MESSAGE_CLASS psMessageClass; PROVIDER_SPECIFIC_REPLACE_OPTION psReplaceOption; } TEXT_PROVIDER_SPECIFIC_DATA;
The dwMessageOptions field specifies any options that can be used with the provider. It can be set to one or more of the following:
PS_MESSAGE_OPTION_NONE indicates that no options are being used.
PS_MESSAGE_OPTION_REPLYPATH indicates that the TP-Reply-Path bit should be set when sending a message (GSM 3.40 specification).
PS_MESSAGE_OPTION_STATUSREPORT indicates that the TP-Status-Report-Request bit should be set when sending a message (GSM 3.40 specification).
PS_MESSAGE_OPTION_DISCARD indicates that the TP-User-Data-Header bit should set to discard when sending a message (GSM 3.40 specification).
The psMessageClass field specifies how a message should interact with the SMS Service Center when it is received by its destination. It should be set to one of the following options:
PS_MESSAGE_CLASS0 indicates that the message should be displayed immediately and not stored in the SIM. This is typical for an alert message, and should always send an acknowledgment back to the SMSC.
PS_MESSAGE_CLASS1 indicates that the message should be stored in the SIM, and an acknowledgment should be sent to the SMSC once it has been received.
PS_MESSAGE_CLASS2 indicates that the message should be stored in the SIM, and an acknowledgment should be sent to the SMSC once it has been stored. If the message cannot be stored in the SIM and other memory is available, then an "unspecified protocol error" will be returned. If there is no memory to store the message, a "memory capacity exceeded" error will be sent to the SMSC.
PS_MESSAGE_CLASS3 indicates that an acknowledgment should be sent to the SMSC when the message has reached its target address.
The psReplaceOption field specifies how a message should be replaced for a previously received notification. It can be one of the following values:
PSRO_NONE indicates that no earlier notifications should be replaced, and that the message should be acknowledged.
PSRO_REPLACE_TYPE1 indicates that Replace Short Message Type 1 notifications should be replaced if the originating address and parameter values match.
PSRO_REPLACE_TYPE2 indicates that Replace Short Message Type 2 notifications should be replaced if the originating address and parameter values match.
PSRO_REPLACE_TYPE3 indicates that Replace Short Message Type 3 notifications should be replaced if the originating address and parameter values match.
PSRO_REPLACE_TYPE4 indicates that Replace Short Message Type 4 notifications should be replaced if the originating address and parameter values match.
PSRO_REPLACE_TYPE5 indicates that Replace Short Message Type 5 notifications should be replaced if the originating address and parameter values match.
PSRO_REPLACE_TYPE6 indicates that Replace Short Message Type 6 notifications should be replaced if the originating address and parameter values match.
PSRO_REPLACE_TYPE7 indicates that Replace Short Message Type 7 notifications should be replaced if the originating address and parameter values match.
PSRO_RETURN_CALL indicates that the originating device address can accept a return call.
PSRO_DEPERSONALIZATION indicates that the message contains code to depersonalize the device, and should not be displayed to the user.
The notification message protocol is used only when receiving messages that are message waiting alerts, such as a new voice mail.
Notification messages use the NOTIFICATION_PROVIDER_SPECIFIC_DATA structure, which is defined as follows:
typedef struct notification_provider_specific_data_tag { DWORD dwMessageOptions; PROVIDER_SPECIFIC_MESSAGE_CLASS psMessageClass; PROVIDER_SPECIFIC_REPLACE_OPTION psReplaceOption; NOTIFICATION_PROVIDER_SPECIFIC_MSG_WAITING_TYPE npsMsgWaitingType; int iNumberOfMessagesWaiting; NOTIFICATION_PROVIDER_SPECIFIC_INDICATOR_TYPE npsIndicatorType; } NOTIFICATION_PROVIDER_SPECIFIC_DATA;
The dwMessageOptions, psMessageClass, and psReplaceOptions fields specify any additional information about the message, and are identical to those found in the TEXT_PROVIDER_SPECIFIC_DATA structure.
The npsMsgWaitingType field specifies the type of notification that was received. Pocket PC Phone Edition supports the following notification types:
NOTIFICATIONPSMWT_NONE, if no message is waiting
NOTIFICATIONPSMWT_GENERIC, if the waiting message is generic
NOTIFICATIONPSMWT_VOICEMAIL, if the waiting message is a voice mail message
NOTIFICATIONPSMWT_FAX, if the waiting message is a fax
NOTIFICATIONPSMWT_EMAIL, if the waiting message is an e-mail message
NOTIFICATIONPSMWT_OTHER, if the waiting message is of an unknown type
The iNumberofMessagesWaiting field contains the number of messages that are waiting. The last field, npsIndicatorType, will specify the phone line that the notification is for, and can be set to NOTIFICATIONPSIT_NONE, NOTIFICATIONPSIT_LINE1, or NOTIFICATIONPSIT_LINE2.
The Wireless Datagram Protocol (WDP) is used to send and receive packets of binary data (similar to UDP) from one wireless device to another over the SMS service, and is part of the Wireless Application Protocol (WAP) stack. More information on WDP can be found in the WAP-202-WCMP specification, which is downloadable from http://www.etsi.org.
WDP messages use the WDP_PROVIDER_SPECIFIC_DATA structure, which is defined as follows:
typedef struct wdp_provider_specific_data_tag { WDP_PROVIDER_SPECIFIC_PORT_ADDRESSING wdppsPortAddressing; WORD wDestinationPort; WORD wOriginatorPort; } WDP_PROVIDER_SPECIFIC_DATA;
The first field, wdppsPortAddressing, specifies how the port numbers in the remaining fields should be addressed. This can be set to WDPPSA_8_BIT_PORT_NUMBERS for 8-bit addressing or WDPPSPA_16_BIT_PORT_NUMBERS for 16-bit values.
The wDestinationPort and wOriginatorPort fields specify which ports should be used for the delivery and reception of WDP packets, respectively.
The Wireless Control Message Protocol (WCMP) is used to send status messages and report errors that occur while using the Wireless Datagram Protocol. It is similar in functionality to TCP/IP's ICMP protocol for handling echo requests and responses. In addition, WCMP messages can be used for diagnostic and informational purposes.
WCMP messages use the WCMP_PROVIDER_SPECIFIC_DATA structure, which has the following prototype:
typedef struct wcmp_provider_specific_data_tag { WCMP_PROVIDER_SPECIFIC_MESSAGE_TYPE wcmppsMessageType; WORD wParam1; WORD wParam2; WORD wParam3; SMS_ADDRESS smsaAddress; } WCMP_PROVIDER_SPECIFIC_DATA;
The wcmppsMessageType field indicates the type of WCMP message that the structure represents. It can be any of the following:
WCMPPSMT_UNSUPPORTED, if there is no way to route the message to the destination address
WCMPPSMT_PORT_UNREACHABLE, if the destination port is unreachable
WCMPPSMT_MESSAGE_TOO_BIG, if the message is larger than the destination's buffer size
WCMPPSMT_ECHO_REQUEST, if the message is an echo request
WCMPPSMT_ECHO_REPLY, if the message is an echo response
The type of WCMP message that was sent or received (see Table 8.13) determines the value of the other fields in the structure.
Message Type | wParam1 | wParam2 | wParam3 | smsaAddress |
---|---|---|---|---|
UNSUPPORTED | None | None | None | None |
PORT_UNREACHABLE | Destination port | Source port | None | Destination address of original datagram |
MESSAGE_TOO_BIG | Destination port | Source port | Maximum size | Destination address of original datagram |
ECHO_REQUEST | Identifier | Sequence number | None | None |
ECHO_REPLY | Identifier | Sequence number | None | None |
If an SMS message (such as a text or notification message) requested status information by setting the PS_MESSAGE_OPTION_STATUSREPORT value, then a status SMS message will be sent to the device.
When a status message is received, it uses the STATUS_PROVIDER_SPECIFIC_DATA structure to report any information. The structure is defined as follows:
typedef struct status_provider_specific_data_tag { SMS_STATUS_INFORMATION smssiStatusInformation; } STATUS_PROVIDER_SPECIFIC_DATA;
The only information that the structure provides is an SMS_STATUS_INFORMATION structure. SMS_STATUS_INFORMATION will be filled in with the actual details of the status message:
typedef struct sms_status_information_tag { SMS_MESSAGE_ID smsmidMessageID; DWORD dwMessageStatus0; DWORD dwMessageStatus1; SMS_ADDRESS smsaRecipientAddress; SYSTEMTIME stServiceCenterTimeStamp; SYSTEMTIME stDischargeTime; } SMS_STATUS_INFORMATION, *LPSMS_STATUS_INFORMATION;
The smsmidMessageID field is the identifier of the message for which the status message was sent, and is the same as the identifier that you are returned from calling SmsSendMessage().
The dwMessageStatus0 and dwMessageStatus1 fields are two DWORD values that indicate the status message. Note that both fields must have the MESSAGE_STATUS_UNKNOWN flag set if the message status is truly undefined.
dwMessageStatus0 can be one of the values described in Table 8.14.
SMS Message Status | Delivered? | Description |
---|---|---|
MESSAGE_STATUS_UNKNOWN | N/A | Unknown status message |
MESSAGE_STATUS_0_RECEIVEDBYSME | Yes | Message was received by destination |
MESSAGE_STATUS_0_FORWARDEDTOSME | Yes | Message was forwarded to destination |
MESSAGE_STATUS_0_REPLACEDBYSC | Yes | Message was replaced by SMSC |
MESSAGE_STATUS_0_CONGESTION_TRYING | No | Network congestion; continuing to try |
MESSAGE_STATUS_0_SMEBUSY_TRYING | No | Busy signal; continuing to try |
MESSAGE_STATUS_0_SMENOTRESPONDING_TRYING | No | No response from destination; continuing to try |
MESSAGE_STATUS_0_SVCREJECTED_TRYING | No | Service rejected; continuing to try |
MESSAGE_STATUS_0_QUALITYUNAVAIL_TRYING | No | Quality unavailable; continuing to try |
MESSAGE_STATUS_0_SMEERROR_TRYING | No | Destination error |
MESSAGE_STATUS_0_CONGESTION | No | Temporary network congestion |
MESSAGE_STATUS_0_SMEBUSY | No | Temporary busy signal at destination |
MESSAGE_STATUS_0_SMENOTRESPONDING | No | Destination is not responding temporarily |
MESSAGE_STATUS_0_SVCREJECTED | No | Service has been rejected by destination temporarily |
MESSAGE_STATUS_0_QUALITYUNAVAIL_TEMP | No | Quality is temporarily unavailable |
MESSAGE_STATUS_0_SMEERROR | No | Temporary destination error |
MESSAGE_STATUS_0_REMOTEPROCERROR | No | Permanent failure |
MESSAGE_STATUS_0_INCOMPATIBLEDEST | No | Permanent failure due to destination incompatibility |
MESSAGE_STATUS_0_CONNECTIONREJECTED | No | Permanent failure due to rejection of the connection |
MESSAGE_STATUS_0_NOTOBTAINABLE | No | Permanent failure due to destination being unattainable |
MESSAGE_STATUS_0_NOINTERNETWORKING | No | Permanent failure due to internetworking being unavailable |
MESSAGE_STATUS_0_VPEXPIRED | No | Permanent failure due to validity period expiring |
MESSAGE_STATUS_0_DELETEDBYORIGSME | No | Permanent failure due to message being deleted by originator |
MESSAGE_STATUS_0_DELETEDBYSC | No | Permanent failure due to message being deleted by service center |
MESSAGE_STATUS_0_NOLONGEREXISTS | No | Permanent failure due to the message no longer existing |
MESSAGE_STATUS_0_QUALITYUNAVAIL | No | Permanent failure due to quality not existing |
MESSAGE_STATUS_0_RESERVED_COMPLETED | No | Message has been successfully reserved |
MESSAGE_STATUS_0_RESERVED_TRYING | No | Message has been reserved and is trying to be sent |
MESSAGE_STATUS_0_RESERVED_ERROR | No | Permanent failure due to a reserve error |
MESSAGE_STATUS_0_RESERVED_TMPERROR | No | Temporary error due to reserve on message |
MESSAGE_STATUS_0_SCSPECIFIC_COMPLETED | Yes | Message is destined for the SMSC and has been delivered |
MESSAGE_STATUS_0_SCSPECIFIC_TRYING | No | Continuing to try to send message to SMSC |
MESSAGE_STATUS_0_SCSPECIFIC_ERROR | No | Permanent failure due to SMSC error |
The smsaRecipientAddress field contains the destination address for the message.
The last two fields, stServiceCenterTimeStamp and stDischargeTime, are both SYSTEMTIME values that are set at different stages of the messaging delivery process. The stServiceCenterTimeStamp is set when SMSC received the message, and stDischargeTime may or may not be stamped, depending on the dwMessageStatus value.
A message received over the Cell Broadcast Service is generally sent to multiple recipients from a broadcast center over a geographical area from a service provider. A broadcast message may be up to 93 characters long, and may consist of multiple messages (or pages) that need to be concatenated to form the entire message. You can find more information about the Cell Broadcast Service in the GSM 3.41 specification, which is downloadable from http://www.etsi.org.
Broadcast SMS messages use the BROADCAST_PROVIDER_SPECIFIC_DATA structure, which has the following prototype:
typedef struct broadcast_provider_specific_data_tag { WORD wMessageID; WORD wMessageCode; BROADCAST_PROVIDER_SPECIFIC_GEOGRAPHICAL_SCOPE bpsgsGeographicalScope; WORD wUpdateNumber; } BROADCAST_PROVIDER_SPECIFIC_DATA;
The wMessageID field identifies the particular type of broadcast message, and is defined by the Cell Broadcast Service center that sent the message.
The wMessageCode field enables you to differentiate between multiple broadcast messages that contain the same wMessageID. For example, if the message identifier indicated that the message was a traffic report; different codes would be used for different traffic incidents.
The bpsgsGeographicalScope field indicates the location for which the message is valid, as well as the display mode. It will be one of the following values:
BPSGS_UNKNOWN indicates that the message type is unknown.
BPSGS_CELL_DISPLAY_IMMEDIATE indicates that the message is cell-wide and should be displayed immediately.
BPSGS_CELL indicates that the message is valid for the current cell.
BPSGS_PLMN indicates that the message code must be different for additional messages with the same message ID for the cell.
BPSGS_LOCATION_AREA indicates that the message is location-based.
Lastly, the wUpdateNumber field indicates a change of message content. For example, if a message has the same wMessageID, wMessageCode, and bpsgsGeopgraphicalScope, the update number can be used to determine old and new messages. This value is a four-bit number, and when it is eight or less higher (mod 16) than the last received message, it should be considered more recent.
The raw SMS message provider is used when an SMS message is received and none of the other provider types is appropriate for it.
If you receive a raw SMS message, you should use the RAW_PROVIDER_SPECIFIC_DATA structure:
typedef struct raw_provider_specific_data_tag { DWORD dwHeaderDataSize; BYTE pbHeaderData[SMS_DATAGRAM_SIZE]; } RAW_PROVIDER_SPECIFIC_DATA;
The dwHeaderDataSize field has the number of bytes that are returned in the pbHeaderData field, and pbHeaderData stores the raw user header from the incoming message.
You can send a message over a specific SMS provider by using the following function:
HRESULT SmsSendMessage(const SMS_HANDLE smshHandle, const SMS_ADDRESS* psmsaSMSCAddress, const SMS_ADDRESS* psmsaDestinationAddress, const SYSTEMTIME* pstValidityPeriod, const BYTE* pbData, const DWORD dwDataSize, const BYTE* pbProviderSpecificData, const DWORD dwProviderSpecificDataSize, const SMS_DATA_ENCODING smsdeDataEncoding, const DWORD dwOptions, SMS_MESSAGE_ID* psmsmidMessageID);
As you can see, numerous parameters are needed to send a message over SMS. The first one is the handle to an open SMS provider that was returned from a previous call to SmsOpen. The next parameter, psmsaSMSCAddress, points to the SMS Service Center through which you want to route your message. If you want to use the default SMSC address that was specified by the SmsSetSMSC() function, then you can set this to NULL, as it is an optional parameter. The psmsaDestinationAddress parameter should be set to the address of the device to which the message is sent.
The validity period of a message tells the SMSC how long a message is valid for. It begins when the SMSC receives the message. Once the period has expired, the message is automatically deleted. The pstValidityPeriod parameter is optional and can be set to NULL.
The next two parameters contain the SMS message body. The pbData parameter should point to the contents of the message, and the dwDataSize parameter should specify the size, in bytes, of the buffer pointed to by pbData. If there is no message, then you can set pbData to NULL, and dwDataSize to 0.
The pbProviderSpecificData and dwProviderSpecificDataSize parameters contain additional information for whichever SMS protocol you are using to send your message. For example, to send a text message (using SMS_MSGTYPE_TEXT), you would need to fill out a TEXT_PROVIDER_SPECIFIC_DATA structure. A pointer to this structure would then be passed in for the pbProviderSpecificData parameter, and its size (in bytes) would be passed in for the dwProviderSpecificDataSize parameter.
The smsdeDataEncoding parameter specifies the text encoding method for the message, and can be set to one of the following options:
SMSDE_OPTIMAL will use the best data encoding scheme that results in the least amount of space. This is the recommended option to use.
SMSDE_GSM specifies that standard GSM-7 encoding should be used.
SMSDE_UCS2 specifies that standard Unicode UCS2 encoding should be used.
The dwOptions flag can be set to either of the following:
SMS_OPTION_DELIVERY_NONE, for no special options
SMS_OPTION_DELIVERY_NO_RETRY, if you do not want the SMSC to retry sending a message if it is undeliverable
The last parameter, psmsmidMessageID, will receive a message ID once the function returns; it can be used for additional informational messages regarding the status of a sent message. This parameter is optional.
The following example shows how to send an SMS message:
// Send an SMS message through default SMSC SMS_ADDRESS smsDestination; SMS_MESSAGE_ID smsMsgId = 0; // Set the destination address for the message memset(&smsDestination, 0, sizeof(SMS_ADDRESS)); smsDestination.smsatAddressType = SMSAT_INTERNATIONAL; _tcsncpy(smsDestination.ptsAddress, TEXT("15555555555"), SMS_MAX_ADDRESS_LENGTH); // Create the message DWORD dwMessageLength = 0; TCHAR tchMessage[140] = TEXT("\0"); wsprintf(tchMessage, TEXT("This is a test text message\r\n")); dwMessageLength = lstrlen(tchMessage)*sizeof(TCHAR); // Configure the text provider TEXT_PROVIDER_SPECIFIC_DATA txtProviderData; DWORD dwProviderLength = 0; memset(&txtProviderData, 0, sizeof(TEXT_PROVIDER_SPECIFIC_DATA)); txtProviderData.dwMessageOptions = PS_MESSAGE_OPTION_NONE; txtProviderData.psMessageClass = PS_MESSAGE_CLASS0; txtProviderData.psReplaceOption = PSRO_NONE; dwProviderLength = sizeof(TEXT_PROVIDER_SPECIFIC_DATA); // Send the message hr = SmsSendMessage(hSms, NULL, &smsDestination, NULL, (BYTE *)tchMessage, dwMessageLength, (LPBYTE)&txtProviderData, dwProviderLength, SMSDE_OPTIMAL, SMS_OPTION_DELIVERY_NONE, &smsMsgId); if(FAILED(hr)) OutputDebugString(TEXT("Could not send SMS Text Message.")); else OutputDebugString(TEXT("Message Sent."));
If at any time you need to get the status information for a message that you have sent, you can call the SmsGetMessageStatus() function:
HRESULT SmsGetMessageStatus(const SMS_HANDLE smshHandle, SMS_MESSAGE_ID smsmidMessageID, SMS_STATUS_INFORMATION* const psmssiStatusInformation, const DWORD dwTimeout);
The first parameter is an SMS handle and is followed by the message ID for which you want to get status information. The message ID was returned to you when you originally called the SmsSendMessage() function. The psmssiStatusInformation parameter should point to an SMS_STATUS_INFORMATION structure that will be filled in with the message's status when the function returns (information on the SMS_STATUS_INFORMATION structure can be found in the section "Status SMS Messages"). The last parameter, dwTimeout, specifies the time to wait (in milliseconds) for the status message to be received.
The following code fragment shows how to get the status of an SMS message (notice how you change the txtProviderData.dwMessageOptions flag from the previous sample to indicate you want a status report):
txtProviderData.dwMessageOptions = PS_MESSAGE_OPTION_STATUSREPORT; txtProviderData.psMessageClass = PS_MESSAGE_CLASS0; txtProviderData.psReplaceOption = PSRO_NONE; dwProviderLength = sizeof(TEXT_PROVIDER_SPECIFIC_DATA); // Send the message hr = SmsSendMessage(hSms, NULL, &smsDestination, NULL, (BYTE *)tchMessage, dwMessageLength, (LPBYTE)&txtProviderData, dwProviderLength, SMSDE_OPTIMAL, SMS_OPTION_DELIVERY_NONE, &smsMsgId); if(FAILED(hr)) OutputDebugString(TEXT("Could not send SMS Text Message.")); else OutputDebugString(TEXT("Message Sent.")); // Get message status (wait 2 seconds) SMS_STATUS_INFORMATION smsStatus; memset(&smsStatus, 0, sizeof(SMS_STATUS_INFORMATION)); hr = SmsGetMessageStatus(hSms, smsMsgId, &smsStatus, 2000); if(FAILED(hr)) OutputDebugString(TEXT("Could not get SMS message status")); else { if(smsStatus.dwMessageStatus0 == MESSAGE_STATUS_0_RECEIVEDBYSME) OutputDebugString(TEXT("Message has been received")); }
By using the event handle that was returned when you called SmsOpen(), you can also wait for a new message to be received by your device. Once that event handle has been signaled, reading the contents of a message is a two-step process.
First, you need to find out how large the message is by calling the following function:
HRESULT SmsGetMessageSize(const SMS_HANDLE smshHandle, DWORD* const pdwDataSize);
The function takes only two parameters: an open SMS handle and a pointer to a DWORD value that will receive the size, in bytes, of the message.
Once you have the size of the incoming message, you can retrieve the message contents by using the SmsReadMessage() function. It is defined as follows:
HRESULT SmsReadMessage(const SMS_HANDLE smshHandle, SMS_ADDRESS* const psmsaSMSCAddress, SMS_ADDRESS* const psmsaSourceAddress, SYSTEMTIME* const pstReceiveTime, BYTE* const pbBuffer, DWORD dwBufferSize, BYTE* const pbProviderSpecificBuffer, DWORD dwProviderSpecificDataBuffer, DWORD* pdwBytesRead);
The first parameter, smshHandle, is an open SMS handle for the provider from which you are going to receive a message.
The next two parameters, psmsaSMSCAddress and psmsaSourceAddress, point to variables that will be filled in with the SMSC that delivered the message, as well as the address from which it originated. Both of these are optional and can be set to NULL.
The pstReceiveTime parameter points to a SYSTEMTIME structure that will be filled in with the time the message was received. The pstReceiveTime parameter is also optional and can be set to NULL.
The pbBuffer and dwBufferSize parameters are used to store the message contents. The buffer that pbBuffer points to should be at least the size that was returned from the call to SmsGetMessageSize(). The dwBufferSize parameter should be set to the size of pbBuffer.
The pbProviderSpecificBuffer and dwProviderSpecificDataBuffer parameters contain a provider-specific data structure and its size. For example, if the received message was a WDP SMS message (using the SMS_MSGTYPE_WDP protocol type), the pbProviderSpecificBuffer would then point to a WDP_PROVIDER_SPECIFIC_DATA structure.
The pdwBytesRead parameter should point to a DWORD variable that will be filled in with the actual number of bytes that were put into the pbBuffer buffer when the function returns.
The following code example illustrates a function that is waiting for a new SMS text message to arrive. Once the SMS event is signaled, you get the size of the incoming message, allocate a buffer that is large enough for it, and finally retrieve the message:
// Wait for an incoming message DWORD dwReturn = 0; dwReturn = WaitForSingleObject(hSmsEvent, INFINITE); // SMS event has become signaled if(dwReturn == WAIT_ABANDONED || dwReturn == WAIT_TIMEOUT) { OutputDebugString(TEXT("No longer waiting for a message")); SmsClose(hSms); return FALSE; } // Receive a message. First, get the size DWORD dwMessageSize = 0; hr = SmsGetMessageSize(hSms, &dwMessageSize); if(FAILED(hr)) { OutputDebugString(TEXT("Could not get message size")); SmsClose(hSms); return FALSE; } // Set up to receive the message SMS_ADDRESS smscAddress; SMS_ADDRESS inAddress; SYSTEMTIME rcvTime; TEXT_PROVIDER_SPECIFIC_DATA txtProviderData; DWORD dwProviderLength = 0; dwProviderLength = sizeof(TEXT_PROVIDER_SPECIFIC_DATA); memset(&txtProviderData, 0, dwProviderLength); memset(&smscAddress, 0, sizeof(SMS_ADDRESS)); memset(&inAddress, 0, sizeof(SMS_ADDRESS)); memset(&rcvTime, 0, sizeof(SYSTEMTIME)); // Create a buffer to get the message TCHAR *tchMsgBuffer = NULL; tchMsgBuffer = (TCHAR *)LocalAlloc(LPTR, dwMessageSize*sizeof(TCHAR)); if(!tchMsgBuffer) { OutputDebugString(TEXT("Could not allocate a buffer for the message")); SmsClose(hSms); return FALSE; } // Read the message DWORD dwBytesRead = 0; hr = SmsReadMessage(hSms, &smscAddress, &inAddress, &rcvTime, (LPBYTE)tchMsgBuffer, dwMessageSize, (LPBYTE)&txtProviderData, dwProviderLength, &dwBytesRead); if(FAILED(hr)) { OutputDebugString(TEXT("There was an error reading the message")); LocalFree(tchMsgBuffer); SmsClose(hSms); return FALSE; } // Display the message to the user TCHAR tchDisplayMsg[1024] = TEXT("\0"); wsprintf(tchDisplayMsg, TEXT("New Message!\r\nFrom:%s\r\n\r\n%s"), inAddress.ptsAddress, tchMsgBuffer); MessageBox(NULL, tchDisplayMsg, TEXT("New SMS Message"), MB_OK|MB_ICONEXCLAMATION); LocalFree(tchMsgBuffer);
When you have finished working with SMS, you need to close the handle to the SMS provider to ensure that the messaging component is shut down properly. You can do this by using the following function call:
HRESULT SmsClose(const SMS_HANDLE smshHandle);
The only parameter that the function needs is a handle to an open SMS session. When SmsClose() is called, both the handle specified by the smshHandle parameter and the Windows event handle that was returned from your call to SmsOpen() are closed.
Using the SmsClose() function is straightforward:
// Close the SMS handle if(hSms) { SmsClose(hSms); hSms = NULL; }
The SMS Messaging protocol specifies that GRPS devices should have the ability to receive messages from a Cell Broadcast Service. These messages are typically sent from a broadcast center and are transmitted over a geographic area, and may provide regional information such as traffic reports.
A Pocket PC Phone Edition device fully supports receiving broadcast messages, and provides you with functions to both read and set the accepted range of location IDs for incoming broadcast messages. These two functions are as follows:
HRESULT SmsSetBroadcastMsgRanges( const SMS_BROADCAST_RANGES* const psmsbrBroadcastRanges);
and
HRESULT SmsGetBroadcastMsgRanges( SMS_BROADCAST_RANGES* const psmsbrBroadcastRanges);
Both SmsSetBroadcastMsgRanges() and SmsGetBroadcastMsgRanges() take one parameter: a pointer to an SMS_BROADCAST_RANGES structure, which defines the specifics about the accepted broadcast ranges. The structure is defined as follows:
typedef struct sms_broadcast_ranges_tag { DWORD cbSize; DWORD dwParams; DWORD dwNumRanges; DWORD dwBroadcastMsgLangs; BOOL bAccept; SMS_RANGE smsrBroadcastRanges[]; } SMS_BROADCAST_RANGES, *LPSMS_BROADCAST_RANGES;
The first field, cbSize, needs to be set to the size of the SMS_BROADCAST_RANGES structure before calling either SmsSetBroadcastMsgRanges() or SmsGetBroadcastMsgRanges().
The dwParams field indicates which of the remaining fields of the structure contain valid data, and can be one or more of the following options:
SMS_PARAM_SBR_BROADCASTMSGIDS indicates that the dwNumRanges and smsrBroadcastRanges fields are valid.
SMS_PARAM_SBR_BROADCASTMSGLANGS indicates that the dwBroadcastMsgLangs field is valid.
SMS_PARAM_SBR_ACCEPTIDS indicates that the bAccept field is valid.
The dwNumRanges field specifies how many items are in the smsrBroadcastRanges array. This array is comprised of several SMS_ARRAY structures, each specifying a minimum and maximum number of mobile IDs to listen to. The SMS_ARRAY structure is defined as follows:
typedef struct sms_range_tag { DWORD dwMinimum; DWORD dwMaximum; } SMS_RANGE, *LPSMS_RANGE;
The dwBroadcastMsgLangs field specifies which languages are supported by the Pocket PC device, and can be one or more of the languages listed in Table 8.15.
Broadcast Languages |
---|
SMS_DCSLANG_UNKNOWN SMS_DCSLANG_GERMAN SMS_DCSLANG_ENGLISH SMS_DCSLANG_ITALIAN SMS_DCSLANG_FRENCH SMS_DCSLANG_SPANISH SMS_DCSLANG_DUTCH SMS_DCSLANG_SWEDISH SMS_DCSLANG_DANISH SMS_DCSLANG_NORWEGIAN SMS_DCSLANG_GREEK SMS_DCSLANG_TURKISH SMS_DCSLANG_HUNGARIAN SMS_DCSLANG_POLISH SMS_DCSLANG_CZECH SMS_DCSLANG_PORTUGUESE SMS_DCSLANG_FINNISH SMS_DCSLANG_ALL |
The last field, bAccept, is used to indicate whether or not the message IDs defined by dwBroadcastMsgLangs and smsrBroadcastRanges are accepted or not. If bAccept is set to TRUE, then the device will accept message from the specified identifiers.
Pocket PC Phone Edition provides your application with the capability to receive SMS messages even if it is not currently running. The function SmsSetMessageNotification() can be used to configure an application that should be started when a message from a specific SMS protocol arrives. The function is defined as follows:
HRESULT SmsSetMessageNotification( const SMSREGISTRATIONDATA* psmsrd);
The only parameter that SmsSetMessageNotification() uses is a pointer to an SMSREGISTRATIONDATA structure, which looks like the following:
typedef struct smsregistrationdata_tag { DWORD cbSize; TCHAR tszAppName[SMS_MAX_APPNAME_LENGTH]; TCHAR tszParams[SMS_MAX_PARAMS_LENGTH]; TCHAR tszProtocolName[SMS_MAX_PROTOCOLNAME_LENGTH]; } SMSREGISTRATIONDATA, *LPSMSREGISTRATIONDATA;
The first field should be set to the size of the SMSREGISTRATIONDATA structure. The second field, tszAppName, specifies the application name and path that should be run when a new message arrives. When the application is run, the command-line parameters that are set in the tszParams field will be passed to it.
The last field, tszProtocolName, indicates the message provider (protocol) for which the notification is being set, and can be one of the following:
SMS_MSGTYPE_TEXT, for messages using the Microsoft Text SMS Protocol
SMS_MSGTYPE_NOTIFICATION, for messages using the Microsoft Notification SMS Protocol
SMS_MSGTYPE_WDP, for messages using the Microsoft WDP SMS Protocol
SMS_MSGTYPE_WCMP, for messages using the Microsoft WCMP SMS Protocol
SMS_MSGTYPE_STATUS, for messages using the Microsoft Status Message Protocol
SMS_MSGTYPE_RAW, for messages using the Microsoft Raw SMS Protocol
Be aware that when you set a new notification for a specific SMS protocol, you will overwrite any existing notifications for the provider.
To cancel notification messages from an SMS provider, simply call the following:
HRESULT SmsClearMessageNotification( const LPCTSTR tszProtocolName);
The only parameter the function takes is the name of the protocol for which you want to remove notification messages. This should be the same as the provider name that was passed into the SMS