Статьи о почте

Новости

Все новости

Разное в мире IT

Все заметки

mailinfo.ru - Статьи о почте

Интеграция Microsoft Outlook c другими приложениями Office

Прислал(а) Максим Грибков [12 января 2002]

раздел: [Почтовые клиенты]

Введение

С развитием интернет-технологий у рядовых пользователей все чаще и чаще возникает необходимость в отправке большого количества данных посредством E-mail. Обычно эти данные необходимо отправлять каждый день или чаще. В подавляющем большинстве случаев, пользователи просто набивают их вручную и отправляют посредством своего любимого почтового клиента, например, Microsoft Outlook, входящей в комплект поставки Microsoft Office. Так, постепенно, электронная почта превращается из развлечения в инструмент для работы. Еще чаще случается, что обрабатываемые данные (прайс-листы, списки, таблицы, другие структурированные данные) хранятся и обрабатываются посредством других приложений Office. Было бы логично автоматизировать процесс обработки, подготовки данных, а затем их отправку по E-mail. Microsoft Office и VBA позволяет это сделать легко и эффективно.

Я не буду останавливаться на вопросах обработки данных - это конкретная задача каждого программиста. Речь пойдет лишь о том, как лучше подключиться к объектам Outlook, создать новое письмо и передать в него необходимый текст. Решая такие задачи в течении года, я пришел к выводу, что несмотря на то, что все программы Office интегрированы между собой - интеграция типа Excel-Outlook или Word-Outlook далеко не такая эффективная как Excel-Word. Проблемы возникают именно там, где их совсем не ждешь.

Начнем с различия версий Office 2000 и Office XP. Во-первых, в Office XP любое программное обращение к объектам Outlook перехватывается службой безопасности с выдачей соответствующего сообщения, где пользователю предлагается сделать выбор между "разрешить доступ" и "доступ отклонить". В последнем случае в VBA возвращается соответствующая ошибка. В Office 2000 никаких предупреждающих сообщений не выскакивает.

На рисунке приведено предупреждение о попытке доступа к адресной книге Outlook.

Во-вторых, в Office XP при передачи данных в HTML формате имеет место следующая проблема: письмо нормально формируется, текст в тело письма передается, но... не сохраняется. В тоже время, если перед отправкой окно с сформированным письмом программно вывести на просмотр и вручную внести в него какие-либо, пусть даже, незначительные изменения (достаточно добавить пробел), то сообщение сохраняется нормально. Думается, что это определенные требования современной безопасности (не хочется верить, что это ошибка). Памятуя о различных вирусных атаках, стоит заметить, что такое поведение Outlook очень даже кстати - ни одно письмо не уйдет без проверки и подтверждения его содержания пользователем. Если быть совсем точным, то письмо в формате HTML все же сформируется и будет отослано адресату, но текста сообщения (тело письма) будет автоматически удалено. (Речь идет, естественно, о программном формировании письма). К сожалению, для программистов на VBA это оборачивается определенными проблемами. Одно дело, если нужно передать просто текст, а другое дело, если текст нужно отформатировать… здесь и появляются первые проблемы.

В-третьих, в Office 2000 и Office XP данные в тело письма можно передавать только в виде обычного текста и текста HTML, но в Office XP, наконец, появилось свойство BodyFormat, при помощи которого указать формат, как будущему, так уже созданному сообщению. Есть возможность выбрать между: текстовым сообщением, HTML, и Rich Text (расширенный формат типа RTF). Последний формат, RTF, имеет ограниченную читаемость другими почтовыми клиентами, поэтому его рекомендуется использовать только в тех случаях, когда заранее известно, что у адресатов имеется Microsoft Outlook 98 и старше. С другой стороны, наличие свойства BodyFormat позволяет программисту передать текст в сообщение в формате RTF (со всеми его возможностями оформления текста), а затем переформатировать его в формат HTML. Здесь есть только одна трудность - работать с форматом RTF не так удобно как с HTML и простым текстом. Именно появление этого свойства в Office XP является основной причиной, по которой следует прейти на эту версию Office.

