Почему не отправляется почта из 1С 7.7 в терминальном режиме и как настроить сохранение временных файлов?

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

При организации автоматической отправки отчетов и печатных форм из 1С 7.7 по электронной почте в терминальной среде (Remote Desktop Services) разработчики часто сталкиваются с нестабильным поведением: процесс срабатывает через раз, под учетной записью администратора все функционирует без сбоев, а у рядовых пользователей файл вложения не сохраняется во временную директорию или не прикрепляется к письму.

Разберем по шагам, почему возникает эта ошибка, как локализовать проблему и какими способами можно гарантировать надежную отправку корреспонденции.

Основные причины проблемы в терминальной среде

Проанализируем ключевые факторы, из-за которых процесс сохранения и отправки дает сбой у непривилегированных пользователей:

  1. Изоляция временных папок для каждого сеанса (Per-Session Temp). В службах удаленных рабочих столов Windows по умолчанию включено разделение временных папок. Путь к переменной среды %TEMP% для каждого сеанса принимает вид %TEMP%\1, %TEMP%\2 и так далее. Если 1С или внешняя компонента (например, Dialmail.dll) вычисляет путь один раз или обращается к общему профилю без учета номера текущей терминальной сессии, пути сохранения и прикрепления расходятся.
  2. Асинхронность формирования вложения. Если файл формируется через виртуальный PDF-принтер или внешний конвертер, процесс печати выполняется асинхронно. Код 1С сразу после вызова печати пытается прочитать файл и отправить его по почте, когда запись на диск еще не завершена операционной системой.
  3. Ограничения прав доступа NTFS и UAC VirtualStore. При попытке сохранить файл в корень диска, в общий каталог C:\Temp или в системную папку платформы BIN у обычного пользователя срабатывает перенаправление VirtualStore либо возникает отказ в доступе. Администратор при этом имеет полные права, поэтому под его учетной записью сбоев не возникает.
  4. Логирование внешней компоненты. Библиотека Dialmail.dll при включенном протоколировании событий без явного указания пути пытается создать лог-файл в рабочем каталоге 1С (обычно Program Files). Отсутствие прав на запись в эту папку приводит к сбою инициализации почтового клиента.

Способ 1. Устранение асинхронности при формировании файла

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

Посмотрим на пример организации ожидания готовности файла:


// Формирование файла вложения
ИмяВложения = КаталогВременныхФайлов + "Счет_" + СокрЛП(НомерДок) + ".pdf";
Таб.Напечатать(0); // или экспорт в файл

// Ожидание физического создания файла на диске
МаксОжидание = 10; // секунд
Счетчик = 0;
Пока (ФС.СуществуетФайл(ИмяВложения) = 0) И (Счетчик < МаксОжидание) Цикл
    // Пауза через пустой цикл или системный вызов
    глПауза(1); 
    Счетчик = Счетчик + 1;
КонецЦикла;

Если ФС.СуществуетФайл(ИмяВложения) = 0 Тогда
    Сообщить("Ошибка: файл вложения не сформирован за отведенное время!", "!");
    Возврат;
КонецЕсли;

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

Способ 2. Использование гарантированно доступного каталога

Чтобы избежать путаницы с путями %TEMP% в различных сессиях RDS, рекомендуется сохранять файлы в каталог пользователя 1С. К этой папке у терминального пользователя гарантированно настроены полные права NTFS, и путь не зависит от смены номеров сеансов терминала.

Используем встроенную функцию КаталогПользователя():


ПутьККаталогу = КаталогПользователя();
ИмяФайла = ПутьККаталогу + "Отчет.xls";

// Сохраняем таблицу
Таб.Записать(ИмяФайла, "XLS");

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

Способ 3. Настройка групповых политик узла сеансов RDS

Если для работы требуется использование именно системной переменной %TEMP%, отключим создание раздельных папок сеансов в политиках сервера:

  1. Откроем редактор локальной групповой политики (gpedit.msc) на терминальном сервере.
  2. Перейдем в раздел: Конфигурация компьютера -> Административные шаблоны -> Компоненты Windows -> Службы удаленных рабочих столов -> Узел сеансов удаленных рабочих столов -> Временные папки.
  3. Активируем параметр «Не использовать временные папки для каждого сеанса» (переведем в состояние Включено).
  4. Применим изменения командой gpupdate /force в командной строке.

После этого путь к временной папке для каждого пользователя станет постоянным и будет указывать строго на C:\Users\<ИмяПользователя>\AppData\Local\Temp.

Способ 4. Альтернативная отправка через .NET (System.Net.Mail)

Если внешняя библиотека Dialmail.dll продолжает работать нестабильно в среде терминального сервера, заменим ее вызовом стандартных почтовых классов .NET Framework через COM-обертку. Это решает проблему совместимости со сторонними DLL и обеспечивает корректную работу с путями.

Рассмотрим реализацию функции подготовки вложения и процедуры отправки:


