1

Тема: WSH: пишем COM-серверы в формате Windows Script Component (WSC)

Пример создания минимального COM-сервера Windows Script Component (WSC). Код компонента (сохраните как файл .wsc):

<?xml version='1.0' encoding='windows-1251' standalone='yes'?>
<component>
<registration progid='FoldersTreeEnumerator'/>
<public>
  <method name='ConnectToFolder'/>
  <method name='item'/>
  <method name='atEnd'/>
  <method name='moveNext'/>
</public>

<object id='fso' progid='Scripting.FileSystemObject'/>

<script language="JScript"><![CDATA[
var Item, Top;

function ConnectToFolder(FolderPath) {
  Item=fso.GetFolder(FolderPath);
  Top=null;
}

function item() {return Item;}

function atEnd() {return !Item;}

function moveNext() {
  Top={
    SubFolders: new Enumerator(Item.SubFolders),
    Parent: Top
  };
  while (!(Item=Top.SubFolders.item())) {
    if (!(Top=Top.Parent)) return;
    Top.SubFolders.moveNext();
  }
}
]]></script>
</component>

Зарегистрируйте компонент командой наподобие следующей:

regsvr32 "%SystemRoot%\system32\test.wsc"

Пример скрипта, использующего компонент (рекурсивно выведет подкаталоги каталога C:\Temp):

Set t = CreateObject("FoldersTreeEnumerator")
t.ConnectToFolder "C:\Temp"
While Not t.atEnd()
  WScript.Echo t.item().Path
  t.moveNext
Wend

Автор примера - wisgest.

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

2

Re: WSH: пишем COM-серверы в формате Windows Script Component (WSC)

С помощью технологии Windows Script Components можно зарегистрировать в системе COM-объект, написанный на языке JScript или VBScript. Любой сценарий WSH можно упаковать в COM-объект и использовать его в приложениях, написанных на любом языке, который позволяет подключать подобные внешние объекты.

Механизм работы объектов-сценариев базируется на технологии ActiveX Scripting. Основную роль здесь играет динамическая библиотека scrobj.dll, которая является оболочкой компонентов-сценариев и отвечает за функционирование файла-сценария в качестве COM-объекта. Оболочка компонентов-сценариев scrobj.dll отвечает за то, чтобы при вызове из внешнего приложения метода объекта-сценария или обращении к его свойству запускалась соответствующая функция, описанная в этом сценарии.

При регистрации объекта-сценария в разделе реестра HKEY_CLASSES_ROOT\CLSID\ создаётся новый подраздел, название которого совпадает с CLSID регистрируемого объекта. В этом подразделе создаётся подраздел InprocServer32, значением по умолчанию которого является полный путь к библиотеке scrobj.dll. Кроме того, там же создаются подразделы ProgID (программный идентификатор объекта) и ScriptletURL (полный путь к файлу объекта-сценария).

При создании экземпляра объекта из внешнего приложения в разделе реестра HKEY_LOCAL_MACHINE\SOFTWARE\Classes\ ищется подраздел с именем, которое совпадает с программным идентификатором объекта (такой подраздел создаётся при регистрации объекта-сценария). Определяется CLSID объекта (соответствующий параметр найденного подраздела). После этого разыскивается подраздел с именем, которое совпадает с найденным CLSID объекта, в разделе реестра HKEY_CLASSES_ROOT\CLSID\. По значению подраздела InprocServer32 загружается scrobj.dll, которая в свою очередь уже загружает файл самого сценария, используя значение подраздела ScriptletURL.

Компоненты-сценарии, реализованные по технологии Windows Script Components, представляют из себя файлы с расширениями wsc (или sct), которые содержат специальную XML-разметку (объектная модель WSC XML).

Одним из главных преимуществ WSC является возможность повторного использования кода. Такие компоненты могут свободно использоваться в других сценариях, а также в любых языках программирования, поддерживающих технологию COM и способных выступить в роли OLE-клиента.

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

3

Re: WSH: пишем COM-серверы в формате Windows Script Component (WSC)

Обязательными для создания компонента-сценария являются элементы <component>, <registration>, <public> и <script>.

Комментарии

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

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

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

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

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

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

Элемент <package>

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

Элемент <component>

Описывает COM-объект. Необязательный атрибут id определяет идентификатор объекта, если в одном WSC-файле находятся несколько COM-объектов. Элемент <component> должен содержать один элемент <registration> и один элемент <public>.

Элемент <registration>

Содержит информацию для регистрации COM-объекта в системе. Атрибуты progid и classid задают соответственно программный идентификатор и CLSID. Из этих двух атрибутов обязательно должен быть указан хотя бы один. Если classid не указан, этот код может оказаться различным при регистрации объекта на разных машинах.

Атрибут description может задавать краткое описание объекта, которое при регистрации попадёт в реестр.

Атрибут version позволяет указать номер версии COM-объекта. Этот номер позволяет запрашивать из приложения определённую версию COM-объекта, например, "MyClass.MyObject.1".