Далее я буду рассматривать вопросы программирования, ориентируясь на Office XP, для программистов в среде Office 2000 это будет также полезно почитать, так как 99% программного кода работоспособно в этой платформе. однажды, вы без труда сможете создать связь и на базе любых приложений Office. Я также рассчитываю, что вы имеете достаточный опыт работы в среде VBA.

Подключение к Outlook

Само по себе подключение к Outlook можно включить в обработку события нажатия на кнопку или в любую другую процедуру. Здесь все зависит только от конкретной задачи.

Вам доступно два способа подключения, которые называются ранее и позднее связывание. А самое, интересное, что можно использовать эти два способа сразу. Ничего страшного в этом нет. Обычно, ранее связывание удобно тем, что в дальнейшем при написании кода будут вываливаться все, привычные, подсказки и списки методов и свойств объектов Outlook. Я, обычно, использую ранее связывание.

Для организации связывания (как раннего, так и позднего) следует установить соответствующую галочку в окне References.

В код следует включить следующую строку описания переменной

Dim mailApp As Outlook.Application

(В случае позднего связывания переменную mailApp следует описать как Dim mailApp As Object. В дальнейшем никакой разницы с точки зрения программирования нет. Отличие заключается лишь в том, что в случае раннего связывания подключение связываемых объектов происходит на стадии компиляции (читай "на стадии проектирования"), а в случае позднего - подключение к объектам происходит на стадии исполнения программы. И тот и другой способ имеет недостатки, но для современных компьютеров, имеющих достаточный объем оперативной памяти и достаточную производительность процессора, изменения в производительности будут не столь незаметны. В любом случае, предпочтительнее использовать ранее связывание.)

Для получения доступа к объектам Outlook используется два оператора VBA

Set mailApp = GetObject(, "Outlook.Application")

и

Set mailApp = CreateObject("Outlook.Application")

Первый вариант рекомендуется в том случае, если Outlook уже запущена и находится в памяти компьютера. Второй - в случае, если с Outlook, ее нужно сначала открыть. Понято, что функция GetObject выполняется быстрее, так как не создавая объекта получает ссылку на него. Функция CreateObject, всегда, создает новый объект и формирует соответствующую ссылку на него, при этом сам Outlook не появляется на экране (естественно, объект в любой момент можно показать на экране). В ряде случаев всегда проще использовать функцию CreateObject, но такой подход не эффективен - не стоит создавать копию того или иного объекта, если он уже создан (загружен ранее программно или пользователем). Используя средства VBA и API можно проверить, загружена ли Outlook в данный момент или нет.

Описание функции API:

Declare Function FindWindowByClass Lib "user32" _
    Alias "FindWindowA" (ByVal lpClassName As String, _
    ByVal lpWindowName As Long) As Long

Код для проверки:

Dim lngRetVal As Long

\'поиск окна Microsoft Outlook
    lngRetVal = FindWindowByClass("rctrl_renwnd32", 0&)

Имя класса значится как rctrl_renwnd32. Так было задумано программистами Microsoft и название класса не меняется в течение нескольких поколений Oitlook, думаю, что в целях совместимости, так будет и дальше.

Функция API FindWindowByClass возвращает 0 если окно не найдено, во всех остальных случаях возвращается дескриптор найденного окна.

В зависимости о того, найдено окно или нет, выполняем вызов функции GetObject или CreateObject.

If lngRetVal <> 0 Then
    Set mailApp = GetObject(, "Outlook.Application")
Else
    Set mailApp = CreateObject("Outlook.Application")
End If

Не думаю, что этот код вызовет много затруднений, его можно просто взять и скопировать.

На этом, собственно, подключение к Outlook закончено. Вы можете получать доступ к различным папкам, приложения, создавать новые элементы в них и выполнять другие действия. К сожалению, программист не может изменять большинство параметров настройки Outlook, но кое-что сделать можно. Это относится также и к создаваемым новым сообщениям. Например, нельзя программно изменить шрифт, устанавливаемый как "шрифт по-умолчанию" для вновь создаваемых сообщений.

Создаем новое сообщение:

