Запуск Wscript.Shell и Shell.Application. Заметки

10.08.21

Разработка - БСП (Библиотека стандартных подсистем)

Некоторые примечания по работе с запуском приложений.

Общих обзоров и материалов на эту тему уже достаточно, данная заметка не претендует на полноценную публикацию или исследование нюансов. Рассматривается только ОС Windows и только с точки зрения разработчика 1С, без подробностей системного администрирования и погружения в API винды. Переход по навигационным ссылкам (открытие страниц, запуск программ по протоколам) и открытие/запуск приложений по ассоциированным файлам не рассматриваем.

В общем случае разработчику доступны два командных процессора, cmd или powershell. Это именно исполнители команд, передающие далее ядру ОС более низкоуровневые команды; они доступны из консолей, пункта меню "Выполнить", они лежат в основе многих действий графических оболочек и системных менеджеров. 

При этом, cmd не имеет "надстройки", и обращение к ней это всегда консольная команда. А вот powershell предоставляет оболочку над утилитой, что упрощает некоторые действия, но создаёт путаницу между собственно процессором и его "управляющим" объектом в памяти некоего процесса. Причём, эта оболочка позволяет вызвать выполнение cmd. Сложно сказать, в каких терминологических отношениях, например, находятся API-функции и процессоры, поэтому фиксируем лишь практически важные, прикладные моменты.

Важно не путать, что мы запускаем (какой процессор) и чем запускаем (какая оболочка). Под оболочкой рассматриваем СОМ-объект, созданный динамически в ходе исполнения кода на языке 1С, безотносительно, на сервере 1С это или на клиенте.

Для запуска разработчику доступны две оболочки, WScript.Shell и Shell.Application. Суть одинакова: создаётся их объект, ведётся работа с его методами и свойствами, а суть - в исполнении командной строки, команды, обычно с параметрами. В общем случае командой может быть директива ОС, собственно команда, запуск пакетного файла (скрипта), запуск исполняемого файла, открытие файла по ассоциации ОС итд. В общем случае всё это можно звать запускаемым сценарием.

Запуск может ждать завершения, т.е. остановить процесс-инициатор до момента окончания/прерывания/вылета запущенного процесса, либо продолжить процесс-инициатор и взаимодействовать с запущенным в реальном времени. Называть ли это асинхронностью или многопоточностью - не столь важно, но с помощью взаимодействия нескольких запущенных процессов и их "родителя" можно добиться параллельности некоторых действий. Правильнее вообще говорить не столько об ожидании завершения, сколько об управляемости запущенного.

В обоих случаях запущенный что-то получает на вход, что-то отдаёт на выход (и как код возврата, и, если предусмотрел разработчик, как подробные выходные данные), что-то сообщает при ошибке. Всё это - общеизвестные потоки StdIn, StdOut, StdErr. Разумеется, не считая собственных фишек запускаемой команды, например, чтения из где-то лежащих файлов, записи в реестр итд. Поток можно читать поэтапно, целиком, направлять в переменную или файл, и всё это доступно либо на уровне файловых команд, либо через свойства и методы оболочек. Можно напрямую прописывать вывод результатов исполнения в файл (> и >>, т.е. полная перезапись и дописывание соответственно), учитывая доступ к местоположению такого файла.

 

WScript.Shell

Каждый экземпляр объекта WScript автоматически создаётся сервером сценариев (CScript.exe или WScript.exe) в момент вызова конструктора. Регистрация в СОМ+ и DCOM не требуется.

Создание: 
Оболочка = Новый COMОбъект("WScript.Shell"); // во времена 8.2 это не всегда работало, требовалось "подружить" 1С и WSH

 
 О свойствах оболочки

В некоторых случаях в передаваемой командной строке надёжнее писать путь вызова в формате 8х3, т.е. "C:\Progra~1\" итд.

По завершении работы с Оболочка её надо принудительно приравнять Неопределено. Если объект был создан на управляемой форме, то, за исключением общеизвестного "удержания", контекст и занимаемая память по закрытии формы будут сброшены, а вот если создан из общего модуля, и сложился ещё ряд факторов, то может остаться в памяти, в т.ч. по завершении создавшего сеанса.

