1

Тема: WSH: пишем сценарии в формате WSF

Сценарии Windows Script Host с расширением wsf представляют собой файлы в формате XML, поддерживающие схему Windows Script XML.

Преимущества формата WSF:
- возможность задания числа, имён и типов аргументов; встроенная обработка аргумента /?, "самодокументирование";
- возможность задания ссылок на библиотеки типов используемых в сценарии COM-объектов с помощью тега <reference>, что даёт доступ к внешним мнемоническим константам;
- удобное создание объектов посредством тега <object>;
- поддержка "вложенных" файлов (атрибут "src" тега <script>);
- хранение нескольких независимых сценариев в одном файле (тег <job>);
- возможность написания сценариев и частей одного сценария на разных языках (одновременно в одном WSF-файле), вплоть до специально устанавливаемых "неродных" языков, например, Python.

Обязательными для создания сценария являются элементы <job> и <script>.

Порядок вложенности тегов:
- элемент <package> может содержать один или несколько элементов <job>;
- элемент <job> может содержать один или несколько элементов <runtime>, <resource>, <object>, <reference> и <script>;
- элемент <runtime> может содержать один или несколько элементов <named> и <unnamed>, а также элементы <description> и <example>;

Предложения в русском языке начинаются с большой буквы и заканчиваются точкой.
В названии ветки всегда должен быть указан язык программирования или среда исполнения скрипта, если это возможно.

2

Re: WSH: пишем сценарии в формате WSF

Комментарии

Комментарии вставляются двумя способами:

<!-- Первый комментарий -->

<comment>Второй комментарий</comment>

Элемент <?XML?>

Этот элемент размещается в самом начале файла - никаких других символов или пустых строк перед ним быть не должно. Чтобы использовать в файле символы кириллицы, обязательно должен быть указан атрибут encoding, например:

<?xml version='1.0' encoding='windows-1251'?>

Элемент <script>

Содержит собственно код скрипта. Атрибут language определяет язык (например, JScript или VBScript).

Код сценария должен быть помещён в секцию CDATA, которая начинается с символов <![CDATA[ и заканчивается символами ]]>, чтобы избежать ошибочной интерпретации фрагментов кода как разметки XML. Данное замечание справедливо, только если присутствует элемент <?XML?>, в противном случае присутствие секции CDATA приведёт к ошибке.

Атрибут src позволяет подключать внешний файл с другим сценарием и приводит к результату, как если бы содержимое подключаемого файла было расположено внутри тега <script>. Таким образом, можно выделить код, который должен использоваться в нескольких сценариях, и повторно использовать его без буквального копирования.

Элемент <object>

Предлагает ещё один способ создания экземпляра COM-объекта внутри сценария, например, аналогично функции CreateObject(). Атрибут id - это имя, применяемое для обращения к объекту внутри сценария. Объект, созданный с помощью этого элемента, будет глобальным, т.е. может использоваться во всех элементах <script> внутри данного элемента <job>.

Атрибуты progid и classid задают соответственно программный идентификатор и CLSID. Из этих двух атрибутов может быть указан только один.

Создание объектов посредством тега <object> не во всех случаях безопасно. Например, если создать таким способом объект "Word.Application", то при запуске скрипта с аргументом /? этот объект будет автоматически создан. Однако после этого - при завершении работы скрипта - этот объект может оказаться неразрушенным, и вы получите "зависший" в памяти процесс winword.exe.

Предложения в русском языке начинаются с большой буквы и заканчиваются точкой.
В названии ветки всегда должен быть указан язык программирования или среда исполнения скрипта, если это возможно.

3

Re: WSH: пишем сценарии в формате WSF

Элемент <?job?>

Задаёт режим отладки при выполнении. Задание может быть выполнено во внешнем отладчике, если значение атрибута debug равно true (по умолчанию - false).

Элемент <package>

Может содержать один или несколько элементов <job>. Необходим тогда, когда с помощью элементов <job> нужно определить более одного задания. Если описывается только одно задание, элемент <package> можно не использовать.

