При разработке внешних обработок для 1С 8.3 на управляемых формах часто возникает необходимость определить каталог, из которого запускается файл обработки. Эта задача становится критически важной, когда обработка должна взаимодействовать с другими файлами, расположенными рядом, например, с внешними библиотеками (DLL), текстовыми файлами настроек или дополнительными данными. Однако стандартные методы 1С, такие как КаталогТекущейБазы() или ТекущийКаталог(), не предоставляют нужной информации, поскольку они возвращают каталог информационной базы или текущий рабочий каталог процесса, но не путь к файлу самой обработки.
Давайте подробно рассмотрим, как мы можем решить эту проблему, учитывая различные сценарии запуска внешних обработок в управляемых формах.
Прежде чем погрузиться в решения, мы должны понимать ключевые особенности архитектуры 1С 8.3, особенно при работе с управляемыми формами и в клиент-серверном варианте. Внешние обработки могут запускаться в следующих режимах:
Эта разница в режимах работы накладывает отпечаток на методы получения пути к файлу. Универсального решения, которое бы давало "родной" каталог файла .epf для всех сценариев, к сожалению, не существует. Нам придётся применять различные подходы в зависимости от контекста.
Если ваша внешняя обработка запускается пользователем вручную через меню "Файл -> Открыть" в толстом клиенте, мы можем достаточно просто получить её путь. Для этого мы используем функции, которые доступны на стороне клиента и позволяют работать с путями к файлам.
КаталогФайла() и ЭтотОбъект.ИмяФайлаВ контексте формы внешней обработки, запущенной таким образом, свойство ИмяФайла объекта обработки содержит полный путь к файлу .epf. Используя функцию КаталогФайла(), мы можем извлечь из этого полного пути только каталог.
Рассмотрим пример кода:
Функция ПолучитьКаталогВнешнейОбработки_ФайлОткрыть()
// Проверяем, что код выполняется на клиенте,
// так как на сервере эта информация недоступна.
Если НЕ ЭтоКлиент() Тогда
Возврат Неопределено; // Сервер не знает о расположении файла на клиенте
КонецЕсли;
// В толстом клиенте, при открытии через "Файл -> Открыть",
// свойство ИмяФайла формы обработки содержит полный путь.
// Если обработка запускается как основная (не дополнительная)
// и свойство доступно в контексте
Если ТипЗнч(ЭтотОбъект) = Тип("ВнешняяОбработкаОбъект") И ЭтоКлиент() Тогда
Попытка
// ЭтотОбъект.ИмяФайла доступно в модуле объекта внешней обработки
// или через форму, если она передается в качестве параметра.
Возврат КаталогФайла(ЭтотОбъект.ИмяФайла);
Исключение
// В некоторых случаях (например, если обработка загружена из базы),
// ИмяФайла может быть недоступно или пустым.
Возврат Неопределено;
КонецПопытки;
КонецЕсли;
// Для универсальности и доступа из модуля формы
// мы можем получить путь через ПутьКВнешнейОбработке.
// Это свойство более надежно для управляемых форм,
// запущенных через "Файл -> Открыть".
Попытка
Возврат КаталогФайла(ВнешниеОбработки.ПутьКВнешнейОбработке);
Исключение
Возврат Неопределено;
КонецПопытки;
КонецФункции
Важно: Метод ВнешниеОбработки.ПутьКВнешнейОбработке (обращение к глобальному контексту клиента) наиболее надёжен для получения пути к файлу, если обработка открыта именно через "Файл -> Открыть". Мы всегда должны помнить, что этот метод работает только на клиенте, поскольку сервер не имеет представления о расположении файла на машине пользователя.
Ситуация значительно усложняется, когда внешняя обработка регистрируется в информационной базе как "Дополнительный отчет или обработка". В этом случае 1С копирует файл .epf во временный каталог на клиентской машине или в кэш. Соответственно, "родного" каталога у файла обработки в привычном понимании больше нет, и методы, описанные выше, не будут работать или вернут путь к временному расположению.
Если вашей обработке необходимо работать с сопутствующими файлами (например, DLL-библиотеками или файлами настроек), которые должны "лежать рядом" с ней, то для дополнительных обработок этот подход не годится. Нам придётся использовать альтернативные механизмы.
Для дополнительных обработок нам нужно обеспечить, чтобы сопутствующие файлы были доступны. Вот несколько подходов:
Давайте подробнее остановимся на первом и втором подходах, так как они наиболее универсальны для управляемых форм.
Мы можем добавить в нашу внешнюю обработку (или в конфигурацию) макет, который будет содержать необходимые нам данные. Например, это может быть XML-файл с настройками или даже бинарные данные, которые затем будут записаны на диск.
Разберем по шагам, как это сделать:
Посмотрим на пример кода для сохранения двоичных данных из макета во временный файл:
Функция СохранитьФайлИзМакетаВоВременныйКаталог(ИмяМакета, ИмяФайлаДляСохранения)
Если НЕ ЭтоКлиент() Тогда
// На сервере нет файловой системы клиента,
// но можно сохранить на сервер, если нужно.
// Для клиентской работы - Возврат Неопределено
Возврат Неопределено;
КонецЕсли;
Попытка
// Получаем макет по имени
Макет = ЭтотОбъект.ПолучитьМакет(ИмяМакета);
Если Макет = Неопределено Тогда
Сообщить("Макет '" + ИмяМакета + "' не найден.");
Возврат "";
КонецЕсли;
// Получаем двоичные данные из макета
ДвоичныеДанные = Макет.ПолучитьДвоичныеДанные();
Если ДвоичныеДанные = Неопределено Тогда
Сообщить("Не удалось получить двоичные данные из макета '" + ИмяМакета + "'.");
Возврат "";
КонецЕсли;
// Создаем путь к временному файлу
ПутьКВременномуКаталогу = КаталогВременныхФайлов();
ПолныйПутьКВременномуФайлу = ПутьКВременномуКаталогу + ИмяФайлаДляСохранения;
// Записываем двоичные данные во временный файл
ДвоичныеДанные.Записать(ПолныйПутьКВременномуФайлу);
Сообщить("Файл '" + ИмяФайлаДляСохранения + "' успешно сохранен в: " + ПолныйПутьКВременномуФайлу);
Возврат ПолныйПутьКВременномуФайлу;
Исключение
Сообщить("Ошибка при сохранении файла из макета: " + ОписаниеОшибки());
Возврат "";
КонецПопытки;
КонецФункции
// Пример использования:
// Если мы хотим использовать DLL-библиотеку MyLib.dll, которая хранится в макете "MyLibDLL"
// ПутьКDLL = СохранитьФайлИзМакетаВоВременныйКаталог("MyLibDLL", "MyLib.dll");
// Если Не ПустаяСтрока(ПутьКDLL) Тогда
// // Теперь можно работать с DLL по пути ПутьКDLL
// КонецЕсли;
Этот подход позволяет нам "вынимать" необходимые файлы из обработки при её запуске и использовать их во временном каталоге. Это очень удобно для небольших вспомогательных файлов или DLL.
Для более крупных или часто обновляемых файлов, которые нецелесообразно хранить в макетах самой обработки, мы можем использовать регистры сведений. Например, мы можем создать регистр сведений "ФайлыВнешнихОбработок" с измерениями "ИмяФайла" и ресурсом "ДвоичныеДанныеФайла".
Проанализируем шаги:
ИмяФайла (Строка)ДвоичныеДанные (ХранилищеЗначения)Посмотрим на пример кода для получения и сохранения файла из регистра сведений:
Функция ПолучитьФайлИзРегистраВоВременныйКаталог(ИмяФайлаВРегистре)
Если НЕ ЭтоКлиент() Тогда
Возврат Неопределено;
КонецЕсли;
Попытка
// Выполняем запрос для получения двоичных данных из регистра
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| ХранилищеФайловДляОбработок.ДвоичныеДанные
|ИЗ
| РегистрСведений.ХранилищеФайловДляОбработок КАК ХранилищеФайловДляОбработок
|ГДЕ
| ХранилищеФайловДляОбработок.ИмяФайла = &ИмяФайла";
Запрос.УстановитьПараметр("ИмяФайла", ИмяФайлаВРегистре);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Если Выборка.Следующий() Тогда
ЗначениеХранилища = Выборка.ДвоичныеДанные;
Если ЗначениеХранилища = Неопределено Тогда
Сообщить("В регистре 'ХранилищеФайловДляОбработок' для файла '" + ИмяФайлаВРегистре + "' не найдены двоичные данные.");
Возврат "";
КонецЕсли;
// Получаем сами двоичные данные
ДвоичныеДанные = ЗначениеХранилища.Получить();
// Создаем путь к временному файлу
ПутьКВременномуКаталогу = КаталогВременныхФайлов();
ПолныйПутьКВременномуФайлу = ПутьКВременномуКаталогу + ИмяФайлаВРегистре;
// Записываем двоичные данные во временный файл
ДвоичныеДанные.Записать(ПолныйПутьКВременномуФайлу);
Сообщить("Файл '" + ИмяФайлаВРегистре + "' успешно сохранен из регистра в: " + ПолныйПутьКВременномуФайлу);
Возврат ПолныйПутьКВременномуФайлу;
Иначе
Сообщить("Файл '" + ИмяФайлаВРегистре + "' не найден в регистре сведений 'ХранилищеФайловДляОбработок'.");
Возврат "";
КонецЕсли;
Исключение
Сообщить("Ошибка при получении файла из регистра: " + ОписаниеОшибки());
Возврат "";
КонецПопытки;
КонецФункции
// Пример использования:
// Если нам нужна DLL-библиотека MyBigLib.dll, хранится в регистре
// ПутьКDLL = ПолучитьФайлИзРегистраВоВременныйКаталог("MyBigLib.dll");
// Если Не ПустаяСтрока(ПутьКDLL) Тогда
// // Теперь можно работать с DLL по пути ПутьКDLL
// КонецЕсли;
Этот метод особенно полезен, когда файлы большие или требуют централизованного управления, а также когда мы не хотим засорять макеты самой обработки или конфигурацию.
Для создания действительно гибких внешних обработок, способных работать в разных режимах, нам придется реализовать комбинированный подход, который учитывает особенности каждого сценария.
Мы можем создать функцию, которая будет пытаться получить каталог различными способами и выбирать наиболее подходящий:
Рассмотрим, как могла бы выглядеть такая функция:
Функция ПолучитьДоступныйПутьКСопутствующемуФайлу(ИмяФайлаДляПоиска, ИмяМакетаСФайлом = "", ИмяФайлаВРегистре = "")
Если НЕ ЭтоКлиент() Тогда
Возврат Неопределено; // Этот код предназначен для клиента
КонецЕсли;
// 1. Попытка получить путь, если обработка открыта через "Файл -> Открыть" (толстый клиент)
Попытка
КаталогОбработки = КаталогФайла(ВнешниеОбработки.ПутьКВнешнейОбработке);
Если Не ПустаяСтрока(КаталогОбработки) И ФайлСуществует(КаталогОбработки + ИмяФайлаДляПоиска) Тогда
Сообщить("Файл найден рядом с обработкой, открытой через 'Файл -> Открыть'.");
Возврат КаталогОбработки + ИмяФайлаДляПоиска;
КонецЕсли;
Исключение
// Игнорируем ошибку, если свойство недоступно, переходим к следующему способу
КонецПопытки;
// 2. Если файл не найден рядом с открытой обработкой или это дополнительная обработка,
// пытаемся извлечь из макета
Если Не ПустаяСтрока(ИмяМакетаСФайлом) Тогда
ПутьИзМакета = СохранитьФайлИзМакетаВоВременныйКаталог(ИмяМакетаСФайлом, ИмяФайлаДляПоиска);
Если Не ПустаяСтрока(ПутьИзМакета) Тогда
Сообщить("Файл успешно извлечен из макета обработки.");
Возврат ПутьИзМакета;
КонецЕсли;
КонецЕсли;
// 3. Если и из макета не удалось (или макет не указан), пытаемся извлечь из регистра сведений
Если Не ПустаяСтрока(ИмяФайлаВРегистре) Тогда
ПутьИзРегистра = ПолучитьФайлИзРегистраВоВременныйКаталог(ИмяФайлаВРегистре);
Если Не ПустаяСтрока(ПутьИзРегистра) Тогда
Сообщить("Файл успешно извлечен из регистра сведений.");
Возврат ПутьИзРегистра;
КонецЕсли;
КонецЕсли;
Сообщить("Не удалось получить доступ к файлу '" + ИмяФайлаДляПоиска + "' ни одним из доступных способов.");
Возврат "";
КонецФункции
// Пример использования:
// ПутьКМоейБиблиотеке = ПолучитьДоступныйПутьКСопутствующемуФайлу("MyCustom.dll", "МакетМоейDLL", "MyCustom.dll");
// Если Не ПустаяСтрока(ПутьКМоейБиблиотеке) Тогда
// // Далее работаем с библиотекой по полученному пути
// КонецЕсли;
В этой функции СохранитьФайлИзМакетаВоВременныйКаталог и ПолучитьФайлИзРегистраВоВременныйКаталог — это функции, которые мы уже рассмотрели ранее. Такой подход обеспечивает максимальную гибкость и надёжность для большинства сценариев.
ФайлСуществует()).ПоместитьФайл(), ПолучитьФайл(), АдресВременногоХранилища).Таким образом, мы выяснили, что прямой универсальный способ получить "родной" каталог внешней обработки для всех сценариев в 1С 8.3 УФ отсутствует. Однако, применяя комбинацию методов — прямое получение пути для обработок, открытых вручную, и извлечение файлов из макетов или регистров сведений для дополнительных обработок — мы можем успешно решить задачу взаимодействия с сопутствующими файлами и библиотеками. Это обеспечивает гибкость и надёжность наших разработок в различных условиях эксплуатации 1С.