Оболочка, в свою очередь, позволяет создавать процессы ОС, как объекты типа WshScriptExec (некоторые источники утверждают, что может потребоваться доступ ко всем зарегистрированным классам, на практике не сталкивался).

Опять же, можно запускать консольно: cscript.exe ПутьВКавычкахКФайлуСценария (запуск сценариев пакетно). Такой подход позволяет указать, какой сервер сценариев применять (CScript.exe или используемый по умолчанию WScript.exe), можно подавить вывод диалогов и сообщений сценария.


С возможностью управления

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

Процесс = Оболочка.Exec(СтрокаЗапуска);

 
 О свойствах процесса


Входной, выходной, ошибочный потоки: StdIn, StdOut, StdErr - это тоже свойства, возвращающие актуальные на момент обращения к ним потоки, они имеют тип объект "TextStream", соответствующий стандартному потоку. Доступны, если скрипт запущен с помощью CScript.exe.
ВходнойПоток = Оболочка.StdIn.ReadLine() или ReadAll(), или Read(КолвоСимволов).
Выходной поток StdOut и поток ошибок StdErr аналогично.

Примечания по потокам:
* Некоторые приложения пишут вывод прямо в консоль, а не в стандартный поток, поэтому читать его, а равно и перенаправлять куда-то, бесполезно.
* Ввод и вывод буферизуются, поэтому важно не забывать про свойство Поток.AtEndOfStream; например, известны случаи, когда из StdOut читается сразу всё-всё, или просто больше, чем буферизовано, то процесс 1С может тихо рухнуть (наблюдалось на 8.3.18.1289), а если читается строка, после которой программа ожидает ввод текста, то управление в запустивший процесс 1С никогда не вернётся и 1С повиснет. Особенно это касается фоновых заданий - при неаккуратной работе с потоками очень глючат.

 

Процесс, запущенный Exec, разумно мониторить в цикле с задержкой, и при необходимости насильственно прерывать. Для этого используют:

* Процесс.Terminate(); // показал себя как наиболее надёжный, но, если процесс уже завершился (например, рухнул по ошибке), вызов этого метода тоже вызовет ошибку; метод позволяет завершить дочерний процесс. Процессу будет послана команда WM_CLOSE, если это не сработает, то процесс будет завершен принудительно;

* Процесс.Quit(КодВозвратаКакОшибки) - необязательный числовой код возврата приложения // код возврата будет не в потоке выхода и не в потоке ошибок, а именно в ExitCode; метод поддерживается не всеми серверами.

Важно учитывать, что на 64-х битном сервере 1С (Именно 1С, не ОС) вспомогательный объект, например, "Процесс", не создается согласно политикам безопасности. Не все 32-х битные COM-ы могут быть вызваны 64-х битным приложением - например, MSScriptControl не может. И вообще приложение может быть скомпилировать в несовместимой разрядности, и сервер сценариев не сможет создать экземпляр объекта процесса такого приложения.

Метод позволяет контролировать ход работы запущенного процесса, и запущенный сценарий является дочерним для запускающего, ему присущи те же переменные среды Windows, что и у процесса-родителя, и те же ограничения доступа. Использование метода разумно для консольных приложений, которые выполняются в консольном окне, особенно с диалогами запросов пользователю. Видимость диалога определяется запуском на клиенте 1С и совокупностью свойств сервера сценариев. Метод работает более адекватно и прозрачно при возможных ограничениях UAC.

Концепция: запустили и управляем сами: можем подождать (Sleep), можем сразу опросить, можем прервать из 1С.


Без возможности управления

Запуск внешних приложений с ожиданием завершения.

КодВозврата = Оболочка.Run(СтрокаЗапуска, СостояниеОкна, ЖдатьЗавершения);

* СтрокаЗапуска - строка сценария; если содержит пробелы, то обязательно заключать её в двойные кавычки, иначе выдаст "The system cannot find the file specified", и настоятельно рекомендуется использовать переменные окружения в пути к приложению. Для Exec подобные требования менее часты и менее жёстки.
* СостояниеОкна - число (интересно разве что на клиенте), и обобщая VBScript, они таковы: 0 – скрывает окно, будет виден только процесс в диспетчере задач. 1 – нормальный режим 2 – свернутый вид 3 – развернутый вид;
* ЖдатьЗавершения - булево, остановить процесс-инициатор или продолжать. Если установлено Истина, то метод вернет числовой код возврата вызванного приложения, если установлено Ложь – всегда будет возвращаться ноль.