C помощью элемента <script> внутри контейнера <registration> можно указать две функции, одна из которых будет вызываться при регистрации объекта в системе, а другая - при удалении объекта из системы. Эти функции должны иметь имена соответственно Register() и Unregister().

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

4

Re: WSH: пишем COM-серверы в формате Windows Script Component (WSC)

Элемент <public>

Может содержать один или несколько элементов <property>, <method> или <event>.

Элемент <property>

Объявляет свойство, которое будет доступно для клиентов автоматизации. Атрибут name определяет имя свойства.

Объявляемое свойство может быть доступно только для чтения, только для записи, или для чтения и записи. Это определяется наличием внутри контейнера <property> элементов <get/> и <put/>.

Атрибут internalName в элементах <get/> и <put/> задаёт имена функций, которые будут использоваться для чтения и записи свойства соответственно. Эти функции описываются внутри контейнера <script>. Если атрибут internalName не указан, необходимо задать функции get_PropertyName и/или put_PropertyName, где PropertyName - имя свойства (атрибут name).

Элемент <method>

Объявляет метод, который будет доступен для клиентов автоматизации. Атрибут name определяет имя метода. Если не указан атрибут internalName, внутри контейнера <script> должна быть описана функция или процедура с таким именем.

Список параметров метода (если они есть) задаётся внутри элемента <method> с помощью элементов <parameter>, каждый из которых должен содержать артрибут name.

Элемент <event>

Объявляет событие, которое может обрабатываться клиентами автоматизации. Атрибут name определяет имя события.

Атрибут dispid может содержать числовой идентификатор интерфейса события.

Чтобы вызвать наступление события, внутри элемента <script> используется функция fireEvent(), с именем нужного события в качестве параметра.

Элемент <resource>

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

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

Ver = getResource("ver")

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

5

Re: WSH: пишем COM-серверы в формате Windows Script Component (WSC)

Элемент <object>

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

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

Элемент <reference>

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

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

Например:

<reference object="Scripting.FileSystemObject"/>

s = ForReading

Элемент <script>

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

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

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

6

Re: WSH: пишем COM-серверы в формате Windows Script Component (WSC)

С помощью элемента <?component?> может быть реализована более углубленная проверка ошибок. Используются два атрибута: error и debug. Установка error="true" позволит выводить более подробные сообщения об ошибках. Например, при невозможности создания объекта вы получите сообщение типа "Невозможно создание объекта контейнером ActiveX". Если же атрибут error не задан, вы получите сообщение типа "Неопознанная ошибка". Установка debug="true" даст возможность пошагового выполнения кода сценария в отладчике - в случае возникновения ошибки будет выдано окно с предложением запустить отладчик (на выбор - из всех отладчиков, установленных в системе). Элемент <?component?> можно задать в начале файла, непосредственно после элемента <?xml?>, например:

<?component error="true" debug="true"?>

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

7

Re: WSH: пишем COM-серверы в формате Windows Script Component (WSC)

В WSC можно использовать объект WScript, если он передан туда из клиентского сценария. Для этого можно объявить свойство наподобие такого:

<property name="ScriptingHost" internalName="WScript"/>

Если это свойство правильно установлено клиентским скриптом, при использовании свойств и методов этого объекта в коде WSC они будут работать в контексте клиентского скрипта. Примером может являться компонент C:\WINDOWS\system32\cmdlib.wsc, поставляемый с Windows XP. Пример использования:

Set CmdLib = CreateObject("Microsoft.CmdLib")
Set CmdLib.ScriptingHost = WScript
WScript.Echo CmdLib.checkScript()

Функция checkScript() вернёт 2 в случае, если клиентский скрипт запущен с помощью cscript.exe, и 0 в остальных случаях.
Благодарность за разъяснения - wisgest.

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

8

Re: WSH: пишем COM-серверы в формате Windows Script Component (WSC)

Можно использовать в WSC объекты собственных классов, с собственными свойствами и методами, и передавать эти объекты клиентским скриптам. Демонстрация:
C:\Temp\test.wsc

<?xml version="1.0" encoding="windows-1251" standalone="yes"?>
<?component error="true" debug="true"?>
<component>
<registration progid="SomeProgId"/>
<public>
    <method name="Construct"/>
    <method name="Destruct"/>
</public>
<script language="VBScript"><![CDATA[
    ' --------------------------------------------------
    Function Construct()
        Set Construct = New MyClass
    End Function
    ' --------------------------------------------------
    Sub Destruct(ByRef objMyClass)
        Set objMyClass = Nothing
    End Sub
    ' --------------------------------------------------
    Class MyClass
        Private intX1
        Private intX2
        
        Public Property Let x1(intValue)
            intX1 = intValue
        End Property
        
        Public Property Let x2(intValue)
            intX2 = intValue
        End Property
        
        Public Function Add()
            Add = intX1 + intX2
        End Function
    End Class
]]></script>
</component>

Клиентский скрипт:

Set objWSC = GetObject("script:C:\Temp\test.wsc")
Dim objMyClass

