Thursday, August 7, 2008

How to create and send SMS using MAPI?

Here is a code snippet i use to create and send my SMS in SmileySMS program.
From my experience,using fewer properties for ModifyRecipients or SetProps will make it fail on some devices.

IMsgStore *FindInboxSMSStore(IMAPISession *pSession)
{
const SizedSPropTagArray (2, spta) = { 2, PR_DISPLAY_NAME, PR_ENTRYID };
HRESULT hr;
SRowSet *prowset = NULL;
CComPtr<IMAPITable> ptbl;
CComPtr<IMsgStore> pStore;

// Get the table of accounts
hr = pSession->GetMsgStoresTable(0, &ptbl);
CHR(hr);
// set the columns of the table we will query
hr = ptbl->SetColumns((SPropTagArray *) &spta, 0);
CHR(hr);

while (TRUE)
{
hr = ptbl->QueryRows (1, 0, &prowset);
if ((hr != S_OK) || (prowset == NULL) || (prowset->cRows == 0))
break;

ASSERT (prowset->aRow[0].cValues == spta.cValues);
SPropValue *pval = prowset->aRow[0].lpProps;

ASSERT (pval[0].ulPropTag == PR_DISPLAY_NAME);
ASSERT (pval[1].ulPropTag == PR_ENTRYID);

if (!_tcscmp(pval[0].Value.lpszW, TEXT("SMS")))
{
// Get the Message Store pointer
hr = pSession->OpenMsgStore(0, pval[1].Value.bin.cb, (LPENTRYID)pval[1].Value.bin.lpb, 0, 0, &pStore);
CHR(hr);
FreeProws (prowset);

return pStore;
}
// Free the previous row
FreeProws (prowset);
prowset = NULL;
}
Error:
FreeProws (prowset);
return NULL;
}



HRESULT CreateSendSMSMessage(IMAPISession *spSession,LPCTSTR lpszToNumber, LPCTSTR lpszToDisplay,LPCTSTR lpszMessage,IMessage** pMsg)
{
CComPtr<IMsgStore> spMsgStore;
ULONG cItems;
ULONG rgtagsMsgStore[] = { 1, PR_CE_IPM_DRAFTS_ENTRYID };
LPSPropValue rgprops;
HRESULT hr;

spMsgStore = FindInboxSMSStore(spSession);
if (spMsgStore == NULL)
return S_FALSE;

CComPtr<IMAPIFolder> spFolder;

hr = spMsgStore->GetProps((LPSPropTagArray)rgtagsMsgStore, MAPI_UNICODE, &cItems, &rgprops);
if (FAILED(hr))
return hr;

hr = spSession->OpenEntry(rgprops[0].Value.bin.cb,(LPENTRYID)rgprops[0].Value.bin.lpb,
NULL, 0, NULL, (LPUNKNOWN*)&spFolder);
if (FAILED(hr))
return hr;

MAPIFreeBuffer(rgprops);

hr = spFolder->CreateMessage(NULL, 0 ,&(*pMsg));
if (FAILED(hr))
return hr;

int cb;
LPSRowSet pRowSet = NULL;
LPSPropValue pVal = NULL;
LPBYTE pb = NULL;

// * 5 because there are 5 props
cb = sizeof(SRowSet) + sizeof(SPropValue) * 5; // SRowSet includes one SRow.

// Now allocate a buffer.
CPR((pb = new BYTE[cb]))

// Copy the properties over.
pRowSet = (LPSRowSet)pb;
pVal = (LPSPropValue)(pb + ( sizeof(SRowSet) + sizeof(SRow) ));

// initialize
pRowSet->aRow[0].cValues = 0;
pRowSet->aRow[0].lpProps = pVal;

// begin filling the values
pVal->ulPropTag = PR_RECIPIENT_TYPE;
pVal->Value.ul = MAPI_TO;
++pVal;
++pRowSet->aRow[0].cValues;

pVal->ulPropTag = PR_EMAIL_ADDRESS;
pVal->Value.lpszW = (LPWSTR)lpszToNumber;
++pVal;
++pRowSet->aRow[0].cValues;

pVal->ulPropTag = PR_DISPLAY_NAME;
pVal->Value.lpszW = (LPWSTR)lpszToDisplay;
++pVal;
++pRowSet->aRow[0].cValues;

pVal->ulPropTag = PR_ADDRTYPE;
pVal->Value.lpszW = L"SMS";
++pVal;
++pRowSet->aRow[0].cValues;

pVal->ulPropTag = PR_MESSAGE_CLASS;
pVal->Value.lpszW = (LPTSTR)L"IPM.SMStext";
++pVal;
++pRowSet->aRow[0].cValues;

pRowSet->cRows = 1;

hr = (*pMsg)->ModifyRecipients(MODRECIP_ADD, (LPADRLIST)pRowSet);
CHR(hr);

// now we set the additional properties for the
// message
SPropValue props[6];

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;

props[1].ulPropTag = PR_MSG_STATUS;
props[1].Value.ul = MSGSTATUS_RECTYPE_SMS;

props[2].ulPropTag = PR_SENDER_ADDRTYPE;
props[2].Value.lpszW = L"SMS";

props[3].ulPropTag = PR_MESSAGE_CLASS;
props[3].Value.lpszW = L"IPM.SMStext";

props[4].ulPropTag = PR_MESSAGE_FLAGS;
props[4].Value.ul = MSGFLAG_FROMME | MSGFLAG_UNSENT;

props[5].ulPropTag = PR_SENDER_EMAIL_ADDRESS;
props[5].Value.lpszW = (LPWSTR)lpszToNumber;

hr = (*pMsg)->SetProps(sizeof(props) / sizeof(props[0]), (LPSPropValue)&props, NULL);
if (FAILED(hr))
return hr;

(*pMsg)->SaveChanges(0);
// having set all the required fields we can now
// pass the message over to the msgstore transport
// to be delivered.
//hr = (*pMsg)->SubmitMessage(0);
//if (FAILED(hr))
//return hr;
Error:
delete [] pb;
return FALSE;
}

5 comments:

Unknown said...

Thank you for your post. I tried your code on windows mobile 6 emulator and it's doesn't modify recipients (shows no recipients for message in drafts).

Do you have some ideas how to fix it or what things I should check?

Thanks in advance.
Ros.

R.GH. said...

Well,Are you sure?
Because this is the piece of code i use inside my smileysms program,and it works on emulators fine,as well as devices.

Does smileySMS work for you?

Unknown said...

Yes, I'm sure (however I didn't try smileySMS). Btw SaveChanges(0) returns E_NOTIMPL because it's not implemented for windows mobile.

It looks like there're many differences in MAPI impl. between pocket pc and windows mobile.

Anonymous said...

hi. Where should I paste this code? Sorry i'm not a programmer. I can't currently send messages with smiley sms.

Anonymous said...

Hi. Thanks for the code. Could you tell me what property/flag I have to set to enable SMS delivery notification? I've Googled a lot and this information is nowhere to be found. Thanks in advance