[经验分享] windows mobile 发送短信总结

常用方法1
最近在工作中遇到一个比较怪异的现象,在某些省区与 SP 的短信交互存在问题,短信发送不出去。查了一下原因: SmsSendMessage 参数设置的问题。
在 Windows Mobile 5.0SDK 的 HelloSMS 例子中,发送短信的代码是这样写的
void  SendSMS(BOOL bSendConfirmation, BOOL bUseDefaultSMSC, LPCTSTR lpszSMSC, LPCTSTR lpszRecipient, LPCTSTR lpszMessage)
{
     SMS_HANDLE smshHandle;
     SMS_ADDRESS smsaSource;
     SMS_ADDRESS smsaDestination;
     TEXT_PROVIDER_SPECIFIC_DATA tpsd;
     SMS_MESSAGE_ID smsmidMessageID;

      //  try to open an SMS Handle
      if (FAILED(SmsOpen(SMS_MSGTYPE_TEXT, SMS_MODE_SEND,  & smshHandle, NULL)))
       {
         MessageBox(NULL,
                     (LPCTSTR)LoadString(ghInstance, IDS_ERROR_SMSOPEN,  0 ,  0 ),
                     (LPCTSTR)LoadString(ghInstance, IDS_CAPTION_ERROR,  0 ,  0 ),
                     MB_OK  |  MB_ICONERROR);
          return ;
     }

      //  Create the source address
      if ( ! bUseDefaultSMSC)
       {
         smsaSource.smsatAddressType  =  SMSAT_INTERNATIONAL;
         _tcsncpy(smsaSource.ptsAddress, lpszSMSC, SMS_MAX_ADDRESS_LENGTH);
     }

      //  Create the destination address
     smsaDestination.smsatAddressType  =  SMSAT_INTERNATIONAL;
     _tcsncpy(smsaDestination.ptsAddress, lpszRecipient, SMS_MAX_ADDRESS_LENGTH);

      //  Set up provider specific data
     memset( & tpsd,  0 ,  sizeof (tpsd));
     tpsd.dwMessageOptions  =  bSendConfirmation  ?  PS_MESSAGE_OPTION_STATUSREPORT : PS_MESSAGE_OPTION_NONE;
     tpsd.psMessageClass  =  PS_MESSAGE_CLASS1;
     tpsd.psReplaceOption  =  PSRO_NONE;
     tpsd.dwHeaderDataSize  =   0 ;

      //  Send the message, indicating success or failure
      if (SUCCEEDED(SmsSendMessage(smshHandle, ((bUseDefaultSMSC)  ?  NULL :  & smsaSource),
                                   & smsaDestination, NULL, (PBYTE) lpszMessage,
                                  _tcslen(lpszMessage)  *   sizeof (TCHAR), (PBYTE)  & tpsd,
                                   sizeof (TEXT_PROVIDER_SPECIFIC_DATA), SMSDE_OPTIMAL,
                                  SMS_OPTION_DELIVERY_NONE,  & smsmidMessageID)))
       {
         MessageBox(NULL,
                     (LPCTSTR)LoadString(ghInstance, IDS_SMSSENT,  0 ,  0 ),
                     (LPCTSTR)LoadString(ghInstance, IDS_CAPTION_SUCCESS,  0 ,  0 ),
                     MB_OK);
     }
      else
       {
         MessageBox(NULL,
                     (LPCTSTR)LoadString(ghInstance, IDS_ERROR_SMSSEND,  0 ,  0 ),
                     (LPCTSTR)LoadString(ghInstance, IDS_CAPTION_ERROR,  0 ,  0 ),
                     MB_OK  |  MB_ICONERROR);
     }

      //  clean up
     VERIFY(SUCCEEDED(SmsClose(smshHandle)));
}

