При работе с Microsoft Word через COM-объекты из 1С часто возникает неприятная ситуация: после выполнения всех операций (создание, заполнение, сохранение документа) и вызова команды закрытия, сам Word вроде бы исчезает с экрана, но его процесс WINWORD.EXE продолжает "висеть" в диспетчере задач. Это приводит к накоплению "мусорных" процессов, которые потребляют ресурсы системы и могут мешать дальнейшей работе. Разберемся, почему так происходит и как правильно завершать работу с Word, чтобы он полностью выгружался из памяти.
Давайте проанализируем типичный код работы с COM-объектами, который приводит к этой проблеме. Автор исходного сообщения на форуме использовал примерно такую последовательность действий:
Word = Новый COMОбъект("Word.Application");Документ = Word.Documents.Open(ИмяВременногоФайла);Документ.SaveAS(ИмяВременногоФайлаПДФ, 17);Word.Application.Quit(); или Word.Quit();На первый взгляд, все логично. Мы дали команду приложению на закрытие. Но здесь кроется ключевая ошибка. Причина, по которой процесс WINWORD.EXE не завершается, заключается в том, что в памяти 1С остаются активные ссылки на объекты, созданные этим процессом. В нашем случае — это объект открытого документа, хранящийся в переменной Документ. Пока 1С "держит" эту ссылку, сборщик мусора не может полностью освободить COM-объект, и приложение Word считает, что его ресурсы все еще используются, поэтому не может корректно завершить свою работу.
Важный момент: Простого вызова метода Quit() недостаточно. Необходимо придерживаться строгого правила: освобождать COM-объекты нужно в порядке, обратном их созданию. То есть сначала мы должны закрыть "дочерние" объекты (документы, диапазоны, объекты поиска) и только потом — родительский объект приложения.
Чтобы гарантированно закрыть Word, важно соблюдать стандарты и методики разработки и выполнить три ключевых шага. Рассмотрим их подробно.
Перед тем как дать команду на закрытие всего приложения, нужно явно закрыть все открытые в нем документы. Для этого используется метод Close() объекта документа.
// Закрываем документ. Параметр 0 (или константа wdDoNotSaveChanges)
// означает, что документ нужно закрыть без сохранения изменений.
// Это безопасно, так как мы уже сохранили результат в PDF.
Документ.Close(0);
Если бы нам нужно было сохранить изменения в исходном .doc файле, мы бы использовали другой параметр, например, -1 (константа wdSaveChanges).
Теперь, когда все дочерние объекты (документы) закрыты, можно безопасно завершать работу самого приложения. Для этого используется метод Quit().
Word.Quit();
Последний, но не менее важный шаг — это присвоение значения Неопределено всем переменным, которые хранили ссылки на COM-объекты. Это является явным указанием для сборщика мусора 1С, что эти объекты нам больше не нужны и память, занимаемую ими, можно освободить.
Документ = Неопределено;
Word = Неопределено;
// Также нужно присвоить Неопределено и другим переменным,
// если они ссылались на объекты Word (например, на объект Find).
Замена = Неопределено;
Объединим все шаги в единый работающий пример. Крайне рекомендуется оборачивать работу с COM-объектами в конструкцию Попытка ... Исключение ... КонецПопытки. Это гарантирует, что даже если в процессе заполнения документа произойдет ошибка, код очистки (закрытие Word) все равно будет выполнен.
Вот как будет выглядеть доработанный и надежный фрагмент кода:
&НаКлиенте
Процедура СобратьМакетWORD(СсылкаНаОбъект)
// ... Код получения данных и временных имен файлов ...
ИмяВременногоФайла = ПолучитьИмяВременногоФайла("doc");
ИмяВременногоФайлаПДФ = ПолучитьИмяВременногоФайла("PDF");
// ...
Word = Неопределено;
Документ = Неопределено;
Попытка
Word = Новый COMОбъект("Word.Application");
Word.Visible = Ложь; // Работаем в фоновом режиме, не показывая окно пользователю
Документ = Word.Documents.Open(ИмяВременногоФайла);
// --- Блок работы с документом ---
Для Каждого ЭлементДанных Из Данные Цикл
Замена = Документ.Content.Find;
Замена.Execute("«" + ЭлементДанных.Ключ + "»", Ложь, Истина, Ложь, Ложь, , Истина, , Ложь, СокрЛП(ЭлементДанных.Значение), 2);
КонецЦикла;
// Сохраняем результат в PDF
// 17 - это числовое значение константы wdFormatPDF
Документ.SaveAs(ИмяВременногоФайлаПДФ, 17);
ЗапуститьПриложение(ИмяВременногоФайлаПДФ);
Исключение
Сообщение = Новый СообщениеПользователю();
Сообщение.Текст = "Произошла ошибка при формировании документа: " + ОписаниеОшибки();
Сообщение.Сообщить();
КонецПопытки;
// --- Блок обязательной очистки ресурсов ---
// Этот код выполнится в любом случае: и после успешной работы, и после ошибки
Если Документ <> Неопределено Тогда
Документ.Close(0); // 0 - не сохранять изменения
Документ = Неопределено;
КонецЕсли;
Если Word <> Неопределено Тогда
Word.Quit();
Word = Неопределено;
КонецЕсли;
КонецПроцедуры
Иногда, в особо сложных случаях, или когда нет возможности исправить код, разработчики прибегают к другим методам. Рассмотрим их, но с оговорками.
Можно запустить из 1С системную команду taskkill для принудительного "убийства" процесса WINWORD.EXE.
Почему это плохой подход? Как было справедливо замечено в обсуждении, у вас нет надежного способа определить, какой именно из процессов WINWORD.EXE был запущен вашим кодом. Если у пользователя в этот момент открыты другие важные документы Word, ваша команда закроет их все без сохранения, что приведет к потере данных. Этот метод стоит рассматривать только в самых крайних случаях и на свой страх и риск.
Вместо использования шаблонов Word можно рассмотреть встроенные в 1С механизмы:
Эти подходы исключают саму проблему взаимодействия с COM, делая решение более стабильным и независимым от внешних приложений.
Таким образом, ключ к решению проблемы — правильная дисциплина работы с COM-объектами: всегда закрывайте дочерние объекты перед родительскими и очищайте переменные, присваивая им Неопределено.