Функция ПолучитьАтачмент(file, Врап)
    data = Врап.СоздатьОбъект("System.Net.Mail.Attachment", file);
    disposition = data.ContentDisposition;
    IO_File = Врап.ПолучитьТип("System.IO.File");
    disposition.CreationDate = IO_File.GetCreationTime(file);
    disposition.ModificationDate = IO_File.GetLastWriteTime(file);
    disposition.ReadDate = IO_File.GetLastAccessTime(file);
    Возврат data;
КонецФункции

Процедура ОтправитьПисьмо(СписокФайлов, ТемаПисьма, smtp, Кому, ОтКого, login, password, ПолноеИмяОтКого)
    врап = СоздатьОбъект("NetObjectToIDispatch45"); 
    врап.УстЭтоСемерка(); 
    
    port = врап.ChangeType("System.Int32", 25);
    mail = врап.СоздатьОбъект("System.Net.Mail.SmtpClient", smtp, port);
    
    истина = врап.ChangeType("System.Boolean", "true");
    ложь = врап.ChangeType("System.Boolean", "false");
    
    mail.UseDefaultCredentials = ложь;
    mail.DeliveryMethod = врап.ПолучитьТип("System.Net.Mail.SmtpDeliveryMethod").Network;
    
    Message = врап.СоздатьОбъект("System.Net.Mail.MailMessage");
    MailAddressType = врап.ПолучитьТип("System.Net.Mail.MailAddress");
    Message.From = врап.СоздатьОбъект(MailAddressType, ОтКого, ПолноеИмяОтКого);
    Message.To.Add(врап.СоздатьОбъект(MailAddressType, Кому));
    Message.Subject = ТемаПисьма;
    Message.IsBodyHtml = истина;
    Message.Body = ""; 
    
    Для сч = 1 По СписокФайлов.РазмерСписка() Цикл
        Файл = СписокФайлов.ПолучитьЗначение(сч);
        Message.Attachments.Add(ПолучитьАтачмент(Файл, врап));
    КонецЦикла;
    
    Попытка
        mail.Send(Message);
        Message.Dispose();
        mail.Dispose();
        Сообщить("Письмо успешно отправлено!");
    Исключение
        Сообщить("Ошибка отправки: " + ОписаниеОшибки(), "!");
    КонецПопытки;
КонецПроцедуры

Для корректного формирования временного пути в .NET используем класс System.IO.Path:


врап = СоздатьОбъект("NetObjectToIDispatch45");
Patch = врап.ПолучитьТип("System.IO.Path");
Путь = Patch.GetTempPath();

СпзФайлов = СоздатьОбъект("СписокЗначений");
Файл = Patch.Combine(Путь, "Прайс.xls");

// Формируем отчет и сохраняем
Таб.Записать(Файл, "XLS");
СпзФайлов.ДобавитьЗначение(Файл);

ОтправитьПисьмо(СпзФайлов, "Прайс-лист", "smtp.server.ru", "client@mail.ru", "info@company.ru", "user", "pass", "Компания");

// Удаляем временный файл после отправки
ФС.УдалитьФайл(Файл);

Способ 5. Использование штатного компонента Windows CDO.Message

Если нет возможности регистрировать сторонние библиотеки или использовать .NET-врапперы, отправку почты из 1С 7.7 можно организовать через системный компонент Windows CDO.Message. Он присутствует на всех версиях Windows Server и не требует наличия прав администратора:


Почта = СоздатьОбъект("CDO.Message");
Конфиг = СоздатьОбъект("CDO.Configuration");
Поля = Конфиг.Fields;

Схема = "http://schemas.microsoft.com/cdo/configuration/";
Поля.Item(Схема + "sendusing").Value = 2; // Отправка по сети через SMTP
Поля.Item(Схема + "smtpserver").Value = "smtp.yourserver.ru";
Поля.Item(Схема + "smtpserverport").Value = 25;
Поля.Item(Схема + "smtpauthenticate").Value = 1; // Базовая аутентификация
Поля.Item(Схема + "sendusername").Value = "login";
Поля.Item(Схема + "sendpassword").Value = "password";
Поля.Update();

Почта.Configuration = Конфиг;
Почта.To = "receiver@domain.com";
Почта.From = "sender@domain.com";
Почта.Subject = "Документы из 1С";
Почта.TextBody = "Во вложении высылаем документы.";
Почта.AddAttachment(ИмяФайла);

Попытка
    Почта.Send();
    Сообщить("Сообщение успешно отправлено через CDOSYS.");
Исключение
    Сообщить("Ошибка при отправке: " + ОписаниеОшибки(), "!");
КонецПопытки;

Диагностика ошибок с помощью Process Monitor

Если причина сбоя остается неясной, запустим утилиту Process Monitor (ProcMon) под учетной записью администратора и настроим фильтр по процессу 1cv7s.exe:

  1. Добавим фильтр: Process Name is 1cv7s.exe.
  2. Добавим фильтр: Result is ACCESS DENIED или NAME NOT FOUND.
  3. Воспроизведем операцию сохранения и отправки под проблемным пользователем.

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

← На главную