WScript.Echo "IsObject(objMyClass): " & IsObject(objMyClass)

Set objMyClass = objWSC.Construct()
boolRes = objMyClass Is Nothing
WScript.Echo "IsObject(objMyClass): " & IsObject(objMyClass), "; objMyClass Is Nothing: " & boolRes

objMyClass.x1 = 2
objMyClass.x2 = 3
WScript.Echo objMyClass.Add()

' Первый путь очистки
'Set objMyClass = Nothing
'boolRes = objMyClass Is Nothing
'WScript.Echo "IsObject(objMyClass): " & IsObject(objMyClass), "; objMyClass Is Nothing: " & boolRes

' Второй путь очистки, через подобие destructor'а
objWSC.Destruct objMyClass
boolResult = objMyClass Is Nothing
WScript.Echo "IsObject(objMyClass): " & IsObject(objMyClass), "; objMyClass Is Nothing: " & boolResult

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

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

9

Re: WSH: пишем COM-серверы в формате Windows Script Component (WSC)

Специфические функции, которые можно использовать в коде WSC.

value = createComponent(componentID)

Возвращает ссылку на другой COM-объект (с другим ProgID) из этого же самого пакета (wsc-файла). Параметр componentID - уникальный идентификатор (атрибут "id") компонента. Таким образом, можно использовать функциональные возможности "соседнего" компонента из этого же самого wsc-файла.

fireEvent(eventName)

Возбуждает указанное событие. Параметр eventName - имя события, как оно определено в элементе <event>, который должен быть объявлен в элементе <public>.

value = getResource(resourceID)

Получает значение ресурса, определенного в элементе <resource>. Параметр resourceID - уникальный идентификатор ресурса в пределах компонента.

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

10

Re: WSH: пишем COM-серверы в формате Windows Script Component (WSC)

В функциях, которые будут использоваться для чтения и записи объявленного свойства COM-сервера, можно использовать аргументы. Это позволит применять в клиентском скрипте синтаксис "свойств с параметрами". Например, код сервера:

<?xml version='1.0' encoding='windows-1251' standalone='yes'?>
<component>
<registration progid='testWSC'/>
<public>
    <property name='testProperty'><get/><put/></property>
</public>

<script language="JScript"><![CDATA[
var test;

function get_testProperty() {
    return test;
}

function put_testProperty(arg1, arg2) {
    test = arg1 * arg2;
}

]]></script>
</component>

После этого, например, в клиентском JS-скрипте будет вполне допустимо писать что-то вроде:

oTest = new ActiveXObject("testWSC")
oTest.testProperty(5) = 3;
WScript.Echo(oTest.testProperty); // 5 * 3 = 15
oTest.testProperty(5, 4) = 3;
WScript.Echo(oTest.testProperty); // 5 * 4 = 20
oTest.testProperty(5, 8, 6) = 3;
WScript.Echo(oTest.testProperty); // 5 * 8 = 40
Предложения в русском языке начинаются с большой буквы и заканчиваются точкой.
В названии ветки всегда должен быть указан язык программирования или среда исполнения скрипта, если это возможно.

11

Re: WSH: пишем COM-серверы в формате Windows Script Component (WSC)

Динамическое создание новых "свойств" объекта на лету, по строковому имени.
Код сервера:

<?xml version='1.0' encoding='windows-1251' standalone='yes'?>
<component>
<registration progid='testWSC'/>
<public><property name='hash'><get/><put/></property></public>
<script language="JScript"><![CDATA[

var hash = {};
function get_hash() {
  if(arguments.length)
    return hash[arguments[0]];
  else
    return hash;
}

function put_hash() { 
  var key = arguments[0], prop = arguments[1];
  hash[key] = prop;
}

]]></script>
</component>

Код клиента (JScript):

var wsc = WSH.CreateObject("testWSC");
wsc.hash('test') = 'one'; // создание нового "свойства"
WSH.echo(wsc.hash('test')); // one
WSH.echo(wsc.hash.test); // one

wsc.hash.test = "two";
WSH.echo(wsc.hash.test); // two

wsc.hash("test") = "three";
WSH.echo(wsc.hash("test")); // three

Код клиента (VBScript):

Set wsc = CreateObject("testWSC")
wsc.hash("test") = "one" ' создание нового "свойства"
WSH.echo wsc.hash("test") ' one
WSH.echo wsc.hash.test ' one

wsc.hash.test = "two"
WSH.echo wsc.hash.test ' two

wsc.hash("test") = "three"
WSH.echo wsc.hash("test") ' three

Автор решения — YMP.

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

12 (изменено: wisgest, 2014-01-02 05:16:08)

Re: WSH: пишем COM-серверы в формате Windows Script Component (WSC)

Решение из темы «AHK: Выполненние JScript-кода с помощью WSC» можно использовать и в других языках программирования, которые в состоянии выступить в качестве OLE-клиента. Например, в VBScript вместо AutoHotkey-функции ComObjGet() надо использовать GetObject().