您的位置:首页 > 其它

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

2010-06-09 18:14 375 查看
常用方法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("//Windows//tmail.exe
"), 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;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: