Aug
06

Программная отправка SMS на C++ с помощью CE MAPI

Google Buzz

Появилась необходимость программно отправлять 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
Скриншот тестового приложения:

Еще интересные посты о программировании для мобильных устройств:

No Comments

Make A Comment

No comments yet.

Comments RSS Feed   TrackBack URL

Leave a comment

Please leave these two fields as-is:
Переезд? Мы Вам поможем: такелажные работы. Перевозка сейфов. ; Объявления о продаже квартир: москва тиват москва авиабилеты. Авиабилеты / жд билеты.

top