Элемент <job>

Определяет отдельный сценарий (задание). Сценарий может состоять из нескольких частей, написанных, возможно, на разных языках.

Атрибут id определяет уникальное имя задания. Чтобы запустить конкретное зание из многозадачного WSF-файла, нужно воспользоваться параметром //job:"JobID" в командной строке, например:

cscript //job:"Task1" file.wsf

Если параметр //job:"JobID" не указан, запустится первое задание в файле.

Элемент <runtime>

Позволяет автоматически обрабатывать параметр /? командной строки, т.е. делает сценарий самодокументированным. Элемент <runtime> является контейнером для элементов <named>, <unnamed>, <description> и <example>. Элемент <runtime> является  дочерним относительно <job> и относится к текущему заданию.

Элемент <named>

Элементы <named> описывают именные параметры командной строки сценария. Атрибуты:

- name - имя параметра;
- helpstring - описание параметра;
- type - тип параметра: "string", "boolean" или "simple" (последнее - по умолчанию);
- required - если "true", параметр является обязательным; по умолчанию - false.

Информация, которая указывается в элементах <named>, используется только для самодокументируемости: например, если при запуске сценария не будут указаны "обязательные" параметры, никакой ошибки не произойдёт.

Параметры типа "string" при вызове сценария задаются парами "имя:значение" (двоеточие является разделителем), примерно так:

/par1:"some value" /par2:40

Параметры типа "simple" при вызове сценария задаются без значений, примерно так:

/par1 /par2

Параметры типа "boolean" при вызове сценария задаются примерно так (минус означает "ложь", плюс - "истина"):

/par1- /par2+

Предложения в русском языке начинаются с большой буквы и заканчиваются точкой.
В названии ветки всегда должен быть указан язык программирования или среда исполнения скрипта, если это возможно.

4

Re: WSH: пишем сценарии в формате WSF

Элемент <unnamed>

Элементы <unnamed> описывают безымянные параметры командной строки сценария. Атрибуты:

- name - имя параметра, как оно будет описано при выводе информации о сценарии;
- helpstring - описание параметра;
- many - если "false", параметр может быть указан только один раз; по умолчанию - "true".
- required - если "true", "on" или 1, параметр является обязательным; по умолчанию - "false", "off" или 0; целое число, большее 1, указывает, сколько раз параметр обязательно должен быть указан.

Информация, которая указывается в элементах <unnamed>, используется только для самодокументируемости: например, если при запуске сценария не будут указаны "обязательные" параметры, никакой ошибки не произойдёт.

Элемент <description>

Текст, описывающий сценарий. Выводится на экран при запуске сценария с ключом /?.

Элемент <example>

Текст, описывающий пример запуска сценария. Выводится на экран при запуске сценария с ключом /? после текста, указанного в теге <description>.

Элемент <resource>

Позволяет отделить символьные или числовые константы от остального кода сценария. Для получения значения ресурса нужно вызвать метод getResource, передав в качестве параметра символьный идентификатор ресурса (значение атрибута id). Например:

<resource id="ver">0.001</resource>

Ver = getResource("ver")

Элемент <reference>

Обеспечивает доступ к мнемоническим константам, определённым в библиотеке типов нужного объекта (экземпляр объекта при этом не создаётся). Это позволяет не запоминать числовые значения констант, к которым постоянно нужно обращаться при написании кода.

Атрибут object задаёт программный идентификатор объекта. При необходимости можно указать версию объекта в атрибуте version. Вместо программного идентификатора объекта можно использовать глобальный код его библиотеки типов, задав его в атрибуте guid.

Например:

<reference object="Scripting.FileSystemObject"/>

s = ForReading

Предложения в русском языке начинаются с большой буквы и заканчиваются точкой.
В названии ветки всегда должен быть указан язык программирования или среда исполнения скрипта, если это возможно.

5

Re: WSH: пишем сценарии в формате WSF

