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

wxSendSMS.h

#ifndef _WX_SMS_SENDER_H
#define _WX_SMS_SENDER_H

#include <wx/wx.h>
#include <atlbase.h>
#include <cemapi.h>
#include <mapiutil.h>

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

class wxSMSSenderMAPI : public wxSMSSender
{
	HRESULT GetSMSFolder(const CComPtr<IMsgStore>& msgStore, 
		CComPtr<IMAPIFolder>& folder);
	HRESULT GetSMSMsgStore(const CComPtr<IMAPISession>& session, 
		CComPtr<IMsgStore>& msgStore);
	HRESULT SendSMSMessage(const CComPtr<IMAPISession>& 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<IMsgStore>& msgStore, 
			CComPtr<IMAPIFolder>& 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<IMAPISession>& session, 
			CComPtr<IMsgStore>& msgStore)
{
	// first we get the msgstores table from the session
	CComPtr<IMAPITable> 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<IMAPISession>& session, 
			LPCTSTR lpszFrom, LPCTSTR lpszTo, LPCTSTR lpszMessage)
{

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

	CComPtr<IMessage> 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<IMAPISession> 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
Скриншот тестового приложения:

Previous ArticleNext Article
Технический директор IT-Dimension, компании-разработчика кросс-платформенного программного обеспечения

Leave a Reply

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

О.

Обучающие материалы по Windows Mobile (Windows Mobile RampUp Track) доступны на MSDN

На MSDN доступен курс по разработке для Windows Mobile. Материал состоит из 7ми модулей, каждый из которых содержит лекции и медиа-материалы по разработке мобильных приложений. После прохождения курса можно получить что-то вроде серитификата. У них это называется “Graduation Award”.

W.

Windows Mobile Widgets – Новый тип приложений для Windows Mobile

Windows Mobile Widgets - MSN WidgetСегодня в блоге разработчиков Windows Mobile появилась информация о новом типе приложений, который будет доступен в Windows Mobile 6.5.

На данный момент для разработки приложений для Windows Mobile можно было использовать либо системное API (Native) либо управляемый код (Managed). В новой версии Windows Mobile ,удет доступна разработка приложений с использованием Web-технологий (HTML, CSS, AJAX, JavaScript).

Т.к. эти приложения будут использовать возможности новой версии Internet Explorer, то разработчики будут иметь доступ к использованию Flash и ActiveX компонентов, установленных на устройстве, таких, например, как MediaPlayer.

Заявлено также соответствие стандарту разработки мобильных widget-приложений от W3C, а также прозрачности для PNG-bзображений и нормальная работа с AJAX (support for the Window.XMLHttpRequest object work as expected).

Более подробно о новом типе приложений разработчики обещают рассказать на TechDays в апреле.