Does someone know how SMS messages are stored on a PPC device?
Thanks in advance
Otto
SMS Messaging
The Short Message Service, or SMS, is a popular way, as the name implies, to exchange short text messages between cellular phones. By default, the InBox application on both the Smartphone and the Pocket PC Phone edition reads SMS messages. On the Smartphone, the Pocket InBox is also responsible for composing and sending SMS messages. On the Pocket PC, SMS messages can be composed and sent from a menu on the phone application. In addition to these applications, both systems expose a set of functions that allows third-party applications to send and receive SMS messages.
The process of sending and receiving SMS messages involves getting an SMS handle for sending and another for receiving. The message is composed, and the address is defined as the phone number of the receiving phone. Instead of sending the message directly to the phone, however, the message is sent to the SMS Service Center, which forwards the message on to the destination phone.
Receiving a message involves blocking on an event that is signaled when a message has been received. The message can then be read with a call to the SMS system. Because of the blocking nature of the reading process, this task is usually accomplished with a secondary thread.
The SMS system doesn’t provide any way of saving the read messages. Instead, the application that is responsible for receiving the messages is responsible for saving the messages in a database if the user wants them saved. On the Pocket PC and Smartphone, Pocket Inbox saves the messages in the e-mail database.
Accessing the SMS System
The task of sending or receiving messages starts with accessing the SMS subsystem using SmsOpen, prototyped as:
HRESULT SmsOpen (const LPCTSTR ptsMessageProtocol,
const DWORD dwMessageModes, SMS_HANDLE* const psmshHandle,
HANDLE* const phMessageAvailableEvent);
The first parameter is a string that describes the type of message that the application is interested in sending or receiving. For sending and receiving basic text messages, the SMS_MSGTYPE_TEXT protocol should be used. The include file Sms.h defines a number of other protocols that can be used for broadcast, status, and a couple of control protocols. The dwMessageModes parameter should be set for either SMS_MODE_RECEIVE or SMS_MODE_SEND depending on if the open is to send or receive messages. The psmshHandle parameter points to an SMS_HANDLE value that will receive the SMS handle if the function is successful.
The final parameter, phMessageAvailableEvent, points to an event handle but is used only when opening to receive messages. When asking for a handle to send SMS messages, the parameter should be NULL. When asking for a handle to receive messages, this parameter must point to a standard Windows CE event handle that was previously created by the application. This event handle should be an auto-reset event, not initially signaled. The event will be signaled when a message has been received by the system. The application should not set the event or close the event handle. The event will be closed when the application calls SmsClose.
The return value of SmsOpen will be ERROR_SUCCESS if the call was successful. Otherwise, an error code will be returned.
Sending a Message
Sending a message is accomplished by calling the rather involved SmsSendMessage function, prototyped as:
HRESULT SmsSendMessage (const SMS_HANDLE smshHandle,
const SMS_ADDRESS * const psmsaSMSCAddress,
const SMS_ADDRESS * const psmsaDestinationAddress,
const SYSTEMTIME * const pstValidityPeriod,
const BYTE * const pbData, const DWORD dwDataSize,
const BYTE * const pbProviderSpecificData,
const DWORD dwProviderSpecificDataSize,
const SMS_DATA_ENCODING smsdeDataEncoding,
const DWORD dwOptions,
SMS_MESSAGE_ID * psmsmidMessageID);
The first parameter is an SMS handle that was opened for sending a message. The psmsaSMSCAddress parameter points to a SMS_ADDRESS structure that contains the phone number of the SMS service center. In most cases, this parameter can be NULL to indicate that the system should use the default SMS center address. The psmsaDestinationAddress parameter points to another SMS_ADDRESS structure that contains the destination address of the message. I’ll discuss the format of the SMS_ADDRESS structure in a moment.
The fourth parameter of SmsSendMessage, pstValidityPeriod, sets the length of time the message can remain undelivered at the server before it’s deleted. Contrary to the parameter’s SYSTEMTIME type, the format of this field is not a SYSTEMTIME but a data type defined by the SMS specification. This parameter can be NULL.
The next two fields are the pointer to the message text and the length in bytes of the text. The maximum length of a single message is 140 bytes or 160 7-bit characters. The format of the data is defined in the smsdeDataEncoding parameter discussed later.
The pbProviderSpecificData parameter points to optional provider-specific data. The provider-specific data is a structure specialized to the message format being used when sending the data. The format of the TEXT_PROVIDER_SPECIFIC_DATA structure, used when sending standard text messages, is described later. The dwProviderSpecificDataSize parameter should be set to the size of the structure pointed to by pbProviderSpecificData. See the discussion of the TEXT_PROVIDER_SPECIFIC_DATA structure for special handling of this parameter.
The smsdeDataEncoding parameter describes how the message data is encoded. For most messages, the parameter should be set to SMSDE_OPTIMAL to tell the SMS code to define the optimal encoding. The other values are SMSDE_GSM to use the 7-bit GSM encoding and SMSDE_UCS2 to specify a Unicode UCS2 encoding. The dwOptions parameter specifies how the service center will handle retries. If the parameter is set to SMS_OPTION_DELIVERY_NONE, the service center will retry sending the message. If SMS_OPTION_DELIVERY_NO_RETRY is specified, the message won’t retry the delivery.
The final parameter points to a DWORD that will receive a message ID value. The message ID can be used to get status of the message using the function SmsGetMessageStatus. This parameter can be NULL if the message ID isn’t needed.
The return value of SmsSendMessage is a standard HRESULT value, with S_OK (0) indicating success. The function can take a short but noticeable amount of time to complete so it’s best to display a wait cursor or use some other method to prevent the user from thinking the system is momentarily locked up.
The structures used by SmsSendMessage included the SMS_ADDRESS and TEXT_PROVIDER_SPECIFIC_DATA structures. The SMS_ADDRESS structure is defined as:
typedef struct sms_address_tag {
SMS_ADDRESS_TYPE smsatAddressType;
TCHAR ptsAddress[SMS_MAX_ADDRESS_LENGTH];
} SMS_ADDRESS, *LPSMS_ADDRESS;
The first field of the structure is the address type. For most uses, this field can be set to SMSAT_INTERNATIONAL. The second field is the address, for SMSAT_INTERNATIONAL, the address is in the form of a phone number complete with country code and area code as in +12225551212.
When sending standard text messages, the provider specific data structure used is TEXT_PROVIDER_SPECIFIC_DATA, shown here. Other structures can be used when sending other types of message, but for brevity I’ll only describe this structure.
typedef struct text_provider_specific_data_tag {
DWORD dwMessageOptions;
PROVIDER_SPECIFIC_MESSAGE_CLASS psMessageClass;
PROVIDER_SPECIFIC_REPLACE_OPTION psReplaceOption;
DWORD dwHeaderDataSize;
BYTE pbHeaderData[SMS_DATAGRAM_SIZE];
BOOL fMessageContainsEMSHeaders;
DWORD dwProtocolID;
} TEXT_PROVIDER_SPECIFIC_DATA;
This structure definition is somewhat misleading because only the first three fields are used when sending a standard text message. The additional fields are only used for message concatenation. The first field is the message options field that can optionally request that various message bits be set, such as reply path, discard, or status. The message class value ranges from PS_MESSAGE_CLASS0 through PS_MESSAGE_CLASS3 and PS_MESSAGE_CLASSUNSPECIFIED. The message class indicates how the service center handles the message. For text messages, PS_MESSAGE_CLASS0 is used. The psReplaceOption field contains PSRO_NONE for standard messages. The field can be set to PSRO_REPLACE_TYPEn, where n is a value from 1 through 7. If a replace type field is set, the message will replace a message at the destination with the same parameters and the same replace type value.
A function of this complexity deserves an example. The following code calls SmsOpen, fills in the proper structures, and then sends the message. The SMS handle is then closed with SmsClose.
SMS_HANDLE smshHandle;
SMS_ADDRESS smsaDestination;
TEXT_PROVIDER_SPECIFIC_DATA tpsd;
SMS_MESSAGE_ID smsmidMessageID = 0;
// try to open an SMS Handle
HRESULT hr = SmsOpen(SMS_MSGTYPE_TEXT, SMS_MODE_SEND, &smshHandle, NULL);
if (hr != ERROR_SUCCESS) {
printf ("SmsOpen fail %x %d", hr, GetLastError());
return 0;
}
// Create the destination address
memset (&smsaDestination, 0, sizeof (smsaDestination));
smsaDestination.smsatAddressType = SMSAT_INTERNATIONAL;
lstrcpy(smsaDestination.ptsAddress, TEXT("+18005551212"));
// Set up provider specific data
tpsd.dwMessageOptions = PS_MESSAGE_OPTION_NONE;
tpsd.psMessageClass = PS_MESSAGE_CLASS0;
tpsd.psReplaceOption = PSRO_NONE;
char szMessage[] = "Watson! Come here, I need you!";
// Send the message, indicating success or failure
hr = SmsSendMessage (smshHandle, NULL, &smsaDestination, NULL,
(PBYTE) szMessage, strlen(szMessage)+1,
(PBYTE) &tpsd, 12, SMSDE_OPTIMAL,
SMS_OPTION_DELIVERY_NONE, &smsmidMessageID);
if (hr == ERROR_SUCCESS)
printf ("Message sent");
SmsClose (smshHandle);
SmsOpen is called with SMS_MODE_SEND to open a handle for sending. Notice that the final parameter is NULL because there isn’t a need for the read event handle. The destination address is then filled in with a 10-digit phone number. The provider specific data is constructed with no options and message class 0. The message is then constructed, and the call to SmsSendMessage is made. Notice that the size of the provider specific data is not set to sizeof (TEXT_PROVIDER_SPECIFIC_DATA) because this is a simple, standalone message and the last few fields of TEXT_PROVIDER_SPECIFIC_DATA aren’t used. After the message is sent, the handle is closed with SmsClose, which has the handle as the single parameter.
Receiving a Message
Receiving a message is accomplished with the function SmsReadMessage. When opening a SMS handle for reading, an event handle must be passed as the last parameter. The SMS system will use this handle to signal the application when a message has been received.
When the event is signaled, the application can get an idea of the size of the incoming message by calling SmsGetMessageSize, prototyped as:
HRESULT SmsGetMessageSize (const SMS_HANDLE smshHandle,
DWORD * const pdwDataSize);
The two parameters are the SMS handle that was opened previously and a pointer to a DWORD that will receive the message size. The size received is not necessarily the exact size of the message. Instead, it’s an upper bound that can be used to allocate the buffer that receives the message.
With a buffer allocated, the message can be read using SmsReadMessage, prototyped as:
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 is a SMS handle that was opened in receive mode. The second parameter is an optional SMS_ADDRESS structure that can receive the number of the SMS service center that sent the message. If the message center address is of no interest, this parameter can be set to NULL. The third parameter points to an SMS_ADDRESS structure that will be filled in with the address of the message received. The pstReceiveTime parameter points to a SYSTEMTIME structure that will receive the UTC-based time of the message. This parameter can be NULL if the time isn’t required. The next two parameters, pbBuffer and dwBufferSize, are the pointer to the buffer to receive the data and the size of the buffer. The pbProviderSpecificBuffer parameter points to a buffer that will receive the provider-specific data that accompanies the message, and dwProviderSpecificDataBuffer contains the size of the buffer. The final parameter points to a DWORD that will receive the size of the message received.
SmsReadMessage will fail if there is no message to be read, so the application must block on the event used when SmsOpen was called and only read the message when the event is signaled. The blocking nature of the process means that SmsReadMessage, or at least the wait on the event object, should be done in a secondary thread. The following code is a separate thread that creates an event, opens an SMS handle, blocks on the event, and if signaled reads the message.
DWORD ThreadRead (PVOID pArg) {
SMS_ADDRESS smsaDestination;
TEXT_PROVIDER_SPECIFIC_DATA tpsd;
SMS_HANDLE smshHandle;
HANDLE hRead = CreateEvent (NULL, FALSE, FALSE, NULL);
// Open an SMS Handle
HRESULT hr = SmsOpen (SMS_MSGTYPE_TEXT, SMS_MODE_RECEIVE,
&smshHandle, &hRead);
if (hr != ERROR_SUCCESS) {
printf ("SmsOpen fail %x %d\r\n", hr, GetLastError());
return 0;
}
// Wait for message to come in.
int rc = WaitForSingleObject (hRead, 30000);
if (rc != WAIT_OBJECT_0) {
printf ("WaitForSingleObject %d\r\n", rc);
SmsClose (smshHandle);
return 0;
}
memset (&smsaDestination, 0, sizeof (smsaDestination));
DWORD dwSize, dwRead = 0;
hr = SmsGetMessageSize (smshHandle, &dwSize);
if (hr != ERROR_SUCCESS) {
dwSize = 1024;
return 0;
}
char *pMessage = (char *)malloc (dwSize+1);
memset (&tpsd, 0, sizeof (tpsd));
hr = SmsReadMessage (smshHandle, NULL, &smsaDestination, NULL,
(PBYTE)pMessage, dwSize,
(PBYTE)&tpsd, sizeof(TEXT_PROVIDER_SPECIFIC_DATA),
&dwRead);
if (hr == ERROR_SUCCESS) {
printf ("Dst Address >%s<\r\n", smsaDestination.ptsAddress);
printf ("Msg: >%s<", pMessage);
} else
printf ("Failed %x LastErr:%d\r\n", hr, GetLastError());
free (pMessage);
SmsClose (smshHandle);
printf ("ThreadExit");
return 0;
}
This code could be better written to check the length of the received data and to insure that the message is zero terminated.
Configuring the SMS System
There are a number of functions in the SMS API that are provided for querying the state and managing the SMS system. The SMS phone number for the device can be queried with a call to SmsGetPhoneNumber, defined as.
HRESULT SmsGetPhoneNumber (SMS_ADDRESS* const psmsaAddress);
The only parameter is an SMS_ADDRESS structure that is filled in with the phone number of the device.
The status of a sent message can be queried with SmsQueryMessageStatus, prototyped as:
HRESULT SmsGetMessageStatus (const SMS_HANDLE smshHandle,
SMS_MESSAGE_ID smsmidMessageID,
SMS_STATUS_INFORMATION * const psmssiStatusInformation,
const DWORD dwTimeout);
The first two parameters are the SMS handle and the message ID that was returned by SmsSendMessage. The dwTimeout value is the time, in milliseconds, that the function should wait for status information from the SMS service center. If the function returns successfully, the SMS_STATUS_INFORMATION structure is filled with status information about the message. The structure is defined as:
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 first field is the ID of the message. The next two fields contain status flags that define the state of the message. There are two fields because there are more than 32 status flags defined. The SMS_ADDRESS field is filled with the destination address of the message. The stServiceCenterTimeStamp field contains the time the message was received by the service center. The stDischargeTime field is a time that depends on the status flags returned in the two dwMessageStatus fields.
The SMS service center number can be queried and set with the functions SmsGetSMSC and SmsSetSMSC, prototyped as:
HRESULT SmsGetSMSC (SMS_ADDRESS* const psmsaSMSCAddress);
and
HRESULT SmsSetSMSC (const SMS_ADDRESS * const psmsaSMSCAddress);
Both functions take a single parameter, a pointer to an SMS_ADDRESS structure. Typically, the telephony provider preconfigures this service center number in the phone.
The current time can be estimated with a call to SmsGetTime, prototyped as:
HRESULT SmsGetTime (SYSTEMTIME * const ptsCurrentTime,
DWORD * const pdwErrorMargin);
The time returned is based on the time received by the SMS service center the last time the system received a timestamp. The time is a UTC number so it needs to be corrected for the local time zone. The pdwErrorMargin parameter should point to a DWORD that receives an estimated error margin, in seconds, for the time. If an error margin can’t be determined, the error margin will be set to 0xFFFFFFFF.
An application can ask to be started when a message is received by calling the function SmsSetMessageNotification, prototyped as:
HRESULT SmsSetMessageNotification (const SMSREGISTRATIONDATA * psmsrd);
The single parameter is a pointer to a SMSREGISTRATIONDATA structure, defined as:
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 cbSize field should be set with the size of the structure before calling the function. The tszAppName and tszParams fields specify the application name and command line for the application when it’s launched. The tszProtocolName field should be set to the message protocol for the messages the application wants to receive. For example, if the application wants to receive standard text messages, the field should be set to SMS_MSGTYPE_TEXT.
When the application no longer wants to be notified when messages are received, it can call SmsClearMessageNotification, prototyped as:
HRESULT SmsClearMessageNotification (const LPCTSTR tszProtocolName);
The single parameter is the message protocol that was specified when SmsSetMessageNotification was called.
// (c) Aicom Global Software, S.L.U. - 2009
// José Luis Capel
#include "FWCE.ch"
//----------------------------------------------------------------------------//
function Main()
local oDlg, cText := "Hello world! ", cNumber := " "
DEFINE DIALOG oDlg TITLE "Sending SMS" ;
SIZE 200, 150 COLOR "N/G"
@ 0.5, 2 SAY "Message:" SIZE 22, 10
@ 0.6, 5 GET cText SIZE 50, 12 COLOR "GR+/G"
@ 1.3, 2 SAY "Phone no:" SIZE 22, 10
@ 1.5, 5 GET cNumber SIZE 50, 10 COLOR "GR+/G"
@ 2.2, 6 BUTTON "Send" ACTION MsgInfo(SEND_SMS(Substr(AllTrim(cText),1,140),AllTRim(cNumber)),"Resultado") SIZE 30, 10
ACTIVATE DIALOG oDlg CENTERED
return nil
//----------------------------------------------------------------------------//
#pragma BEGINDUMP
#include <hbapi.h>
#include <windows.h>
#include <sms.h>
HB_FUNC( SEND_SMS ) // cMessage, cNumber
{
SMS_HANDLE smshHandle;
SMS_ADDRESS smsaDestination;
TEXT_PROVIDER_SPECIFIC_DATA tpsd;
SMS_MESSAGE_ID smsmidMessageID = 0;
wchar_t * sztMessage = HB_TCHAR_CONVTO( hb_parc(1) );
wchar_t * sztPhoneNumber = HB_TCHAR_CONVTO( hb_parc(2) );
BOOL bInternational = *sztPhoneNumber == '+' ? TRUE : FALSE ;
// try to open an SMS Handle
HRESULT hr = SmsOpen(SMS_MSGTYPE_TEXT, SMS_MODE_SEND, &smshHandle, NULL);
if (hr != ERROR_SUCCESS) {
HB_TCHAR_FREE(sztMessage);
HB_TCHAR_FREE(sztPhoneNumber);
hb_retc("ERROR - Can't open Sms system");
return ;
}
// Create the destination address
memset (&smsaDestination, 0, sizeof (smsaDestination) );
smsaDestination.smsatAddressType = bInternational ? SMSAT_INTERNATIONAL : SMSAT_NATIONAL;
lstrcpy(smsaDestination.ptsAddress, sztPhoneNumber);
// Set up provider specific data
tpsd.dwMessageOptions = PS_MESSAGE_OPTION_NONE;
tpsd.psMessageClass = PS_MESSAGE_CLASS0;
tpsd.psReplaceOption = PSRO_NONE;
// Send the message, indicating success or failure
hr = SmsSendMessage (smshHandle, NULL, &smsaDestination, NULL,
(PBYTE) sztMessage, _tcslen(sztMessage) * sizeof(wchar_t),
(PBYTE) &tpsd,12, SMSDE_OPTIMAL,
SMS_OPTION_DELIVERY_NONE, &smsmidMessageID);
switch( hr )
{
case S_OK :
hb_retc("OK");
break;
case E_FAIL :
hb_retc("ERROR - E_FAIL");
break;
case E_INVALIDARG :
hb_retc("ERROR - E_INVALIDARG");
break;
case E_OUTOFMEMORY :
hb_retc("ERROR - E_OUTOFMEMORY");
break;
case E_UNEXPECTED :
hb_retc("ERROR - E_UNEXPECTED");
break;
case SMS_E_TOOMUCHDATA :
hb_retc("ERROR - SMS_E_TOOMUCHDATA");
break;
case SMS_E_INVALIDDATA :
hb_retc("ERROR - SMS_E_INVALIDDATA");
break;
case SMS_E_BUFFERTOOSMALL :
hb_retc("ERROR - SMS_E_BUFFERTOOSMALL");
break;
case SMS_E_PROVIDERSPECIFICBUFFERWRONGSIZE :
hb_retc("ERROR - SMS_E_PROVIDERSPECIFICBUFFERWRONGSIZE");
break;
case SMS_E_TIMEUNAVAILABLE :
hb_retc("ERROR - SMS_E_TIMEUNAVAILABLE");
break;
case SMS_E_UNKNOWNSCADDRESS :
hb_retc("ERROR - SMS_E_UNKNOWNSCADDRESS");
break;
case SMS_E_RECEIVEHANDLEALREADYOPEN :
hb_retc("ERROR - SMS_E_RECEIVEHANDLEALREADYOPEN");
break;
case SMS_E_DESTINATIONOUTOFSVC :
hb_retc("ERROR - SMS_E_DESTINATIONOUTOFSVC");
break;
case SMS_E_INVALIDADDRESS :
hb_retc("ERROR - SMS_E_INVALIDADDRESS");
break;
case SMS_E_MSGBARREDBYOPERATOR :
hb_retc("ERROR - SMS_E_MSGBARREDBYOPERATOR");
break;
case SMS_E_MSGCALLBARRED :
hb_retc("ERROR - SMS_E_MSGCALLBARRED");
break;
case SMS_E_NOSCSUBSCRIPTION :
hb_retc("ERROR - SMS_E_NOSCSUBSCRIPTION");
break;
case SMS_E_SCBUSY :
hb_retc("ERROR - SMS_E_SCBUSY");
break;
case SMS_E_SVCNOTSUBSCRIBED :
hb_retc("ERROR - SMS_E_SVCNOTSUBSCRIBED");
break;
case SMS_E_UNASSIGNEDNUMBER :
hb_retc("ERROR - SMS_E_UNASSIGNEDNUMBER");
break;
case SMS_E_UNIDENTIFIEDSUBCRIBER :
hb_retc("ERROR - SMS_E_UNIDENTIFIEDSUBCRIBER");
break;
default :
hb_retc("ERROR - NO ERROR CODE");
break;
}
SmsClose (smshHandle);
HB_TCHAR_FREE(sztMessage);
HB_TCHAR_FREE(sztPhoneNumber);
return;
}
#pragma ENDDUMP
Return to FiveWin for Pocket PC
Users browsing this forum: No registered users and 3 guests