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