В wsf-файлах можно использовать функцию createComponent(), чтобы из одного задания (<job>) вызвать по его id другое задание.

<?xml version='1.0' encoding='windows-1251' standalone='yes'?>
<package>
<job id='job1'>
<script language='VBScript'><![CDATA[
  WScript.Echo "Выполняется job1"
  Set obj = createComponent("job2")
  WScript.Echo "job1 продолжает работу"
  'WScript.Echo obj.t 'эта строка вызывает ошибку
                      '«Требуется объект 'obj'» даже, если
                      'снять комментарий с элемента <public> в job2 
  WScript.Echo VarType(obj)  '13 = vbDataObject
  WScript.Echo TypeName(obj) '"job2" — id вызывавшегося задания
]]></script>
</job>

<job id='job2'>
<!-- public><property name='t'/></public -->
<script language='VBScript'><![CDATA[
  Public t: t = 10
  WScript.Echo "Выполняется job2"
]]></script>
</job>
</package>

Похоже, из возвращаемого функцией объекта нельзя извлечь никакой пользы, и поэтому имеет смысл всегда вызывать её как процедуру; но здесь есть тонкости, о которых ниже.

Особенности запуска заданий с помощью createComponent():
1) не создаётся нового процесса WScript.exe или CScript.exe;
2) даже если файл уже удалён, можно вызывать описанные в нём задания;
3) вызванное задание имеет те же аргументы командной строки, входной и выходной потоки, что и вызвавшее (можно доказать, что они имеют общий объект WScript);
4) WScript.Quit() в вызванном задании завершает и вызвавшее;
5) имеются особенности связанные с действием раздела <runtime> и сообщением, выдаваемым методом WshArguments.ShowUsage(), о них ниже.

Благодарность за разъяснения - wisgest.

Предложения в русском языке начинаются с большой буквы и заканчиваются точкой.
В названии ветки всегда должен быть указан язык программирования или среда исполнения скрипта, если это возможно.

6

Re: WSH: пишем сценарии в формате WSF

О существовании функции createComponent() в WSF я узнал, выполнив

<job>
<script language='JScript'>
for (var i in this) WScript.Echo(i);
</script>
</job>

и получив ответ

WSH
WScript
getResource
createScriptlet
createComponent

Несколько замечаний:
WSH — то же, что и WScript: WSH===WScript;
— хотя createScriptlet!=createComponent, но различий между ними не заметно. Вероятно, createScriptlet() сохранена для совместимости с более ранней версией WSH (2.0?), так же как расширение «.sct» наряду с «.wsc» и элемент <scriptlet> наряду с <component>;
— если выполнить такой же код из «чистого» js-файла, получим:

WScript
WSH
i

т.е. можно заметить, что в первом случае переменная i не является перечисляемым свойством глобального объекта, а во втором — является (свойством она является по-любому).

Благодарность за разъяснения - wisgest.

Предложения в русском языке начинаются с большой буквы и заканчиваются точкой.
В названии ветки всегда должен быть указан язык программирования или среда исполнения скрипта, если это возможно.

7

Re: WSH: пишем сценарии в формате WSF

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

<package>
<job id='job1'>
<script language='VBScript'>
  Name = InputBox("Введите ваше имя:")
  If Name <> "" Then
    Set WshShell = CreateObject("WScript.Shell")
    Set WshEnvironment = WshShell.Environment("Process")
    WshEnvironment("Name") = Name
    createComponent "job2"
  End If
</script>
</job>

<job id='job2'>
<script language='VBScript'>
  Set WshShell = CreateObject("WScript.Shell")
  Name = WshShell.ExpandEnvironmentStrings("%Name%")
  WScript.Echo Name 'эта строка вставлена лишь для отладки,
                     'т.к. при пакетной обработке взаимодействие
                     'с пользователем не предполагается
  'Дальнейшая работа с переменной Name...
</script>
</job>
</package>

Благодарность за разъяснения - wisgest.

