• СТАТЬЯ MAPI. Добавь почту в свое приложение.
  • Программирование на Visual C++

    Выпуск №35 от 4 марта 2001 г.

    Здравствуйте!

    В сегодняшнем выпуске мы рассмотрим тему, которая также весьма и весьма часто фигурирует в ваших письмах – работа с электронной почтой из программы. Тема эта хорошо описывается в статье Михаила Плакунова, которую я и хочу предложить вашему вниманию.

    СТАТЬЯ

    MAPI. Добавь почту в свое приложение.

    Автор: Михаил Плакунов

    Источник: Софт Терра

    Введение

    В последние годы электронная почта является неотъемлемой составляющей персональных информационных средств. С большой степенью вероятности верно то, что, если у человека на столе стоит компьютер, то на нем установлена та или иная система электронной почты. Вместе с тем типичные приложения для работы с электронной почтой в чистом виде далеко не всегда отвечают требованиям автоматизации трудовой деятельности пользователя. В связи с этим существует потребность в создании различного рода приложений, не являющихся частью почтовых систем, но вместе с тем предоставляющих пользователю некоторые средства для работы с электронной почтой. Программный интерфейс MAPI, о котором пойдет речь в этой статье, является одним из средств, позволяющим создавать подобные приложения на платформе Windows.

    Что есть MAPI?

    В широком понимании MAPI (Messaging Application Programming Interface) – это целая архитектура, специфицирующая процессы взаимодействия отдельных приложений с различными почтовыми системами. Архитектура MAPI описывает так называемую подсистему MAPI, которая обеспечивает взаимодействие клиентских приложений с различными службами почтовой системы, такими как служба хранения информации, служба адресной книги, служба транспорта и т.д. С другой стороны MAPI – это прикладной интерфейс, который был создан для того, чтобы разработчики на C, C++, Visual Basic (а в последствии и Visual Basic Script) имели возможность добавлять в свои приложения функциональность для работы с электронной почтой. С точки зрения прикладной программы подсистема MAPI – это набор динамических библиотек, содержащих функции и объектно-ориентированные интерфейсы, благодаря которым взаимодействуют клиентские и серверные части почтовых приложений. О MAPI можно говорить много и долго (благо компания Microsoft постаралась сделать из MAPI очередного программного  «монстра»), но наибольший интерес для разработчиков представляют так называемые клиентские прикладные программные интерфейсы, среди которых следует выделить в первую очередь Simple MAPI, MAPI и CDO.

    Начнем с простого – Simple MAPI

    Simple MAPI предоставляет в распоряжение разработчиков всего 12 простейших функций. Они позволяют выполнять такие действия, как «сформировать сообщение», «указать адрес получателя», «отправить», «получить».  Причем все операции с сообщениями можно производить только в рамках одной папки, являющейся папкой для входящих сообщений текущего контейнера доставки (в почтовой системе MS Exchange Server это обычно или в русскоязычной версии). Разработчик не имеет доступа к полной структуре папок почтового сервера, то есть может контролировать сообщение лишь до тех пор, пока пользователь не переместит его из папки в какую-либо другую.

    Другим недостатком Simple MAPI является то, что он позволяет работать только со стандартными полями сообщения, такими как «Тема», «Отправитель», «Получатель», «Дата отправки», «Текст сообщения», «Класс сообщения», а также с вложенными файлами.

    При всей своей ограниченности Simple MAPI подкупает имено простотой в освоении и использовании. Тому, кто имеет даже небольшой опыт программирования на C или Visual Basic достаточно нескольких минут для, того чтобы научиться использовать этот интерфейс.

    Следующий фрагмент кода на языке C позволит убедиться в этом (для простоты несущественные участки кода, такие как объявление переменных и обработка ошибок опущены).

    MAPILogon(0,"My Profile", NULL, MAPI_NEW_SESSION, 0, &pSession);

    MAPIResolveName(pSession, 0, "Bill Gates", 0, 0, &pRecipient);

    ZeroMemory(&pMessage, sizeof(pMessage));

    pMessage.lpszSubject = " Greeting";

    pMessage.lpszNoteText = "Hello Bill!";

    pMessage.nRecipCount = 1;

    pMessage.lpRecips = Recipient;

    MAPISendMail(pSession, 0, &pMessage, 0, 0);

    MAPILogoff(pSession, 0, 0, 0);

    В приведенном примере формируется сообщение с темой , содержащее текст и отправляется адресату, видимое имя которого в адресной книге. Сейчас мы разберем по шагам как это делается.

    Первым делом клиентской программе необходимо начать сеанс работы с почтовой системой, для чего при помощи функции MAPILogon открывается сессия Simple MAPI. Затем из видимого имени ("Bill Gates") функция MAPIResolveName формирует структуру, содержащую точную и полную информацию об адресате (в частности его электронный адрес). Полученная информация об адресате наряду с темой и текстом формирует структуру, содержащую почтовое сообщение, готовое к отправке. Функция MAPISendMail отправляет сообщение по электронной почте. Наконец, функция MAPILogoff завершает сеанс работы с почтовой системой, закрывая сессию Simple MAPI.

    Просто, не правда ли? Немного модифицировав программу, можно дать ей возможность отправлять сообщения, содержащие не только текст, но и вложенные файлы. Изучив еще пару-тройку функций Simple MAPI, можно с легкостью запрограммировать получение анализ и удаление сообщений, содержащихся в почтовом ящике пользователя.

    Simple MAPI позволяет запрограммировать две основные функции электронной почты – отправку и прием сообщений. Зачастую это вся функциональность, необходимая приложению для работы с электронной почтой. Типичными примерами использования Simple MAPI являются приложения, производящие рассылку сообщений (возможно однотипных, по шаблону) множеству адресатов, а также приложения, время от времени сканирующие почтовый ящик пользователя и производящие анализ и обработку поступающей в него корреспонденции.

    Программисты на C найдут определения всех функций, структур и констант Simple MAPI в файле MAPI.H, входящем в состав Microsoft Visual Studio. Его аналогом для Visual Basic является файл MAPI.BAS. Сами функции находятся в динамической библиотеке MAPI.DLL. Как правило Simple MAPI входит в состав клиентских почтовых программ, причем не только работающих в архитектуре (MS Outlook, MS Exchange Client), но и обычных (MS Outlook Express, Eudora Pro, а в скором будущем и The Bat!).

    MAPI 1.0 – для продвинутых

    Simple MAPI на то и simple, что накладывает серьезные ограничения на разработчика как в плане функциональности, так и в плане производительности приложения. Полностью снять эти оковы позволяет гибкий и мощный программный интерфейс MAPI 1.0 (в прошлом – Extended MAPI по аналогии с Simple MAPI). MAPI 1.0 – это совокупность более ста функций и нескольких десятков COM-интерфейсов, предоставляющих программистам на C и C++ богатый инструментарий для создания приложений, работающих с электронной почтой. Simple MAPI можно назвать оберткой MAPI 1.0, которая скрывает множество деталей и нюансов взаимодействия приложений с почтовыми системами.

    MAPI 1.0 предоставляет разработчику не только возможность реализации таких простых функций как отправка или прием почтовых сообщений, но и механизмы для более тесного взаимодействия с отдельными частями систем электронной почты – с адресной книгой, иерархической структурой папок на почтовом сервере, службой транспорта и т.д. Более того, с помощью MAPI 1.0 можно создавать даже части почтовых систем – программные шлюзы, различные службы обработки информации, которые являются частью MAPI-совместимых почтовых серверов. Не будет преувеличением сказать, что, используя MAPI 1.0 можно создать свою собственную клиентскую почтовую программу, аналогичную MS Oulook со всеми ее богатыми возможностями.

    Вместе с тем создание приложений на базе MAPI 1.0 – более трудоемкий процесс, нежели программирование с использованием Simple MAPI. MAPI 1.0 требует от разработчика дополнительной квалификации, в частности знания технологии COM. Перепишем уже имеющийся пример с использованием MAPI 1.0. Для наглядности каждый блок кода сопоставлен с соответствующим фрагментом из предыдущего примера.

    // Begin MAPILogon(:);

    MAPILogonEx(0, "My Profile", NULL, MAPI_NEW_SESSION, &lpSession);

    // End MAPILogon(:);

    lpSession->GetMsgStoresTable(0, &StoresTable);

    HrQueryAllRows(StoresTable, (LPSPropTagArray)&tagDefaultStore, NULL, NULL, 0, &lpRow);

    for(i = 0; i < lpRow -> cRows; i++) {

     if (lpRow->aRow[i].lpProps[0].Value.b == TRUE) break;

    }

    lpSession->OpenMsgStore(0, lpRow->aRow[i].lpProps[1].Value.bin.cb,

     (LPENTRYID)lpRow->aRow[i].lpProps[1].Value.bin.lpb, NULL,

     MDB_WRITE, &lpMDB);

    lpMDB->OpenEntry(lpPropValue->Value.bin.cb, (LPENTRYID)lpPropValue->Value.bin.lpb,

     NULL, MAPI_MODIFY, &ulObjType, (LPUNKNOWN *)&lpFolder);

    lpFolder->CreateMessage(NULL, 0, &lpMsg);

    SInitPropValue MsgProps[] = {

     {PR_DELETE_AFTER_SUBMIT, 0, TRUE},

     {PR_MESSAGE_CLASS, 0, (ULONG)"IPM.NOTE "},

     {PR_SUBJECT, 0, (ULONG)"Greeting"},

     {PR_BODY, 0, (ULONG)" Hello Bill!"}

    };

    lpMsg->SetProps(4, (LPSPropValue)&MsgProps, NULL);


    // Begin MAPIResolveName(:);

    lpSession->OpenAddressBook(0, NULL, AB_NO_DIALOG, &lpAdrBook);

    MAPIAllocateBuffer(CbNewADRLIST(1), (LPVOID*)&lpAdrList);

    MAPIAllocateBuffer(2*sizeof(SPropValue), (LPVOID*)&(lpAdrList->aEntries->rgPropVals));

    ZeroMemory(lpAdrList->aEntries->rgPropVals, 2*sizeof(SPropValue));

    lpAdrList->cEntries = 1;

    lpAdrList->aEntries[0].ulReserved1 = 0;

    lpAdrList->aEntries[0].cValues = 2;

    lpAdrList->aEntries[0].rgPropVals[0].ulPropTag = PR_DISPLAY_NAME;

    lpAdrList->aEntries[0].rgPropVals[0].Value.lpszA = "Bill Gates";

    lpAdrList->aEntries[0].rgPropVals[1].ulPropTag  = PR_RECIPIENT_TYPE;

    lpAdrList->aEntries[0].rgPropVals[1].Value.l = MAPI_TO;

    lpAdrBook->ResolveName(0, 0, NULL, lpAdrList);

    lpMsg->ModifyRecipients(MODRECIP_ADD, lpAdrList);

    // End MAPIResolveName(:);


    // Begin MAPISendMail(:);

    lpMsg->SubmitMessage(0);

    // End MAPISendMail(:);


    // Begin MAPILogoff (:);

    lpSession->Logoff(0, 0, 0);

    // End MAPILogoff (:);

    Как видно из этого примера большинство операций, являющихся примитивными для Simple MAPI, в MAPI 1.0 состоят из последовательностей вызовов тех или иных методов различных интерфейсов. Так, для того, чтобы создать сообщение в MAPI 1.0 требуется получить доступ и открыть контейнер с сообщениями, отыскать в нем папку для исходящих сообщений и только потом собственно создать в ней само сообщение и начинить его всей необходимой информацией. В Simple MAPI все эти промежуточные шаги скрыты от разработчика. С другой стороны MAPI 1.0 позволяет создавать сообщения в любой папке (да и не только сообщения, а объекты календаря, задачи и т.д.). Таким образом, взамен простоте использования появляются новые возможности.

    Интерфейс MAPI 1.0, в отличие от Simple MAPI можно использовать при создании служб Windows NT. Это очень полезное свойство позволяет создавать различного рода почтовые мониторы. Типичная задача почтового монитора может заключаться в сканировании почтового ящика пользователя на предмет поступающей в него корреспонденции, ее разбор, анализ и последующие действия по результатам этого анализа.

    Отдельного обсуждения заслуживает такая возможность MAPI 1.0 как создание всевозможных расширений (extensions) к клиентским программам почтовой системы MS Exchange Server (MS Outlook или MS Exchange Client). Расширения позволяют автоматизировать различные функции обработки сообщений, не реализованные в базовом наборе функций клиентской программы. В частности, механизм расширений позволяет создавать модули для обработки входящих сообщений, так называемые правила (rules), добавлять к клиентской программе дополнительные команды и пункты меню, а также обработчики событий, изменяющие поведение системы при определенных событиях и многое другое.

    CDO – разумный компромисс

    Интерфейс CDO (Collaboration Data Objects), ранее известный как OLE Messaging и Active Messaging представляет собой библиотеку, обеспечивающую доступ приложений к несколько ограниченному набору функция MAPI 1.0 через вызовы Automation. Функции работы с сообщениями могут быть встроены в приложения, созданные с помощью любого средства разработки, являющегося контроллером Automation. К таковым относятся C/C++, Visual Basic, Visual Basic for Applications, VBScript, Javascript. Использование CDO существенно упрощает разработку приложений, работающих с электронной почтой, вместе с тем оставляя разработчику широкие возможности MAPI. Наибольшее применение CDO находит в скриптовых языках. Так, например, в комбинации с APS использование CDO позволяет достаточно легко создать почтового Web-клиента.

    Сухой остаток

    Итак, мы кратко рассмотрели 3 программных интерфейса, позволяющих встраивать в приложения на платформе Windows функциональность для работы с электронной почтой. У каждого из них есть свои преимущества и недостатки. Ничего универсального не существует – окончательный выбор того или иного средства зависит от конкретной задачи и является прерогативой разработчика. Более подробную информацию на эту тему можно получить на http://msdn.microsoft.com/library/psdk/mapi/.

    ВОПРОС-ОТВЕТ 

    Q. Как создать окно ввода текста переключаясь в которое устанавливался бы заданный язык. Например, необходим ввод только русских слов в строке, по которой ищется перевод на английский, а язык по умолчанию в виндовс английский. Хотелесь бы при запуске программы, когда пользователь ткнет мышкой в поле ввода, чтобы он не переключал язык по умолчаню на русский.

    (Alexander Shinkevich )

    A1 Для переключения раскладок необходимо вызвать функцию LoadKeyboardLayout. 

    Ниже приводится пример ее использования: 

    1) Добавить в проект с помощью Class Wizard'а новый класс CMyEdit на основе CEdit.

    2) Добавить в класс переменную, хранящую предыдущую установленную раскладку клавиатуры: 

    TCHAR m_PreviousLayout[KL_NAMELENGTH];

    3) Добавить обработчики WM_SETFOCUS и WM_KILLFOCUS: 

    void CMyEdit::OnSetFocus(CWnd* pOldWnd) {

     CEdit::OnSetFocus(pOldWnd);

    // запоминаем предыдущую раскладку клавиатуры

     ::GetKeyboardLayoutName(m_PreviousLayout);

     // устанавливаем новую раскладку для языка "Русский"

     ::LoadKeyboardLayout(_T("00000419"), KLF_ACTIVATE);

    }


    void CMyEdit::OnKillFocus(CWnd* pNewWnd) {

     CEdit::OnKillFocus(pNewWnd);

     // восстанавливаем предыдущую раскладку клавиатуры

     ::LoadKeyboardLayout(m_PreviousLayout, KLF_ACTIVATE);

    }

    4) Использовать CMyEdit вместо CEdit (на примере диалога): 

    class CMyDlg : public CDialog {

     // ...

     CMyEdit m_Edit;

     // ...

    };


    void CMyDlg::DoDataExchange(CDataExchange* pDX) {

     CDialog::DoDataExchange(pDX);

     //{{AFX_DATA_MAP(CTestKeyboardDlg)

     DDX_Control( pDX, IDC_EDIT, m_Edit );

     //}}AFX_DATA_MAP

    }

    (Алексей Гончаров )

    A2 […] Также можно активизировать т.н. keyboard layout (раскладку клавиатуры) с помощью функции ActivateKeyboardLayout, активизирующей раскладку, загруженную предварительно с помощью указанной выше функции LoadKeyboardLayout. Хотя LoadKeyboardLayout сама может активизировать раскладку (при использовании флага KLF_ACTIVATE), но при частой смене языка оптимальнее использовать ActivateKeyboardLayout. Т.е. в начале загрузить раскладку с помощью LoadKeyboardLayout, а многократно переключать язык ввода функцией ActivateKeyboardLayout.

    (Igor Sukharev )
    В ПОИСКАХ ИСТИНЫ 

    Q. Можно ли из моей программы управлять окном которое создано другим приложением (закрывать, сворачивать, нажимать в нем кнопки и т.д.), если да то как?

    (Alhim )

    А на сегодня это все. До встречи через неделю! 

    (Алекс Jenter jenter@mail.ru) (Красноярск, 2001.)







    Главная | В избранное | Наш E-MAIL | Прислать материал | Нашёл ошибку | Наверх