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. Figure 8.5. SMS and the Pocket PC Inbox
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. SMS AddressesAll 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.
The ptsAddress field contains a null-terminated string that contains the address, which can be up to 255 characters long. SMS Service Center (SMSC)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:
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; } Opening the SMS ServiceIn 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.
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; } SMS Message ProtocolsSeveral 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). Text SMS MessagesThe 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:
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:
The psReplaceOption field specifies how a message should be replaced for a previously received notification. It can be one of the following values:
Notification SMS MessagesThe 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:
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. WDP SMS MessagesThe 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. WCMP SMS MessagesThe 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:
The type of WCMP message that was sent or received (see Table 8.13) determines the value of the other fields in the structure.
Status SMS MessagesIf 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.
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. Broadcast SMS MessagesA 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:
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. Raw SMS MessagesThe 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. Sending a MessageYou 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:
The dwOptions flag can be set to either of the following:
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")); } Reading a MessageBy 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); Closing the SMS HandleWhen 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; } Broadcast Message RangeThe 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:
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.
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. SMS NotificationsPocket 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:
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 SMSREGISTRATIONDATA structure's tszProtocolName field when setting up the notification. Getting Additional Information from the SIMThe SMS service on the Pocket PC Phone Edition supports two additional functions that enable you to get more information about the device from the SMS service. To get the SMS address of the device you are using, you can use the following function: HRESULT SmsGetPhoneNumber(SMS_ADDRESS* const psmsaAddress); The only parameter that SmsGetPhoneNumber() needs is a pointer to an SMS_ADDRESS structure, which will be filled in with the device's phone number when the function returns, as shown in the following example: // Device phone number SMS_ADDRESS mySmsAddress; memset(&mySmsAddress, 0, sizeof(SMS_ADDRESS)); hr = SmsGetPhoneNumber(&mySmsAddress); if(FAILED(hr)) { OutputDebugString(TEXT("Could not open a handle to the SMS text message service.")); return FALSE; } MessageBox(NULL, mySmsAddress.ptsAddress, TEXT("Device Phone Number"), MB_ICONINFORMATION|MB_OK); To get an estimate of the current time, you can call the SmsGetTime() function. This function will try to figure out the time by using the last status report message that was sent to the device from the SMS Service Center. The function is defined as follows: HRESULT SmsGetTime(SYSTEMTIME* const ptsCurrentTime, DWORD* const pdwErrorMargin); The first parameter, ptsCurrentTime, is a pointer to a SYSTEMTIME structure that will be filled in with the estimate of the current time. The time that is returned to you will be in Universal Coordinated Time (UTC) format and should be converted to a local format before being used. The pdwErrorMargin parameter is the maximum amount of time, in seconds, that the returned time could be off by. If SMS cannot determine the error margin, this will be set to 0xFFFFFFFF. The following example shows how you can get the current estimated time from SMS and properly convert it: // Estimate current time from SMS SYSTEMTIME estTime; DWORD dwErrorMargin = 0; memset(&estTime, 0, sizeof(SYSTEMTIME)); hr = SmsGetTime(&estTime, &dwErrorMargin); if(FAILED(hr)) { OutputDebugString(TEXT("Could not get estimated time from SMSC.")); return FALSE; } // Since the time information is a UTC value, we will need // to convert it. FILETIME estFileTime; FILETIME localFileTime; SystemTimeToFileTime(&estTime, &estFileTime); FileTimeToLocalFileTime(&estFileTime, &localFileTime); // Convert it back SYSTEMTIME estLocalSysTime; memset(&estLocalSysTime, 0, sizeof(SYSTEMTIME)); FileTimeToSystemTime(&localFileTime, &estLocalSysTime); // Use estLocalSysTime to set clock, etc. ... |