Предложения в русском языке начинаются с большой буквы и заканчиваются точкой.
В названии ветки всегда должен быть указан язык программирования или среда исполнения скрипта, если это возможно.

8

Re: WSH: пишем сценарии в формате WSF

Если wsf-файл содержит несколько заданий, использующих одинаковые объекты, функции, процедуры, константы, можно выделить их (функции, процедуры…) в отдельное задание и вызывать его как вспомогательный компонент. Для взаимодействия между заданиями можно использовать передачу ссылки через временное невидимое окно Internet Explorer (конечно, если в сценарии используется окно IE для создания пользовательского интерфейса, разумнее будет воспользоваться им). При этом самым простым будет передавать (на JScript) вызвавшему заданию ссылку на глобальный объект вспомогательного — все определённые во вспомогательном задании переменные и подпрограммы будут доступны как его свойства и методы.

<package>
<job id='job1'>
<script language='JScript'>
var IE=new ActiveXObject("InternetExplorer.Application");
IE.Navigate("about:blank"); while (IE.Busy) WScript.Sleep(100);
IE.Document.Script.name="mailbox";

var job3=createComponent("job3");
job3=IE.Document.Script.job3;

IE.Document.Script.job3=null; IE.Quit(); delete IE;
</script>

<script language='VBScript'>
  WScript.Echo "7! = " & job3.Factorial(7)
  WScript.Echo "job3.t = " & job3.t
</script>
</job>


<job id='job2'>
<script language='JScript'>var job2=this;</script>
<script language='VBScript'>
  Set IE = CreateObject("InternetExplorer.Application")
  IE.Navigate("about:blank"): While IE.Busy: WScript.Sleep 100: Wend
  IE.Document.Script.name = "mailbox"

  'Set job3 = createComponent("job3")
  job2.eval("createComponent('job3')")
  Set job3 = IE.Document.Script.job3

  'Set IE.Document.Script.job3 = Nothing
  IE.Quit: Set IE = Nothing

  WScript.Echo "7! = " & job3.Factorial(7)
  WScript.Echo "job3.t = " & job3.t
</script>
</job>


<job id='job3'>
<script language='VBScript'>
  Const t = 10

  Function Factorial(N)
    Factorial = 1
    For I = 1 To N: Factorial = Factorial * I: Next
  End Function
</script>

<script language='JScript'>
(function () {
  var t=11;
  var ShellWindows=(new ActiveXObject("Shell.Application")).Windows();
  for (var i=ShellWindows.Count; --i>=0;)
    try {
      var Script=ShellWindows.Item(i).Document.Script;
      if (Script.name=="mailbox") {Script.job3=this; return;}
    } catch (Err) {}
})();
</script>
</job>
</package>

