Как программно добавить подпись пользователя Outlook в письмо, формируемое из 1С?

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

При автоматическом формировании и отправке электронных писем из 1С через COM-объект Outlook часто возникает необходимость включить стандартную подпись пользователя, которая автоматически добавляется при создании нового письма в самом Outlook. Однако, как показывает практика, при программном задании содержимого письма через свойство Body или HTMLBody, эта автоматическая подстановка подписи перестает работать. Мы разберем, почему так происходит, и предложим несколько эффективных способов решения этой задачи.

В чем заключается проблема?

Когда вы создаете новый объект письма MailItem в Outlook через 1С и затем вызываете метод Display(), Outlook самостоятельно подставляет в тело письма подпись, настроенную для учетной записи по умолчанию. Это происходит потому, что Outlook применяет свои внутренние правила формирования письма, включая добавление подписи, именно в момент инициализации отображения письма.

Однако, если вы программно устанавливаете свойства Письмо.Body = "Текст вашего письма"; или Письмо.HTMLBody = "<html><body>Текст вашего письма</body></html>"; до вызова Display() или сразу отправляете письмо методом Письмо.Send(), то Outlook считает, что тело письма уже полностью сформировано вами. В этом случае он не добавляет стандартную подпись автоматически, поскольку считает, что вы взяли эту функцию на себя.

Таким образом, наша задача заключается в том, чтобы либо "обмануть" Outlook, заставив его вставить подпись, а затем получить её, либо самостоятельно найти подпись пользователя и добавить её в письмо.

Решение 1: Извлечение подписи из файлов и добавление её в HTMLBody

Один из наиболее надежных способов — это программное чтение файла подписи пользователя и последующая вставка его содержимого в тело письма. Мы рассмотрим этот подход по шагам.

Шаг 1: Понимание структуры хранения подписей Outlook

Подписи Outlook хранятся в специальной папке на компьютере пользователя. Обычно это %userprofile%\AppData\Roaming\Microsoft\Signatures. Каждая подпись представлена тремя файлами с одинаковым именем, но разными расширениями:

  1. .htm: HTML-версия подписи (наиболее полная, содержит форматирование, изображения).
  2. .rtf: RTF-версия подписи.
  3. .txt: Текстовая версия подписи.

Для корректного отображения форматированной подписи в письме нам потребуется содержимое .htm файла.

Шаг 2: Определение подписи по умолчанию

У пользователя может быть несколько подписей. Чтобы узнать, какая именно подпись установлена по умолчанию для новых писем, нам понадобится обратиться к реестру Windows. Информация о подписи по умолчанию хранится по пути:

HKEY_CURRENT_USER\Software\Microsoft\Office\<Версия_Office>\Outlook\Profiles\<Имя_профиля>\0a0d020000000000c000000000000046

Здесь:

В этой ветке реестра нас интересуют значения 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"); // Получаем имя подписи для новых писем

Если Не ПустаяСтрока(ИмяПодписиПоУмолчанию) Тогда
    Сообщить("Имя подписи по умолчанию: " + ИмяПодписиПоУмолчанию);
Иначе
    Сообщить("Не удалось получить имя подписи по умолчанию.");
КонецЕсли;

Шаг 3: Чтение содержимого файла подписи

После того как мы определили имя подписи, нам нужно построить полный путь к .htm файлу подписи. Используем имя пользователя Windows для формирования пути к папке Signatures.


// ... (Код из Шага 2 для получения ИмяПользователя и ИмяПодписиПоУмолчанию) ...

// Формируем полный путь к папке подписей
ПутьКПапкеSignatures = "C:\Users\" + ИмяПользователя + "\AppData\Roaming\Microsoft\Signatures\";
ИмяФайлаПодписиHTML = ПутьКПапкеSignatures + ИмяПодписиПоУмолчанию + ".htm";

Перем HTMLПодписьТекст;
HTMLПодписьТекст = "";

Если ФайлСуществует(ИмяФайлаПодписиHTML) Тогда
    Попытка
        ТекстовыйДокумент = Новый ТекстовыйДокумент;
        ТекстовыйДокумент.Прочитать(ИмяФайлаПодписиHTML);
        HTMLПодписьТекст = ТекстовыйДокумент.ПолучитьТекст();
    Исключение
        Сообщить("Ошибка при чтении файла подписи: " + ОписаниеОшибки());
    КонецПопытки;
Иначе
    Сообщить("Файл подписи '" + ИмяФайлаПодписиHTML + "' не найден.");
КонецЕсли;

Если Не ПустаяСтрока(HTMLПодписьТекст) Тогда
    Сообщить("HTML-код подписи успешно прочитан.");
Иначе
    Сообщить("HTML-код подписи пуст или не удалось прочитать.");
КонецЕсли;

Шаг 4: Вставка подписи в письмо Outlook

Теперь, когда у нас есть 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();

Сообщить("Письмо успешно отправлено с подписью.");

Решение 2: Использование объекта WordEditor для получения подписи

Этот метод более динамичен, так как он заставляет Outlook вставить подпись, а затем мы ее извлекаем. Он полезен, если вы не хотите разбираться с путями в реестре или если у пользователя сложные подписи с динамическими элементами.

Шаг 1: Создание временного письма для извлечения подписи

Мы создадим временное письмо, вызовем для него 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ПодписьИзВременногоПисьма) Тогда
    Сообщить("Подпись успешно извлечена из временного письма.");
Иначе
    Сообщить("Не удалось извлечь подпись из временного письма. Возможно, подпись не настроена.");
КонецЕсли;

Шаг 2: Создание основного письма и вставка извлеченной подписи

Теперь, когда у нас есть 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();

Сообщить("Основное письмо успешно сформировано и отправлено с извлеченной подписью.");

Важные особенности и рекомендации

  1. Использование HTMLBody: Для подписей, содержащих форматирование, изображения или ссылки, всегда используйте свойство HTMLBody вместо Body. Убедитесь, что Письмо.BodyFormat = 2; (olFormatHTML) установлено.
  2. Парсинг HTML: При чтении подписи из файла или из WordEditor.Content.HTML, вы можете получить полный HTML-документ, включая теги <html>, <head> и <body>. Для корректной вставки в уже формируемое тело письма, критически важно извлечь только содержимое, которое находится внутри тега <body>. Мы показали примеры простого извлечения, но для очень сложных HTML-структур может потребоваться более продвинутый парсинг.
  3. Версии Office: При работе с реестром, учитывайте, что путь к настройкам Outlook зависит от версии Microsoft Office. Универсальный код должен быть способен определить установленную версию.
  4. Обработка ошибок: Всегда используйте блоки Попытка...Исключение...КонецПопытки при работе с COM-объектами, чтобы корректно обрабатывать возможные ошибки (например, Outlook не запущен, COM-объект не создан, файл подписи не найден).
  5. Производительность: Метод с использованием WordEditor может быть немного медленнее, так как требует создания и временного отображения письма. Для очень большого количества писем метод с чтением из файла может быть более эффективным, при условии корректного определения подписи по умолчанию.
  6. Изображения в подписи: Если подпись содержит встроенные изображения, они обычно хранятся в подпапке _files рядом с .htm файлом подписи. При вставке HTML-кода эти изображения будут корректно отображаться, если пути к ним в HTML-коде подписи относительны и подпапка доступна.

Выбирайте метод, который лучше всего соответствует вашим требованиям к надежности, производительности и удобству. Оба подхода позволяют успешно решить проблему отсутствия подписи при программном формировании писем из 1С в Outlook.

← На главную