При автоматическом формировании и отправке электронных писем из 1С через COM-объект Outlook часто возникает необходимость включить стандартную подпись пользователя, которая автоматически добавляется при создании нового письма в самом Outlook. Однако, как показывает практика, при программном задании содержимого письма через свойство Body или HTMLBody, эта автоматическая подстановка подписи перестает работать. Мы разберем, почему так происходит, и предложим несколько эффективных способов решения этой задачи.
Когда вы создаете новый объект письма MailItem в Outlook через 1С и затем вызываете метод Display(), Outlook самостоятельно подставляет в тело письма подпись, настроенную для учетной записи по умолчанию. Это происходит потому, что Outlook применяет свои внутренние правила формирования письма, включая добавление подписи, именно в момент инициализации отображения письма.
Однако, если вы программно устанавливаете свойства Письмо.Body = "Текст вашего письма"; или Письмо.HTMLBody = "<html><body>Текст вашего письма</body></html>"; до вызова Display() или сразу отправляете письмо методом Письмо.Send(), то Outlook считает, что тело письма уже полностью сформировано вами. В этом случае он не добавляет стандартную подпись автоматически, поскольку считает, что вы взяли эту функцию на себя.
Таким образом, наша задача заключается в том, чтобы либо "обмануть" Outlook, заставив его вставить подпись, а затем получить её, либо самостоятельно найти подпись пользователя и добавить её в письмо.
Один из наиболее надежных способов — это программное чтение файла подписи пользователя и последующая вставка его содержимого в тело письма. Мы рассмотрим этот подход по шагам.
Подписи Outlook хранятся в специальной папке на компьютере пользователя. Обычно это %userprofile%\AppData\Roaming\Microsoft\Signatures. Каждая подпись представлена тремя файлами с одинаковым именем, но разными расширениями:
.htm: HTML-версия подписи (наиболее полная, содержит форматирование, изображения)..rtf: RTF-версия подписи..txt: Текстовая версия подписи.Для корректного отображения форматированной подписи в письме нам потребуется содержимое .htm файла.
У пользователя может быть несколько подписей. Чтобы узнать, какая именно подпись установлена по умолчанию для новых писем, нам понадобится обратиться к реестру Windows. Информация о подписи по умолчанию хранится по пути:
HKEY_CURRENT_USER\Software\Microsoft\Office\<Версия_Office>\Outlook\Profiles\<Имя_профиля>\0a0d020000000000c000000000000046
Здесь:
<Версия_Office>: Зависит от установленной версии Microsoft Office (например, 14.0 для Office 2010, 15.0 для Office 2013, 16.0 для Office 2016/2019/365). Мы можем попытаться определить её, перебирая возможные ветки или используя WMI.<Имя_профиля>: Обычно это "Outlook", но может быть другим, если пользователь создал несколько профилей.В этой ветке реестра нас интересуют значения NewSignature (для новых писем) и ReplySignature (для ответов/пересылки). Эти значения содержат имя файла подписи (без расширения). Например, если там указано "МояПодпись", то файл будет МояПодпись.htm.
Рассмотрим пример кода 1С для определения пути к подписям и чтения имени подписи из реестра:
// Функция для получения имени пользователя Windows
Функция ПолучитьИмяПользователяWindows()
Попытка
WshShell = Новый COMОбъект("WScript.Shell");
Возврат WshShell.ExpandEnvironmentStrings("%USERNAME%");
Исключение
Возврат "";
КонецПопытки;
КонецФункции
// Функция для получения версии Office из реестра
Функция ПолучитьВерсиюOfficeOutlook()
Попытка
WshShell = Новый COMОбъект("WScript.Shell");
// Пробуем наиболее распространенные версии Office
Для Каждого Версия Из {"16.0", "15.0", "14.0"} Цикл
Если WshShell.RegExists("HKEY_CURRENT_USER\Software\Microsoft\Office\" + Версия + "\Outlook\") Тогда
Возврат Версия;
КонецЕсли;
КонецЦикла;
Возврат ""; // Версия не найдена
Исключение
Возврат "";
КонецПопытки;
КонецФункции
// Функция для получения имени подписи по умолчанию из реестра
Функция ПолучитьИмяПодписиПоУмолчанию(ТипПодписи = "NewSignature")
// ТипПодписи: "NewSignature" или "ReplySignature"
Перем ИмяПодписи;
ИмяПодписи = "";
ВерсияOffice = ПолучитьВерсиюOfficeOutlook();
Если ПустаяСтрока(ВерсияOffice) Тогда
Сообщить("Не удалось определить версию Office Outlook.");
Возврат "";
КонецЕсли;
Попытка
WshShell = Новый COMОбъект("WScript.Shell");
ПутьКПрофилю = "HKEY_CURRENT_USER\Software\Microsoft\Office\" + ВерсияOffice + "\Outlook\Profiles\";
// Обычно профиль называется "Outlook", но может быть и другой
// Можем попробовать получить список профилей, но для простоты предположим "Outlook"
ИмяПрофиля = "Outlook"; // Можно усложнить и искать активный профиль
ПутьКРееструПодписи = ПутьКПрофилю + ИмяПрофиля + "\0a00020000000000c000000000000046\"; // Обратите внимание на 0a00020000000000c000000000000046
// Для Outlook 2010 и выше, этот ключ может меняться.
// Более универсальный подход - искать его в ветке "Mail"
// Но чаще всего используется ключ вида "0a0d020000000000c000000000000046" или "0a00020000000000c000000000000046"
// Проверим, существует ли нужный ключ
Если WshShell.RegExists(ПутьКРееструПодписи) Тогда
ИмяПодписи = WshShell.RegRead(ПутьКРееструПодписи + ТипПодписи);
Иначе
Сообщить("Ключ реестра для подписей не найден по стандартному пути. Возможно, профиль другой или версия Outlook.");
КонецЕсли;
Исключение
Сообщить("Ошибка при чтении реестра для получения имени подписи: " + ОписаниеОшибки());
КонецПопытки;
Возврат ИмяПодписи;
КонецФункции
// Пример использования:
ИмяПользователя = ПолучитьИмяПользователяWindows();
ИмяПодписиПоУмолчанию = ПолучитьИмяПодписиПоУмолчанию("NewSignature"); // Получаем имя подписи для новых писем
Если Не ПустаяСтрока(ИмяПодписиПоУмолчанию) Тогда
Сообщить("Имя подписи по умолчанию: " + ИмяПодписиПоУмолчанию);
Иначе
Сообщить("Не удалось получить имя подписи по умолчанию.");
КонецЕсли;
После того как мы определили имя подписи, нам нужно построить полный путь к .htm файлу подписи. Используем имя пользователя Windows для формирования пути к папке Signatures.
// ... (Код из Шага 2 для получения ИмяПользователя и ИмяПодписиПоУмолчанию) ...
// Формируем полный путь к папке подписей
ПутьКПапкеSignatures = "C:\Users\" + ИмяПользователя + "\AppData\Roaming\Microsoft\Signatures\";
ИмяФайлаПодписиHTML = ПутьКПапкеSignatures + ИмяПодписиПоУмолчанию + ".htm";
Перем HTMLПодписьТекст;
HTMLПодписьТекст = "";
Если ФайлСуществует(ИмяФайлаПодписиHTML) Тогда
Попытка
ТекстовыйДокумент = Новый ТекстовыйДокумент;
ТекстовыйДокумент.Прочитать(ИмяФайлаПодписиHTML);
HTMLПодписьТекст = ТекстовыйДокумент.ПолучитьТекст();
Исключение
Сообщить("Ошибка при чтении файла подписи: " + ОписаниеОшибки());
КонецПопытки;
Иначе
Сообщить("Файл подписи '" + ИмяФайлаПодписиHTML + "' не найден.");
КонецЕсли;
Если Не ПустаяСтрока(HTMLПодписьТекст) Тогда
Сообщить("HTML-код подписи успешно прочитан.");
Иначе
Сообщить("HTML-код подписи пуст или не удалось прочитать.");
КонецЕсли;
Теперь, когда у нас есть HTML-код подписи, мы можем вставить его в свойство HTMLBody нашего письма. Важно учесть, что файл .htm подписи может содержать полный HTML-документ (с тегами <html>, <head>, <body>). Обычно для вставки в уже существующий HTMLBody письма нам нужна только часть, находящаяся внутри тега <body>.
Рассмотрим пример формирования письма с учетом HTML-подписи:
// ... (Код из Шага 3 для получения HTMLПодписьТекст) ...
Попытка
Outlook = Новый COMОбъект("Outlook.Application");
Исключение
Сообщить("Не удалось подключиться к Outlook: " + ОписаниеОшибки());
Возврат;
КонецПопытки;
Письмо = Outlook.CreateItem(0); // olMailItem
Письмо.Recipients.Add("получатель@example.com"); // Добавляем получателя
// Письмо.CC.Add("копия@example.com"); // Добавляем копию
// Письмо.BCC.Add("скрытая_копия@example.com"); // Добавляем скрытую копию
Письмо.Subject = "Тема нашего автоматического письма из 1С";
Письмо.BodyFormat = 2; // olFormatHTML - указываем, что тело будет в HTML
// Формируем основной текст письма в HTML
ОсновнойТекстПисьмаHTML = "<p><strong>Уважаемый(ая) коллега!</strong></p>" +
"<p>Информируем Вас о важных событиях в нашей компании.</p>" +
"<p>Подробности смотрите во вложенном файле.</p>" +
"<p>С уважением,</p>";
// Парсим HTML-подпись, чтобы извлечь только содержимое тега <body>
// Это важно, чтобы избежать вложенных <html><body> тегов, что может привести к некорректному отображению
Перем HTMLПодписьТолькоТело;
HTMLПодписьТолькоТело = "";
Если Не ПустаяСтрока(HTMLПодписьТекст) Тогда
НачалоBody = СтрНайти(HTMLПодписьТекст, "<body");
Если НачалоBody > 0 Тогда
КонецBody = СтрНайти(HTMLПодписьТекст, "</body>", НачалоBody);
Если КонецBody > 0 Тогда
НачалоBody = СтрНайти(HTMLПодписьТекст, ">", НачалоBody) + 1; // Ищем конец открывающего тега <body>
HTMLПодписьТолькоТело = Сред(HTMLПодписьТекст, НачалоBody, КонецBody - НачалоBody);
Иначе
// Если </body> не найден, берем от <body ... > до конца
НачалоBody = СтрНайти(HTMLПодписьТекст, ">", НачалоBody) + 1;
HTMLПодписьТолькоТело = Сред(HTMLПодписьТекст, НачалоBody);
КонецЕсли;
Иначе
// Если тег <body> не найден в файле, используем весь HTML как есть
HTMLПодписьТолькоТело = HTMLПодписьТекст;
КонецЕсли;
КонецЕсли;
// Собираем полное HTML-тело письма
ПолноеТелоПисьмаHTML = "<html><body>" + ОсновнойТекстПисьмаHTML;
Если Не ПустаяСтрока(HTMLПодписьТолькоТело) Тогда
ПолноеТелоПисьмаHTML = ПолноеТелоПисьмаHTML + HTMLПодписьТолькоТело;
КонецЕсли;
ПолноеТелоПисьмаHTML = ПолноеТелоПисьмаHTML + "</body></html>";
Письмо.HTMLBody = ПолноеТелоПисьмаHTML;
// Добавляем вложения, если необходимо
// ФайлВложение = "C:\Путь\К\Вашему\Файлу.pdf";
// Если ФайлСуществует(ФайлВложение) Тогда
// Письмо.Attachments.Add(ФайлВложение);
// КонецЕсли;
// Отображаем письмо для проверки (опционально)
// Письмо.Display();
// Отправляем письмо
Письмо.Send();
Сообщить("Письмо успешно отправлено с подписью.");
Этот метод более динамичен, так как он заставляет Outlook вставить подпись, а затем мы ее извлекаем. Он полезен, если вы не хотите разбираться с путями в реестре или если у пользователя сложные подписи с динамическими элементами.
Мы создадим временное письмо, вызовем для него Display(), чтобы Outlook автоматически вставил подпись, а затем извлечем эту подпись.
Попытка
Outlook = Новый COMОбъект("Outlook.Application");
Исключение
Сообщить("Не удалось подключиться к Outlook: " + ОписаниеОшибки());
Возврат;
КонецПопытки;
// Шаг 1: Создаем временное письмо для извлечения подписи
// ВАЖНО: Мы НЕ устанавливаем свойства Body или HTMLBody,
// чтобы Outlook сам вставил подпись при Display().
ВременноеПисьмо = Outlook.CreateItem(0); // olMailItem
ВременноеПисьмо.Subject = "Временное письмо для извлечения подписи (не будет отправлено)";
// Отображаем письмо. Outlook автоматически вставит подпись.
// Пользователь может увидеть вспышку окна, если Outlook не на переднем плане.
ВременноеПисьмо.Display();
Перем HTMLПодписьИзВременногоПисьма;
HTMLПодписьИзВременногоПисьма = "";
Попытка
// Получаем доступ к Инспектору письма (окно, в котором оно открыто)
Инспектор = ВременноеПисьмо.GetInspector;
// Получаем доступ к редактору Word, который используется Outlook
// Это объект Document из библиотеки Microsoft Word
РедакторWord = Инспектор.WordEditor;
// Извлекаем HTML-содержимое из редактора Word.
// На этом этапе HTML уже будет содержать подпись, вставленную Outlook.
// Это будет полный HTML-документ, включая теги <html><body>...</body></html>
ПолныйHTMLСПодписью = РедакторWord.Content.HTML;
// Извлекаем только содержимое тега <body>, которое нам нужно для вставки
НачалоBody = СтрНайти(ПолныйHTMLСПодписью, "<body");
КонецBody = СтрНайти(ПолныйHTMLСПодписью, "</body>", НачалоBody);
Если НачалоBody > 0 И КонецBody > 0 Тогда
НачалоBody = СтрНайти(ПолныйHTMLСПодписью, ">", НачалоBody) + 1; // Конец открывающего тега <body>
HTMLПодписьИзВременногоПисьма = Сред(ПолныйHTMLСПодписью, НачалоBody, КонецBody - НачалоBody);
Иначе
// Если парсинг не удался, берем весь HTML, хотя это может быть некорректно
HTMLПодписьИзВременногоПисьма = ПолныйHTMLСПодписью;
Сообщить("Внимание: Не удалось корректно распарсить HTML подписи из временного письма. Вставляется полный HTML.");
КонецЕсли;
// Закрываем временное письмо без сохранения изменений
ВременноеПисьмо.Close(0); // 0 = olDiscard (выбросить изменения)
Исключение
Сообщить("Ошибка при извлечении подписи из временного письма: " + ОписаниеОшибки());
// Если произошла ошибка, убедимся, что окно письма закрыто
ВременноеПисьмо.Close(0);
Возврат;
КонецПопытки;
Если Не ПустаяСтрока(HTMLПодписьИзВременногоПисьма) Тогда
Сообщить("Подпись успешно извлечена из временного письма.");
Иначе
Сообщить("Не удалось извлечь подпись из временного письма. Возможно, подпись не настроена.");
КонецЕсли;
Теперь, когда у нас есть HTML-код подписи, мы можем использовать его для формирования основного письма, которое будет отправлено.
// ... (Код из Шага 1 для получения HTMLПодписьИзВременногоПисьма) ...
// Шаг 2: Создаем основное письмо и вставляем подпись
ПисьмоОсновное = Outlook.CreateItem(0); // olMailItem
ПисьмоОсновное.Recipients.Add("получатель@example.com");
ПисьмоОсновное.Subject = "Ваше важное письмо с динамической подписью";
ПисьмоОсновное.BodyFormat = 2; // olFormatHTML - обязательно для HTML-подписи
// Формируем основное тело письма
ОсновнойТекстПисьмаHTML = "<p><i>Уважаемые партнеры!</i></p>" +
"<p>Сообщаем о новых условиях сотрудничества.</p>" +
"<p>При возникновении вопросов, пожалуйста, свяжитесь с нами.</p>";
// Объединяем основной текст письма и извлеченную подпись
ПолноеТелоДляОтправки = "<html><body>" + ОсновнойТекстПисьмаHTML;
Если Не ПустаяСтрока(HTMLПодписьИзВременногоПисьма) Тогда
ПолноеТелоДляОтправки = ПолноеТелоДляОтправки + "<br>" + HTMLПодписьИзВременногоПисьма; // Добавляем разрыв строки перед подписью
КонецЕсли;
ПолноеТелоДляОтправки = ПолноеТелоДляОтправки + "</body></html>";
ПисьмоОсновное.HTMLBody = ПолноеТелоДляОтправки;
// Добавляем вложения, если необходимо
// ПисьмоОсновное.Attachments.Add("C:\Путь\К\Вашему\Другому\Файлу.docx");
// Отправляем письмо без показа пользователю
ПисьмоОсновное.Send();
Сообщить("Основное письмо успешно сформировано и отправлено с извлеченной подписью.");
HTMLBody: Для подписей, содержащих форматирование, изображения или ссылки, всегда используйте свойство HTMLBody вместо Body. Убедитесь, что Письмо.BodyFormat = 2; (olFormatHTML) установлено.WordEditor.Content.HTML, вы можете получить полный HTML-документ, включая теги <html>, <head> и <body>. Для корректной вставки в уже формируемое тело письма, критически важно извлечь только содержимое, которое находится внутри тега <body>. Мы показали примеры простого извлечения, но для очень сложных HTML-структур может потребоваться более продвинутый парсинг.Попытка...Исключение...КонецПопытки при работе с COM-объектами, чтобы корректно обрабатывать возможные ошибки (например, Outlook не запущен, COM-объект не создан, файл подписи не найден).WordEditor может быть немного медленнее, так как требует создания и временного отображения письма. Для очень большого количества писем метод с чтением из файла может быть более эффективным, при условии корректного определения подписи по умолчанию._files рядом с .htm файлом подписи. При вставке HTML-кода эти изображения будут корректно отображаться, если пути к ним в HTML-коде подписи относительны и подпапка доступна.Выбирайте метод, который лучше всего соответствует вашим требованиям к надежности, производительности и удобству. Оба подхода позволяют успешно решить проблему отсутствия подписи при программном формировании писем из 1С в Outlook.