Отправка SMS под wxWinCE с помощью CE MAPI

Появилась необходимость программно отправлять SMS с телефона под управлением Windows Mobile. Решил не изобретать велосипед и поиспользовать MAPI для этих целей. После недолгих поисков набрел на эту статью на CodeProject. Немного переделал код для использования с wxWidgets. Вот что получилось

wxSendSMS.h

#ifndef _WX_SMS_SENDER_H
#define _WX_SMS_SENDER_H

#include 
#include 
#include 
#include 

class wxSMSSender
{
public:
	virtual bool Send(const wxString & from, 
		const wxString & to, const wxString & text) = 0;
};

class wxSMSSenderMAPI : public wxSMSSender
{
	HRESULT GetSMSFolder(const CComPtr& msgStore, 
		CComPtr& folder);
	HRESULT GetSMSMsgStore(const CComPtr& session, 
		CComPtr& msgStore);
	HRESULT SendSMSMessage(const CComPtr& session, 
		LPCTSTR lpszFrom, LPCTSTR lpszTo, LPCTSTR lpszMessage);
public:
	virtual bool Send(const wxString & from, 
		const wxString & to, const wxString & text);
};

#endif

wxSendSMS.cpp

#include "wxSMSSender.h"

HRESULT wxSMSSenderMAPI::GetSMSFolder(
			const CComPtr& msgStore, 
			CComPtr& folder)
{
	// Now get the Drafts folder.
	SPropTagArray propDefaultFolder;
	propDefaultFolder.cValues = 1;
	propDefaultFolder.aulPropTag[0] = PR_CE_IPM_DRAFTS_ENTRYID;

	ULONG	values;
	LPSPropValue propVals;
	HRESULT hr = msgStore->GetProps(&propDefaultFolder, 
		MAPI_UNICODE, &values, &propVals);
	if (FAILED(hr))
	{
		return hr;
	}

	SBinary& eidDrafts = propVals->Value.bin;

	hr = msgStore->OpenEntry(eidDrafts.cb, (LPENTRYID)eidDrafts.lpb, 
		NULL, MAPI_MODIFY, NULL, (LPUNKNOWN*)&folder);
	return hr;
}

HRESULT wxSMSSenderMAPI::GetSMSMsgStore(
			const CComPtr& session, 
			CComPtr& msgStore)
{
	// first we get the msgstores table from the session
	CComPtr table;
	HRESULT hr = session->GetMsgStoresTable(MAPI_UNICODE, &table);
	if (FAILED(hr))
	{
		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 = table->QueryRows(1, 0, &pRowSet);

		// If we failed to query the
		// rows then we need to break
		if (FAILED(hr)) 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 = session->OpenMsgStore(NULL, blob.cb, 
				(LPENTRYID)blob.lpb, NULL, 0, &msgStore);
		}
		else
		{
			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 values;
		SPropValue* pProps = NULL;
		hr = msgStore->GetProps(&props, MAPI_UNICODE, &values, &pProps);
		if (FAILED(hr) || values != 1)
		{
			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))
	{
		msgStore.Release();
	}

	return hr;
}

HRESULT wxSMSSenderMAPI::SendSMSMessage(const CComPtr& session, 
			LPCTSTR lpszFrom, LPCTSTR lpszTo, LPCTSTR lpszMessage)
{

	// now get the SMS message store
	CComPtr msgStore;
	HRESULT hr = GetSMSMsgStore(session, msgStore);
	if (FAILED(hr)) return hr;
	
	CComPtr folder;
	hr = GetSMSFolder(msgStore, folder);
	if (FAILED(hr)) return hr;

	CComPtr message;
	hr = folder->CreateMessage(NULL, 0 ,&message);
	if (FAILED(hr)) 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 = message->ModifyRecipients(MODRECIP_ADD, &adrlist); 
	if (FAILED(hr)) return hr;

	// 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 = message->SetProps(sizeof(props) / sizeof(SPropValue), 
		(LPSPropValue)&props, NULL);
	if (FAILED(hr)) return hr;

	// having set all the required fields we can now
	// pass the message over to the msgstore transport
	// to be delivered.
	hr = message->SubmitMessage(0);
	if (FAILED(hr)) return hr;

	return FALSE;
}

bool wxSMSSenderMAPI::Send(const wxString & from, 
		const wxString & to, const wxString & text)
{
	do
	{
		HRESULT hr = MAPIInitialize(NULL);
		if (FAILED(hr)) break;
		CComPtr mapiSession;
		hr = MAPILogonEx(0 ,NULL, NULL, 0, &mapiSession);
		if (FAILED(hr)) break;
		bool result = SUCCEEDED(SendSMSMessage(mapiSession, 
			from.GetData(), to.GetData(), text.GetData()));
		mapiSession->Logoff(0, 0, 0);
		mapiSession.Release();
		MAPIUninitialize();
		return result;
	}
	while(false);
	return false;
}

А пользоваться этим всем очень просто:

wxSMSSenderMAPI sender;
if(!sender.Send(m_FromTextCtrl->GetValue(), 
	m_ToTextCtrl->GetValue(), m_SMSTextCtrl->GetValue()))
{
	wxMessageBox(_("Не могу отправить SMS!"));
}

Исходный код примера отправки SMS с помощью CE MAPI для Windows Mobile
Скриншот тестового приложения:

Отправка SMS с помощью CE MAPI - Скришот под Windows Mobile

Отправка SMS с помощью CE MAPI - Скришот под Windows Mobile

Leave a Reply

Your email address will not be published. Required fields are marked *

Please leave these two fields as-is: