Появилась необходимость программно отправлять 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
Скриншот тестового приложения:
Введение Уже долгое время не пишу статьи о разработке, хотя сам процесс написания мне очень…
I can see that there is still a lot of topics at wxWidgets forums related…
I've just published the source code of wxToolBox component and a couple of sample apps at…
Microsoft released their Kinect SDK several days ago. So, for those wxWidgets developers who are…
JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for humans to…
Вдохновленнный читаемой нынче книгой My Job Went to India: 52 Ways to Save Your Job…