这段代码在一般情况下是运行正确的,对于手机与手机的发送是不存在问题的,但是与SP 的交互在某些省区就存在短信发送不出去的情况。
为了更好的理解这段代码,我们先来熟悉一下SmsSendMessage 及各个参数。
SmsSendMessage
功能 : Use the SmsSendMessage function to create and send an Short Message Service (SMS) message. (创建和发送短信,但短信发送后并不保存到发件箱中)
原型 :
HRESULT SmsSendMessage (
const SMS_HANDLE smshHandle, // 调用SmsOpen 时获得的短信句柄
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); // 用于得到系统回执的信息
(具体介绍可查看http://msdn.microsoft.com/en-us/library/aa455068.aspx
在实际应用中短信发送不出去,但是SmsSendMessage 的返回值是S_OK 值。在一些文章中有人这样认为是短信编码的问题造成的。
如果编码格式不对可能造成短信中心网关把短信给吞掉的情况,程序虽然调用成功,但是就是目标号码收不到短信。函数参数中的后三个参数可以不用或设默认值都可以。
起初我也是认为这个地方造成的,很是兴奋。短信的回复内容恰为字母,我误以为短信内容此时是7-BIT 的短消息,短信网关把短信给吞掉了,造成目标号码收不到短信。在练习中却也阴差阳错的成功了。很高兴的把理由归到了这个地方。并这样总结: SmsSendMessage 可以支持7-bit 的ASCII 码的短消息,也支持16-bit 的unicode 的短消息。但内容为ASCII 的时候,短信编码为 SMSDE_GSM 或SMSDE_OPTIMAL ,当内容不全是 ASCII 的时候,短信编码为SMSDE_GSM 或SMSDE_OPTIMAL 。所以回复内容改为汉字即可。
但是这样对么?起初我认为我的解释很合理. 但是我却发现我的一个参数与原来的程序不一样.
是我在尝试中无意修改了一个参数, 将
tpsd .psMessageClass = PS_MESSAGE_CLASS1 ;
修改为了
tpsd .psMessageClass = PS_MESSAGE_CLASSUNSPECIFIED;
这是发送短信中的运营商的指定数据 TEXT_PROVIDER_SPECIFIC_DATA , 它的参数 psMessageClass 是指
Text Short Message Service (SMS) messages with the appropriate flag can replace previously received notifications with a similar flag and originating address.
它有以下五个值 :
PS_MESSAGE_CLASS0: The message should be displayed immediately but not stored. The MS shall send an acknowledgement to the service center when the message has successfully reached the MS. (被接受后立即显示但不存储 ( 称为闪信 ) 。需要向 SMSC 发送确认信息。)
PS_MESSAGE_CLASS1 : The message should be stored and an acknowledgement should be sent to the Service Center when it is stored. (接收后被存储,一旦存储,需要向 SMSC 发送确认信息。)
PS_MESSAGE_CLASS2 : The message should be transferred to the SMS data field in the subscriber identity module (SIM) before an acknowledgement is sent to the Service Center.
PS_MESSAGE_CLASS3 : When the message has successfully reached the destination and can be stored, an acknowledgement is sent to the Service Center.
PS_MESSAGE_CLASSUNSPECIFIED : The message Class is not set in the outgoing or incoming message. (对发出或收到的短信不进行设置)

分析以上五个值,前四个值有一个共同的特点,都需要向 SMSC 发送确认。而最后一个值没有设定。
这个值的改动,解决了我所遇到的问题。但究其原因,我有些想不通为什么?
    但是在实际应用中,出现了 tmail.exe 的异常。不知道是这个值的变动带来的问题,还是实际模块中存在的问题。还需要继续研究一下。
常用方法2
//zhaozm add for SMS Send
BOOL SendSMSMessage_Internal(BOOL bSendConfirmation, BOOL bUseDefaultSMSC, LPCTSTR lpszSMSC, LPCTSTR lpszRecipient, LPCTSTR lpszMessage)
{
BOOL bRet = FALSE;
SMS_HANDLE smshHandle;
SMS_ADDRESS smsaSource;
SMS_ADDRESS smsaDestination;
TEXT_PROVIDER_SPECIFIC_DATA tpsd;
SMS_MESSAGE_ID smsmidMessageID;
int i = 3;
// 打开SMS句柄 // 发送模式
if(FAILED(SmsOpen(SMS_MSGTYPE_TEXT, SMS_MODE_SEND, &smshHandle, NULL)))
{
  return bRet;
}
// Create the source address
if(!bUseDefaultSMSC)
{
  smsaSource.smsatAddressType = SMSAT_INTERNATIONAL;
  _tcsncpy(smsaSource.ptsAddress, lpszSMSC, SMS_MAX_ADDRESS_LENGTH);
}
memset (&smsaDestination, 0, sizeof (smsaDestination));
// 地址类型 SMSAT_NATIONAL :国内电话  SMSAT_INTERNATIONAL:国际电话
//smsaDestination.smsatAddressType = SMSAT_NATIONAL;//SMSAT_NATIONAL  SMSAT_INTERNATIONAL SMSAT_NETWORKSPECIFIC SMSAT_SUBSCRIBER SMSAT_ALPHANUMERIC SMSAT_ABBREVIATED  SMSAT_UNKNOWN
smsaDestination.smsatAddressType = SMSAT_INTERNATIONAL;
_tcsncpy(smsaDestination.ptsAddress, lpszRecipient, SMS_MAX_ADDRESS_LENGTH);
// Set up provider specific data
memset(&tpsd, 0, sizeof(tpsd));
tpsd.dwMessageOptions = bSendConfirmation ? PS_MESSAGE_OPTION_STATUSREPORT : PS_MESSAGE_OPTION_NONE; // 状态报告
tpsd.psMessageClass = PS_MESSAGE_CLASS1;
tpsd.psReplaceOption = PSRO_NONE;
tpsd.dwHeaderDataSize = 0;
//手动清空头信息
ZeroMemory(tpsd.pbHeaderData, sizeof(tpsd.pbHeaderData));        
tpsd.dwHeaderDataSize = 0;
tpsd.fMessageContainsEMSHeaders = FALSE;
tpsd.dwProtocolID = SMS_MSGPROTOCOL_UNKNOWN;
{
  HRESULT hRet = 0;
  hRet = SmsSendMessage(smshHandle, ((bUseDefaultSMSC) ? NULL : &smsaSource),
   &smsaDestination, NULL, (PBYTE) lpszMessage,
   _tcslen(lpszMessage) * sizeof(TCHAR), (PBYTE) &tpsd,
   //sizeof(TEXT_PROVIDER_SPECIFIC_DATA), SMSDE_GSM, //SMSDE_OPTIMAL,//SMSDE_UCS2,//SMSDE_GSM,
   12, SMSDE_UCS2,
   SMS_OPTION_DELIVERY_NONE, &smsmidMessageID);
  if (hRet == S_OK)
  {
   bRet = TRUE;
  }
  Log(L"SmsSendMessage hRet = %08x",hRet);
}
// clean up
VERIFY(SUCCEEDED(SmsClose(smshHandle)));
return bRet;
}
//szPhone 目标电话号码,szMessage 短信内容,nMsgLen 短信长度
BOOL SendSMSMessage(LPCTSTR szPhone, LPCTSTR szMessage)
{
Log(L"SendSMSMessage szPhone = %s,szMessage = %s",szPhone,szMessage);
BOOL bRet = FALSE;
if (szPhone == NULL || szMessage == NULL)
  return FALSE;
if(SendSMSMessage_Internal(TRUE, TRUE, 0, szPhone, szMessage) )
{
  Log(L"SendSMSMessage_Internal success!!!");
  bRet = TRUE;
}
return bRet;
}

常用方法3
TCHAR szCMD[] = _T("-service /"SMS/" -to /"13802263105/" -body/"ddddd/" -subject /"我的文本信息/"");
CreateProcess(_T("[url=file://Windowstmail.exe/]//Windows//tmail.exe [/url]"), szCMD, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL);

常用方法4
// This function is used to get the msgstore named SMS from msgstores on the
// device.
// I could have used just raw pointers but it is much easier and safer to just
// use smart pointers.
HRESULT GetSMSMsgStore(const CComPtr<IMAPISession>& spSession, CComPtr<IMsgStore>& spMsgStore)
{
// first we get the msgstores table from the session
CComPtr<IMAPITable> spTable;
HRESULT hr = spSession->GetMsgStoresTable(MAPI_UNICODE, &spTable);
if (FAILED(hr))
{
  Log(IDS_SMS_FAIL_MSGSTORE_TABLES);
  return FALSE;
}
// next we loop over the message stores opening each msgstore and
// getting its name to see if the name matches SMS.
// If it does then we break out of the loop
while (TRUE)
{
  SRowSet* pRowSet = NULL;
  hr = spTable->QueryRows(1, 0, &pRowSet);
  // If we failed to query the
  // rows then we need to break
  if (FAILED(hr))
  {
   AfxMessageBox(IDS_SMS_FAILEDTABLE);
   break;
  }
  // if we got no rows back then just exit the loop
  //remembering to set an error
  if (pRowSet->cRows == 1)
  {
   ASSERT(pRowSet->aRow[0].lpProps->ulPropTag == PR_ENTRYID);
   SBinary& blob = pRowSet->aRow[0].lpProps->Value.bin;
   hr = spSession->OpenMsgStore(NULL, blob.cb, (LPENTRYID)blob.lpb, NULL, 0, &spMsgStore);
   if (FAILED(hr))
    Log(IDS_SMS_FAILED_OPENMSGSTORE);
  }
  else
  {
   Log(IDS_SMS_MSGSTORENOTFOUND);
   hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
  }
  // now remember to free the row set
  FreeProws(pRowSet);
  if (FAILED(hr))
  {
   break;
  }
  // now get the display name property from the
  // message store to compare it against the name
  // 'SMS'
  SPropTagArray props;
  props.cValues = 1;
  props.aulPropTag[0] = PR_DISPLAY_NAME;
  ULONG cValues;
  SPropValue* pProps = NULL;
  hr = spMsgStore->GetProps(&props, MAPI_UNICODE, &cValues, &pProps);
  if (FAILED(hr) || cValues != 1)
  {
   Log(IDS_SMS_FAILED_GETNAME);
   break;
  }
  // if the name matches SMS then break and as
  // hr == S_OK the current MsgStore smart pointer
  // will correctly be set.
  if (_tcsicmp(pProps[0].Value.lpszW, _T("SMS")) == 0)
  {
   break;
  }
}
// if we failed for some reason then we clear out
// the msgstore smartpointer and return the error.
if (FAILED(hr))
{
  spMsgStore.Release();
}
return hr;
}

/
// This function is used to get the folder named drafts from the msgstore on the
// device.
// I could have used just raw pointers but it is much easier and safer to just
// use smart pointers.
HRESULT GetSMSFolder(const CComPtr<IMsgStore>& spMsgStore, CComPtr<IMAPIFolder>& spFolder)
{
        // Now get the Drafts folder.
  SPropTagArray propDefaultFolder;
  propDefaultFolder.cValues = 1;
  propDefaultFolder.aulPropTag[0] = PR_CE_IPM_DRAFTS_ENTRYID;
  
  ULONG   cValues;
  LPSPropValue pPropVals;
        HRESULT hr = spMsgStore->GetProps (&propDefaultFolder, MAPI_UNICODE, &cValues, &pPropVals);
        if (FAILED(hr))
  {
   Log(IDS_SMS_FOLDERNOTFOUND);
   return hr;
  }
   
  SBinary& eidDrafts = pPropVals->Value.bin;
        hr = spMsgStore->OpenEntry(eidDrafts.cb, (LPENTRYID)eidDrafts.lpb, NULL, MAPI_MODIFY, NULL, (LPUNKNOWN*)&spFolder);
  if (FAILED(hr))
  {
   Log(IDS_SMS_FOLDERNOTOPENED);
  }
  return hr;
}
/
// This function is used to get the send the message.
// This uses an opened MAPI session
HRESULT SendSMSMessage(const CComPtr<IMAPISession>& spSession, LPCTSTR lpszFrom, LPCTSTR lpszTo, LPCTSTR lpszMessage)
{
// now get the SMS message store
CComPtr<IMsgStore> spMsgStore;
HRESULT hr = GetSMSMsgStore(spSession, spMsgStore);
if (FAILED(hr))
{
  return hr;
}
CComPtr<IMAPIFolder> spFolder;
hr = GetSMSFolder(spMsgStore, spFolder);
if (FAILED(hr))
{
  return hr;
}
CComPtr<IMessage> spMessage;
hr = spFolder->CreateMessage(NULL, 0 ,&spMessage);
if (FAILED(hr))
{
  Log(IDS_SMS_FAIL_CREATEMESSAGE);
  return hr;
}
// set the recipients
// set up the required fields for a recipient
SPropValue propRecipient[3];
// it is vital we clear the property structure
// as there are fields we do not use but MAPI seems
// to be sentative to them.
ZeroMemory(&propRecipient, sizeof(propRecipient));
// set the recipient type which coul be to, cc, bcc
// but ehre must at least be a to field
propRecipient[0].ulPropTag = PR_RECIPIENT_TYPE;
propRecipient[0].Value.l = MAPI_TO;
// we set the type of address to sms instead of
// smtp
propRecipient[1].ulPropTag = PR_ADDRTYPE;
propRecipient[1].Value.lpszW = _T("SMS");
// we finally set the email address to the
// phone number of the person we are sending the message
// to
propRecipient[2].ulPropTag = PR_EMAIL_ADDRESS;
propRecipient[2].Value.lpszW = (LPWSTR)lpszTo;
// set the addrlist to point to the properties
ADRLIST adrlist;
adrlist.cEntries = 1;
adrlist.aEntries[0].cValues = 3;
adrlist.aEntries[0].rgPropVals = (LPSPropValue)(&propRecipient);
// finally modify the recipients of the message
hr = spMessage->ModifyRecipients(MODRECIP_ADD, &adrlist);
if (FAILED(hr))
{
  Log(IDS_SMS_FAILED_ADDRECIPIENTS);
  return hr;
}
else
  ; // added the recipient to the message
// now we set the additional properties for the
// message
SPropValue props[4];
//note how we zero out the contents of the
// structure as MAPI is sensative to the
// contents of other fields we do not use.
ZeroMemory(&props, sizeof(props));
// first set the subject of the message
// as the sms we are going to send
props[0].ulPropTag = PR_SUBJECT;
props[0].Value.lpszW = (LPWSTR)lpszMessage;
// next set the senders email address to
// the phone number of the person we are
// sending the message to
props[1].ulPropTag = PR_SENDER_EMAIL_ADDRESS;
props[1].Value.lpszW = (LPWSTR)lpszFrom;
// finally and most importantly tell mapi
// this is a sms message in need of delivery
props[2].ulPropTag = PR_MSG_STATUS;
props[2].Value.ul = MSGSTATUS_RECTYPE_SMS;
    props[3].ulPropTag = PR_MESSAGE_FLAGS;
    props[3].Value.ul = MSGFLAG_FROMME | MSGFLAG_UNSENT;
hr = spMessage->SetProps(sizeof(props) / sizeof(props[0]), (LPSPropValue)&props, NULL);
if (FAILED(hr))
{
  Log(IDS_SMS_FAIL_SETPROPS);
  return hr;
}
// having set all the required fields we can now
// pass the message over to the msgstore transport
// to be delivered.
hr = spMessage->SubmitMessage(0);
if (FAILED(hr))
{
  Log(IDS_SMS_FAIL_SUBMITMSG);
  return hr;
}
return FALSE;
}
/
// This is the function that creates the session, using the
// from, the recipient and the message.
// This opens the session, opens the sms message store and opens
// the drafts folder then create a new message and sets the sender,
// recipient and messag, then finally sends the message.
BOOL DoSendMessage(LPCTSTR lpszFrom, LPCTSTR lpszTo, LPCTSTR lpszMessage)
{
HRESULT hr = MAPIInitialize(NULL);
if (FAILED(hr))
{
  Log(IDS_SMS_FAIL_MAPIINIT);
  return hr;
}
else
{
  Log(L"MAPIInitialize success/n");
}
CComPtr<IMAPISession> spSession;
BOOL bRet = FALSE;
hr = MAPILogonEx(0 ,NULL, NULL, 0, &spSession);
if (FAILED(hr))
{
  Log(IDS_SMS_FAIL_MAPILOGON);
}
else
{  
  bRet = SUCCEEDED(SendSMSMessage(spSession, lpszFrom, lpszTo, lpszMessage));
  Log(L"SendSMSMessage bRet  = %d/n",bRet?1:0);
  spSession->Logoff(0, 0, 0);
  spSession.Release();
}
MAPIUninitialize();
return bRet;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值