В приведённом примере job3 — вспомогательное задание, предоставляющее константу t=10 и функцию для вычисления факториала; job1 и job2 — задания, использующие job3 (для вызова job2 необходимо указывать параметр //job:job2, оно делают то же, что и job1, и создано лишь для показа некоторых особенностей, связанных с использованием VBScript).

• Самое важное! Обратим внимание на строку 8:

var job3=createComponent("job3");

— кажется, что в этом месте нет смысла вызывать createComponent() как функцию, т.к. возвращаемое ей значение не используется и теряется уже при выполнении следующего оператора (можно сделать, например, так:

var temp=createComponent("job3"); delete temp;

и даже так:

try {delete createComponent("job3");} catch (e) {}

). Но, если её заменить на

createComponent("job3");

при попытке получить какое либо свойство объекта job3 возникает ошибка, — по видимому, в таком случае они уничтожаются при завершении задания job3. Это является намёком на то, что, возможно, возвращаемое значение не так бесполезно, и если знать, как с ним работать, не пришлось бы прибегать ко всей этой акробатике.

• На VBScript (задание job2) результат оказывается отрицательным как при вызове createComponent() как процедуры, так и как функции. Поэтому, вместо закомментированной строки 28 пришлось призвать на помощь JScript: в строке 29 с помощью JScript-функции eval() вызывается интерпретатор JScript.

• Для того, чтобы не загромождать основной код вопросами установления взаимодействия между заданиями, можно JScript-сценарий из job1 выделить в отдельный файл и включать его (с помощью тега <script> с атрибутом src) в начало кода всех заданий использующих вспомогательное. Так же в отдельный файл можно выделить JScript-сценарий из job3 (его можно вставлять как в начале, так и в конце).

• Для временного невидимого окна IE вместо "mailbox" надёжнее будет использовать сгенерированное случайным образом имя и передавать его через переменную окружения.

• В job3 сценарий (на JScript) установления взаимодействия между заданиями выполнен в виде вызова анонимной функции, чтобы определённые в нём переменные остались локальными по отношению к сценарию.

Благодарность за разъяснения - wisgest.

Предложения в русском языке начинаются с большой буквы и заканчиваются точкой.
В названии ветки всегда должен быть указан язык программирования или среда исполнения скрипта, если это возможно.

9

Re: WSH: пишем сценарии в формате WSF

Если job2 вызывается из job1, то сообщение, выдаваемое методом WshArguments.ShowUsage() в job2 и в job1 после возврата из job2, строится на основании разделов <runtime> обоих заданий; при этом, если в job2 имеются элементы <description> и <example>, то они перекрывают действие соответствующих элементов из job1. — Это кажется обнадёживающим, как слабый намёк на возможность динамического построения сценариев (возможно, с помощью объекта, возвращаемого createComponent()).
Пример:

<package>
<job id='job1'>
<runtime>
  <named name='a' helpstring='строка' type='string'/>
  <named name='b' helpstring='ключ'/>
  <description>Первый</description>
  <example>Пример 1</example>
</runtime>
<script language='VBScript'>
  'WScript.Echo "Hello!"
  WScript.Arguments.ShowUsage
  createComponent "job2"
  WScript.Arguments.ShowUsage
</script>
</job>

<job id='job2'>
<runtime>
  <named name='b' helpstring='строка' type='string'/>
  <named name='с' helpstring='переключатель' type='boolean'/>
  <description>Второй</description>
  <example>Пример 2</example>
</runtime>
<script language='VBScript'>
  WScript.Arguments.ShowUsage
</script>
</job>
</package>

Первый вызов .ShowUsage() (из job1 до вызова job2) возвратит:

Первый
Использование: test.wsf [/a:значение] [/b]

Параметры:

a : строка
b : ключ
Пример 1

Второй (из job2) и третий (из job1 после возврата из job2):

Второй
Использование: test.wsf [/a:значение] [/b] [/b:значение] [/с[+|-]]

Параметры:

a : строка
b : ключ
b : строка
с : переключатель
Пример 2

Для полноты картины попробуйте убрать элементы <description> и <example> в каждом из заданий примера по отдельности.

Благодарность за разъяснения - wisgest.

Предложения в русском языке начинаются с большой буквы и заканчиваются точкой.
В названии ветки всегда должен быть указан язык программирования или среда исполнения скрипта, если это возможно.

10

Re: WSH: пишем сценарии в формате WSF

Существует такой способ обработки событий объектов:

<job>
    <object progid="Word.Application" id="objWord" events="true"/>
    <script language="VBScript">
        Sub objWord_Quit()
            WScript.Echo "_Quit firing"
            WScript.Quit 0
        End Sub
        
        'Sub objWord_Document_New()
        '    WScript.echo "_Document_New firing"
        'End Sub
        
        With objWord
            .Visible = True
            '.Documents.Add
            .Quit
        End With
        
        Do
            WScript.Sleep 1000
        Loop
    </script>
</job>

Однако, практически, таким способом удается работать не со всеми событиями, и даже не со всеми объектами.
Благодарность за пример - alexii.

Предложения в русском языке начинаются с большой буквы и заканчиваются точкой.
В названии ветки всегда должен быть указан язык программирования или среда исполнения скрипта, если это возможно.