Никаких свойств или объектов не предусмотрено.

Метод работает строго аналогично команде ОС Windows "Пуск/Выполнить", и при запуске приложения сперва идет его поиск в переменных среды, поэтому имена наиболее употребительных приложений можно указывать без пути. Метод не позволяет контролировать ход работы запущенного процесса, и дочерним для запускающего запущенный сценарий не является, а идёт как независимый. При наличии неочевидных UAC работа метода превращается в чёрный ящик.

Диалоги могут быть видны, но "живут своей жизнью", а разумнее всего применять метод к приложениям, не выводящим никакие диалоги. Замечено, что в некоторых скриптах своеобразно игнорирует "@echo off", выводя пустое окно консоли на пару секунд.

Концепция: запустили и ждём, пока сама ОС не разберётся и не вернёт управление 1С.
 

Shell.Application

Как оболочка, по сути это программно-скриптовый способ обращения к объекту, представляющему диспетчер запускаемых задач, в графическом интерфейсе - банальный Windows Explorer (проводник). Работа с такой оболочкой тесно связана с возможностями и доступностью технологии OLE/COM-АctiveX, и вообще в значительной степени это просто Automation-сервер. Более чувствительна к свойствам объекта приложения, с которым имеет дело. Задействуются системные файлы проводника и Shell32.dll (чьи версии очень разные в зависимости от сборок; так, например, большинство современных возможностей пошли с PowerShell 4.0 от Win 8.1, и даже в MSDN открыто пишут, что не все объекты, свойства и методы гарантированно работают на все версиях Windows, а в качестве средства победить баг в первую очередь советуют ставить обновления для... MSIE. Вообще PowerShell как оболочка - способ менее прозрачный, менее управляемый, более зависимый от контекста среды, но имеет ряд достоинств.

Технически, shell-команды могут транслироваться в имена для ActiveX-элементов, например, описанных как ShellFolder в реестре (так, "shell:Downloads" это папка загрузок текущего юзера). Если АctiveX не имеет имени (а оно есть не всегда), можно обратиться по CLSID (так, "shell:::{3080F90D-D7AD-11D9-BD98-0000947B0257}" - это команда свернуть все окна).
С точки зрения разрешений и прав доступа, поведение PowerShell определяется и управляется, по умолчанию, профилем пользователя, под которым выполнялся запуск. Соответственно, на сервере 1С это пользователь ОС, под которым запущена служба (например, USR1CV8). Пользователи ИБ и БД, напомню, описаны на ИТС, обсуждались тут: https://forum.infostart.ru/forum9/topic224824/

Для запуска команд применяют метод ShellExecute и его расширенный вариант, начиная с Vista, ShellExecuteEx. Оба метода позволяют чётко разделить исполняемую команду и параметры к ней (не на уровне положения в командной строке или сценарии, а на этапе вызова). При вызове этих команд вызывается СОМ-консольно основная утилита-процессор: powershell <АргументыИПрочее>, т.е. по сути аналог start-process, а уж что ей передано, определяет разработчик. Повторюсь, это может быть команда, директива, имя файла скрипта; всё это с указанием командного процессора и, главное, с возможностью обойтись без ввода пароля и подтверждения UAC, что особенно ценно при исполнении на сервере, да ещё в фоновом задании. Можно применять напрямую процессор cmd, так, "powershell -Command" идёт применительно к нему.

Создание:

Оболочка = Новый COMОбъект("Shell.Application"); // если проблема с совместимостью, разрядностью или правами, то уже это действие завершится ошибкой

По завершении работы с Оболочка её желательно принудительно приравнять Неопределено, но по завершении сеанса/процесса-инициатора, т.е. 1С, также будет удалена. Кто знает примеры с зависанием и тем более утечкой памяти - пожалуйста, поделитесь.

 

ShellExecute

КодВозврата = Оболочка.ShellExecute(КомандныйПроцессор, АргументыДляДействия, КаталогВыполнения, ДействиеГлагол, СостояниеОкна); // все параметры, кроме первого, необязательны

* КомандныйПроцессор - например, "cmd";

* АргументыДляДействия - описание сценария, например, "/c """ + ИмяФайлаИлиКоманда + """";

* КаталогВыполнения - по умолчанию используется текущий каталог того сервера и того процесса, который инициирует запуск;

* ДействиеГлагол - см.ниже.

* Состояние окна интересно только на клиенте, это рекомендуемое состояние окна при выполнении, многие приложения это игнорируют, нотация аналогична вышеприведённой по vbs);

Результат метода, т.е. КодВозврата - именно возвращаемое запущенным процессом значение, а не какой-либо из связанных с ним потоков.

Метод позволяет использовать "действия", "глаголы" (Verbs), детализирующие выполняемое действие. Есть глаголы, применимые лишь к конкретным приложениям и не во всех случаях: "edit" (запуск редактора), "find" (поиск файла), "print" (печать файла), "properties" (получение свойств файла). Глагол по умолчанию - open. Для разработчика 1С представляет интерес глагол "Open" - запуск приложения, а если это не исполняемый файл, то ассоциированного с ним.

В большинстве документаций нет глагола, определяющего запуск от имени администратора, однако он есть и называется "runas", и тут есть нюансы. Отсутствие запроса подтверждения UAC не эквивалентно эскалации привилегий текущего пользователя, под которым выполняется запуск, или привилегий конкретного процесса - обычно речь идёт о запуске этим процессом другого, уже с административными полномочиями и наследованием переменных контекста.

Для этого в рамках командной строки применяется ключ "-verb runas" (регистр букв не важен), а в рассматриваемом методе, например, ShellExecute("cmd", "/c """ + Команда + """", , "runas", 0);

Такой запуск имеет свои особенности: вместе с "-Verb RunAs" нельзя использовать вывод в файл через -RedirectStandardError и -RedirectStandardOutput, нельзя указывать -Wait и -NoNewWindow.
Пример запуска самой оболочки из-под себя с повышением прав: powershell "start-process powershell -verb runas".

Надеюсь, очевидно, что PowerShell RunAs небезопасна, т.к. SaveCred добавляет удостоверение администратора и пароль в кэш учетных данных, и этот кэш, в принципе, можно перехватить. При этом, в ряде случаев поведение Windows Credential Manager может не удовлетворять требованиям безопасности, и тогда никакое повышение не сработает, и далеко не всегда это ловится кодом возврата, возможно, придётся смотреть журнал событий винды. За возможность запуска программ от имени другого пользователя в Windows отвечает служба вторичного входа в систему (Secondary Log-on). Если эта служба остановлена, все описанные методы с RunAs работать не будут (проверить, запущена ли служба, можно командой PowerShell вида "Get-Service seclogon").

 
 Другие способы, связанные с RunAs
 
 Примечания

 

Общие моменты

* Всегда учитывать, что VBScript разрешает пропускать необязательные параметры методов, а JScript нет! Особенно при заимствовании кода и копи-пасте с примеров в сети)))

* Ключевое значение имеет кодировка - и путей, передаваемых как параметры, и команд, и строк в скриптах, и потоков. Есть кодировка, указанная напрямую в файле скрипта (chcp), и есть, в которой записан файл скрипта и читаются файлы потоков, их нельзя путать и следует проверять/настраивать отдельно. Не стоит уповать на 1С-ный Юникод или системные настройки по умолчанию. Кавычку, например, для верности лучше задавать напрямую как Символ(34).

* Следует быть аккуратнее с расшаренными ресурсами, и с теми, запись в которые виртуализируется. У виртуальных дисков в консольных запусках порой запаздывает отклик ОС по доступу, в т.ч.отказ.

* Если прав по UAC недостаточно, то при запуске на клиенте может вылезти consent.exe с диалогом запроса подтверждения, а на сервере просто втихую не запустится. До потоков вывода и ошибок. До каких-то объектных действий. В таких случаях команду лучше обёртывать запускаемым файлом-скриптом (как делает БСП), а на сервере только смотреть журнал ОС насчёт ошибок, смотреть настройки безопасности IE (при нужде добавлять server-ivc в доверенную зону, с которой разрешён запуск исполняемых).

* Создание сом-объекта, естественно, оборачиваем в попытку-исключение и, естественно, нам доступно только позднее связывание, т.к. такие вещи как Active-Х и тем более OLE статично в объекты 1С не впихнуть. И не забываем про деструктор и уборку мусора за собой.

* Если надо содержимое консольного окна успеть посмотреть, а оно закрывается, используем (например, последней командой в скрипте) команду PAUSE, но тогда помним про особенности чтения потока вывода.

 

По мнению "1С"

На ИТС сравнительно мало конкретики, посвящённой особенностям работы СОМ-объектов с оболочками ОС, и она разрозненна.

Процитирую наиболее полезное: "При запуске внешней программы из кода требуется составлять строку запуска таким образом, чтобы она собиралась только из проверенных частей. если одна из частей, из которых собирается строка запуска, содержит данные, полученные из базы данных, из поля ввода на форме или прочитаны из хранилища настроек, то перед запуском программы требуется проверить, являются ли запуск безопасным. Безопасными считаются такие строковые данные, которые не содержат в себе следующие символы: "$", "`", "|", "||" ";", "&", "&&". Данное требование распространяется на все способы запуска программ."

С одной стороны, выглядит разумно; с другой - наличие в списке символа ";" делает небезопасным, например, запуск другой 1С по строке соединения /IBConnectionString, "благодаря" чему воспользоваться БСП для таких действий затруднительно.

 
 Как сделано в БСП

 

P.S. Всё вышеизложенное - так или иначе - баян и повтор. После блестящей статьи Ю.Пермитина вообще мало смысла освещать эту тему. Поэтому - просто заметка, скорее даже для себя. Аккордеон-антисклерозник.

P.P.S. Тухлые помидоры приветствуются, но, по возможности, конструктивные.

Wscript.Shell Shell.Application запуск внешних приложений из системные команды

См. также

БСП. Добавляем отчет в меню Отчеты

БСП (Библиотека стандартных подсистем) Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Добавим новый отчет в меню нового документа средствами БСП.

02.04.2024    3132    John_d    10    

89

Создать на основании - своя кнопка (БСП). Проблема двух подменю Создать на основании

БСП (Библиотека стандартных подсистем) Адаптация типовых решений Платформа 1С v8.3 1С:ERP Управление предприятием 2 Бесплатно (free)

Понадобилось в подменю "Создать на основании" добавить свою команду, которая открывает обработку. В процессе доработок появилась проблема двух подменю "Создать на основании". В статье о том, как решились проблемы.

01.03.2024    1685    dimanich70    8    

14

Заполнение поля адреса в своей обработке [БСП]

Механизмы платформы 1С БСП (Библиотека стандартных подсистем) Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Небольшая шпаргалка по функциям БСП касательно адреса. Так скажем, еще один способ помимо https://infostart.ru/1c/articles/1060970/

12.02.2024    784    FilippovRI    0    

15

Расширяем возможности дополнительных обработок и настраиваем их отладку

БСП (Библиотека стандартных подсистем) Механизмы типовых конфигураций Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Уже не одна веб-страница исписана знаниями о дополнительных обработках, как создать, как подключить. Есть масса вариантов, как их можно отладить. Я разобрался в кишках работы библиотеки и покажу, как можно расширить возможности дополнительных отчетов, а также покажу удобный способ отладки.

07.02.2024    2610    YA_418728146    11    

43

1С:БСП Дополнительные реквизиты и сведения

БСП (Библиотека стандартных подсистем) Платформа 1С v8.3 Бесплатно (free)

В этой статье расскажем об архитектуре, использовании и особенностях реализации подсистемы «Свойства» из инструментария разработчика «1С:Библиотека стандартных подсистем» (1С: БСП).

19.01.2024    6271    PROSTO-1C    5    

46

Бесплатный митап 9 февраля «Библиотеки и фреймворки на 1С и всё, что с этим связано»

БСП (Библиотека стандартных подсистем) Мероприятия Бесплатно (free)

Митап посвящен вопросам создания, использования и поддержке переиспользуемого кода при разработке решений на платформе 1С:Предприятие 8.

15.01.2024    3884    0    Infostart    0    

21
Оставьте свое сообщение