Set objMail = mailApp.CreateItem(olMailItem)

Добавляем E-mail в поле Кому для созданного сообщения:

objMail.Recipients.Add "asd@ion.ru"

Естественно, можно использовать строковую переменную, содержащую нужный адрес. Можно указать несколько адресов через точку с запятой.

Для указания E-mail для поля Копия используйте следующую конструкцию:

dim dfg As Object
Set dfg = objMail.Recipients.Add("as@as.ru")
dfg.Type = olCC
\'используйте olBCC вместо olCC для отправки скрытой копии

Интересно, что в некоторых случаях, адреса, помеченные как Копия и Скрытая копия, могут попадать, на начальном этапе формирования письма, в поле Кому, но после сохранения (или отправки) письма все адреса попадут именно туда куда надо.

Укажем тему сообщения:

objMail.Subject = "Письмо"

Вот, собственно говоря, и все! Осталось только записать тело самого сообщения. Будем считать, что уже сформатированный и подготовленный текст записан в переменную BodyText. О том как сформировать текст и отформатировать его, можно написать отдельную статью, и если у вас возникают проблемы при работе со строками, то обратитесь к другой литературе.

Важное замечание, по ходу дела! Естественно, как и любой уважающий себя почтовый клиент, Microsoft Outlook умеет работать с невообразимым количеством кодировок. Для переписки, пользователи, используют, как правило, две кодировки: Windows-1251 (основная) и KOI-8R (доставшаяся нам в наследие от Linux). Не менее очевиден тот факт, что в VBA строковые переменные создаются в кодировке Win-1251. Поэтому, при желании и необходимости, можно написать компактную, но эффективную процедуру перекодировки текста. В сети имеется много информации, относящейся к различным кодировкам и написать такую процедуру не проблема. Будьте внимательны, различие кодировок - очередной подводный камень Microsoft Outlook. На первый взгляд, проще всего, использовать кодировку Win-1251, но Outlook, по-умолчанию настроена на использование кодировки KOI-8R (чувствуете какие заморочки!), поэтому для использования, разработанного вами приложения, Outlook следует перенастроить на кодировку Win-1251 (или сразу же писать перекодировщика).

\'запись текста в тело письма в текстовом формате
objMail.Body = BodyText


\'запись текста в формате HTML
objMail.HTMLBody=BodyText

Вы можете использовать любые доступные тэги HTML. В начале я писал о том, что Outlook XP не сохраняет автоматически тело письма, переданное в формате HTML. Поэтому применение данного формата несколько ограничено.

Для изменение формата уже сформированного письма используйте следующий код:

objMail.BodyFormat = olFormatPlain \'просто текст
objMail.BodyFormat = olFormatHTML \'формат HTML
objMail.BodyFormat = olFormatRichText \'формат RTF

Теперь, чтобы сразу отправить сформированное письмо применяйте метод Send.

objMail.Send

Для предварительного просмотра письма используйте метод Display.

objMail.Display

В конце процедуры обработки следует закрыть сеансы Outlook

Set objMail = Nothing
Set mailApp = Nothing

Вот и все! Очень даже просто, а главное, до безобразия эффективно.

И все же, несмотря на простоту, очень хотелось бы использовать формат HTML для формирования и отправки сообщения. Для пользователей Office 2000 здесь нет никаких проблем, а в случае с Office XP придется либо смериться с тем, что сообщение не сохраняется и для нормальной отправки (сохранения) письма, необходимо вмешательство пользователя, либо писать дополнительные модули и библиотеки. Единственно, что мне приходит в голову - это использовать оператор SendKey и попробовать передать в Outlook нужную комбинацию нажатий клавиш, но такой подход крайне не эффективен и не надежен. Конечно, Microsoft не зря создавала ограничения и обойти их такой элементарщиной вряд ли удастся.

Подключение к папке Контакты Microsoft Outlook

Работа пользователя, использующего ваше приложение, будет еще эффективнее, если программа будет сама получать список абонентов из папки Контакты Microsoft Outlook и предоставлять его в удобном для использовании виде. Сложного ничего в этом нет, поэтому начнем программировать. Надеюсь, что с подходящим интерфейсом списка вы справитесь сами.

Получаем объект Outlook:

lngRetVal = FindWindowByClass("rctrl_renwnd32", 0&)

If lngRetVal <> 0 Then
    Set myOlApp = GetObject(, "Outlook.Application")
Else
    Set myOlApp = CreateObject("Outlook.Application")
End If

- это все также как и раньше.

dim myNameSpace As Object
dim myfolder As Object

\'получаем хранилище сообщений MAPI.
Set myNameSpace = myOlApp.GetNamespace("MAPI")

Грубо говоря, все папки Outlook (входящие, отправленные, черновики и так далее), - это хранилище MAPI.

\'получаем папку Контакты из хранилища сообщений MAPI.
Set myfolder = myNameSpace.GetDefaultFolder(olFolderContacts)

Dim i As Long
\'перебираем папку Контакты и читаем нужные нам сведения
For i = 1 To myfolder.Items.Count \' цикл по всем записям в папке
    Me.ListBox1.AddItem (myfolder.Items(i)) \'заполняем ListBox
    Me.ListBox1.List(i - 1, 1) = myfolder.Items(i).Email1Address
    Me.ListBox1.List(i - 1, 2) = myfolder.Items(i).BusinessAddress
Next i

У каждого объекта папки Контакты огромное количество различных свойств. Их полное описание ищите в справочной системе. Я ограничился лишь тремя: название объекта (читай "наименование"), первый E-mail (читай основной), город и основного адреса (читай "рабочего"). В принципе - это все! Естественно перед тем как получить доступ к папке Контакты придется ответить на соответствующее предупреждающее сообщение. Если обращение к папке Контакты в процессе работы происходит часто, то каждый раз отвечать на вопросы безопасности неудобно. Здесь на помощь приходит возможность использования динамических массивов.

Создаем глобальный массив:

Public klients()
ReDim klients(myfolder.Items.Count, 3) \'выделяем память
klients = Me.ListBox1.List() \'заполняем массив данными

Обратите внимание, насколько просто скопировать информацию в массив из ListBox.

Теперь в случае, если пользователь снова обратиться к адресной книге (папке Контакты), то вместо фактической информации можно подставить информацию из массива:

Me.ListBox1.List() = klients

Снова обратите внимание, - насколько все просто.

Естественно, содержимое папки Контакты может измениться и содержимое массива, в таком случае, не будет отражать фактическое положение дел. Следует отметить, что содержимое папки Контакты меняется не так уж и часто, но рекомендую предусмотреть кнопку, нажатие на которую вызывает обновление информации в массиве.

Массив, вообще, отбирая дополнительную, часто драгоценную память, работает в несколько десятков раз быстрее, чем непосредственный доступ к папке Контакты Microsoft Outlook и не вызывает активизации системы безопасности, что делает его применение крайне удобно. В конце следует закрыть сеансы Outlook

Set objMail = Nothing
Set mailApp = Nothing

Обработка ошибок

В заключение скажу несколько слов об обработке ошибок при программировании объектов. Для большинства случаев достаточно отловить небольшое количество ошибок.

Select Case Err.Number
\'возникает в случае, если пользователь ответил Нет на сообщение
\'безопасности. Интересно, что эта ошибка возникает только в том
\'случае, если письмо программно отправляется методом Send,
\'независимо от того, каким образом получен доступ к Outlook. Если
\'быть совсем точным, то эта ошибка не имеет никакого отношения к
\'безопасности, она возникает в том случае, если программа не может
\'получить доступ к приложению или его объекту. Такое может
\'случиться по разным причинам.
Case 287
MsgBox "Ошибка"

\'возникает в случае, если программа не может создать ссылку на
\'объект, например, если Outlook не установлена.
Case 429
"Ошибка"

\'ошибка, возвращаемая службой безопасности, в случае ответа Нет на
\'запрос
Case -2147467259
MsgBox "Ошибка"

\'любая другая, не обрабатываемая ошибка
Case Else
MsgBox "Ошибка"
End Select

Все! Удачи!