![]() |
Технология Клиент-Сервер 2000'1 |
||||||
|
Главными целями разработки СОМ были создание распределенной компонентной архитектуры, взаимодействие компонентов во время исполнения, поддержка автоматизации приложений и совместимость со окриптовыми языками. СОМ старается быть настолько терпимым к языку, насколько это возможно, и теперь вы можете использовать скрипты не только для использования готовых СОМ-объектов, но и для создания новых. Windows Script Components (WSC) позволяют упаковывать скрипты для использования в качестве СОМ-компонентов.
В общем, WSC - это просто XML-файлы, содержащие код скриптов, и прикидывающихся СОМ-классами. В этой статье мы покажем, как работать с такими файлами. XML - язык разметки, схожий с HTML и позволяющий вставлять теги в документ. Но XML гораздо более гибкок. Хотя и XML, и HTML используются для описания форматирования и экранных характеристик документа, XML может гораздо больше. Например, он может описать семантику и организационную структуру документа.
Создание компонентов на базе скриптов влечет за собой написание небольшого XML-файла, соответствующего синтаксису WSC. Листинг 1 показывает формат такого файла. Код этого файла представляет инфраструктуру скрипт-компонента. Обратите внимание на теги.
<?xml version="1.0" encoding="windows-1251" ?> <component> <?component error="true" debug="true"?> <registration description="BareBones" progid="BareBones.WSC" version="1.00" classid="{eeb9e710-9c22-11d3-8069-367a6d000000}" > </registration> <public> </public> <script language="VBScript"> <![CDATA[ ]]> </script> </component>
Выражение в первой строке Листинга 1
<?xml version="1.0" encoding="windows-1251" ?>
говорит скрипт-машине, что XML-файл соответствует стандарту XML 1.0. (Соответствие XML отключается простым выкидыванием этой строки). При включенном соответствии XML имена элементов и атрибуты зависимы от регистра, а значения атрибутов должны быть заключены в вопросительные знаки. Также, зарезервированные XML-символы типа < и > должны быть четко разграничены при использовании в скрипте. Например, знак < может означать "меньше" в скриптовом языке, но он еще означает начало XML-тега. Особое внимание обратите на строку encoding="windows-1251". В принципе, без нее можно было бы обойтись, но руских букв в скрипте при этом использовать будет нельзя. Windows-1251 можно заменить на UTF-8, KOI8R или еще что нибудь, но зачем?..
Вот набор обязательных тегов:
Чтобы опробовать эту технологию, создадим конвертер целых чисел в строку прописью (на VBScript) и конвертер миль в километры (на Jscript).
Проект будет состоять из двух простых функций: L2TR и Convert. Компонент будет также иметь свойства TextRus - текст прописью на русском языке, доступное только на чтение, и Value - целое число, которое надо перевести в строку прописью, доступное как для чтения, так и для записи. Листинг 2 содержит исходный текст компонента. Функции с префиксами get_ и put_ и именами, совпадающими с именами описанных свойств, являются реализацией методов доступа для этих свойств.
<?xml version="1.0" encoding="windows-1251" ?> <component> <?component error="true" debug="true"?> <registration description="Converter VBScript Component" progid="Converter.Long2TxtRus" version="1.00" classid="{5B4C45F9-B9E9-4789-AF25-3312D3D875A6}" > </registration> <public> <property name="Value" dispid = "1"> <get/> <put/> </property> <property name="TextRus" dispid = "2"> <get/> </property> <method name="Convert" dispid = "3"> </method> <method name="L2TR" dispid = "4"> <PARAMETER name="nVal"/> </method> </public> <script language="VBScript"> <![CDATA[ dim lValue lValue = 0 dim sTextRus sTextRus = "" function get_Value() get_Value = lValue end function function put_Value(newValue) lValue = newValue end function function get_TextRus() get_TextRus = sTextRus end function Sub Convert() sTextRus = L2TR(lValue) End Sub Function L2TR(nVal) Dim str1 str1 = Trim(IntrnalL2TR(nVal)) L2TR = UCase(Left(str1, 1)) & Mid(str1, 2) End Function Function IntrnalL2TR(nVal) mass1 = Array("один", "два", "три", "четыре", "пять", "шесть", _ "семь", "восемь", "девять", "десять", "одинадцать", _ "двенадцать", "тринадцать", "четырнадцать", _ "пятнадцать", "шестнадцать", "семнадцать", _ "восемнадцать", "девятнадцать") mass2 = Array("двадцать", "тридцать", "сорок", "пятьдесят", _ "шестьдесят", "семьдесят", "восемьдесят", "девяносто") str1 = "" Dim nVal1 If (nVal >= 1000000000) Then nVal1 = Int(nVal / 1000000000) str1 = str1 + IntrnalL2TR(nVal1) + "миллиард" nVal1 = Int(nVal1 Mod 100) If (nVal1 < 5 Or nVal1 > 20) Then nVal1 = Int(nVal1 Mod 10) If (nVal1 = 1) Then str1 = str1 + "" ElseIf (nVal1 > 1 And nVal1 < 5) Then str1 = str1 + "а" Else str1 = str1 + "ов" End If Else str1 = str1 + "ов" End If nVal1 = Int(nVal - nVal1 * 1000000000) str1 = str1 + " " + IntrnalL2TR(nVal1) ElseIf (nVal >= 1000000) Then nVal1 = Int(nVal / 1000000) str1 = str1 + IntrnalL2TR(nVal1) + "миллион" nVal1 = Int(nVal1 Mod 100) If (nVal1 < 5 Or nVal1 > 20) Then nVal1 = Int(nVal1 Mod 10) If (nVal1 = 1) Then str1 = str1 + "" ElseIf (nVal1 > 1 And nVal1 < 5) Then str1 = str1 + "а" Else str1 = str1 + "ов" End If Else str1 = str1 + "ов" End If nVal1 = Int(nVal Mod 1000000) str1 = str1 + " " + IntrnalL2TR(nVal1) ElseIf (nVal >= 1000) Then nVal1 = Int(nVal / 1000) str1 = str1 + IntrnalL2TR(nVal1) + "тысяч" nVal1 = Int(nVal1 Mod 100) If (nVal1 < 5 Or nVal1 > 20) Then nVal1 = Int(nVal1 Mod 10) If (nVal1 = 1) Then str1 = ATREPL("один", str1, "одна") str1 = str1 + "а" ElseIf (nVal1 > 1 And nVal1 < 5) Then str1 = ATREPL("два", str1, "две") str1 = str1 + "и" End If End If nVal1 = Int(nVal Mod 1000) str1 = str1 + " " + IntrnalL2TR(nVal1) ElseIf (nVal >= 100) Then nVal1 = Int(nVal / 100) If (nVal1 = 1) Then str1 = str1 + "сто " Else If (nVal1 = 2) Then str1 = str1 + "двести " Else If (nVal1 >= 3 And nVal1 <= 4) Then str1 = str1 + mass1(nVal1 - 1) + "ста " Else If (nVal1 >= 5) Then str1 = str1 + mass1(nVal1 - 1) + "сот " Else str1 = str1 + "" End If End If End If End If nVal1 = Int(nVal Mod 100) str1 = str1 + IntrnalL2TR(nVal1) ElseIf (nVal >= 20) Then str1 = str1 + mass2(Int(nVal / 10) - 2) + " " nVal1 = Int(nVal Mod 10) str1 = str1 + IntrnalL2TR(nVal1) ElseIf (nVal >= 1 And nVal <= 19) Then str1 = str1 + mass1(Int(nVal) - 1) End If IntrnalL2TR = Trim(str1) & " " End Function Function ATREPL(str0, str1, str2) 'ATREPL("два", str1, "две") pos = InStr(str1, str0) Const LenTwenty = 8 If pos > 0 Then If Mid(str1, pos, LenTwenty) = "двадцать" Then ATREPL = Left(str1, pos - 1) + str2 + Mid(str1, pos + Len(str0)) Else ATREPL = str1 End If Else ATREPL = str1 End If End Function ]]> </script> </component>
В добавление к инфраструктуре скрипта, показанной в Листинге 1, конвертер из Листинга 2 включает теги <property> и <method> и некоторый код внутри тега <component>. Тег <property> означает свойство объекта, доступные клиентам. Свойство доступно для записи, если в его описании присутствует тег <put/> и для чтения - если <get/>. Тег <method> описывает доступные методы объекта и их параметры (через тег <PARAMETER>). Информация, содержащаяся в этой части XML, чисто декларативна. Настоящие определения свойств и методов находятся в секции Script.
Реальный код скрипта начинается с тега <script>. Поскольку тег <script> указывает на использование VBScript, переменные и методы определяются через VBScript-выражение dim. Глобальные переменные (доступные во всем скрипте) объявляются в самом начале, до объявления первого метода. Код, размещенный вне методов, будет аналогичен коду, размещенному в конструкторе объектно-ориентированного языка. К сожалению, аналога деструктору не предусмотрено.
Протестировать только что созданный компонент можно опять же с помощью VBScript. Для этого создайте .vbs-файл следующего содержания:
Dim c Set c = CreateObject("Converter.Long2TxtRus") Err.Clear On Error Resume Next Dim n n = CLng(InputBox("Введите номер", "Script Component Test", 1234567890)) If Err.Number <> 0 Or n = 0 Then MsgBox "Неправильно набран номер. Введите целое число, не равное нулю." Else MsgBox n & " пишется так: " & vbCrLf & c.L2TR(n) End If
В файле для второго компонента (см. Листинг 3) определяет свойства CumeMiles и CumeKilometers, доступные для записи и чтения и функции MilesToKilometers и KilometersToMiles.
<?xml version="1.0" encoding="windows-1251"?> <component> <?component error="true" debug="true"?> <registration description="DistanceConverterJScript" progid="DistanceConverterJScript.WSC" version="1.00" classid="{f7b9ed70-9dd4-11d3-806a-a6b1d8000000}" > </registration> <public> <property name="CumeMiles"> <get/> <put/> </property> <property name="CumeKilometers"> <get/> <put/> </property> <method name="MilesToKilometers"> <PARAMETER name="Miles"/> </method> <method name="KilometersToMiles"> <PARAMETER name="Kilometers"/> </method> </public> <script language="JScript"> <![CDATA[ var description = new DistanceConverterJScript; function DistanceConverterJScript() { this.get_CumeMiles = get_CumeMiles; this.put_CumeMiles = put_CumeMiles; this.get_CumeKilometers = get_CumeKilometers; this.put_CumeKilometers = put_CumeKilometers; this.MilesToKilometers = MilesToKilometers; this.KilometersToMiles = KilometersToMiles; } var CumeMiles = 0; var CumeKilometers = 0; function get_CumeMiles() { return CumeMiles; } function put_CumeMiles(newValue) { CumeMiles = newValue; } function get_CumeKilometers() { return CumeKilometers; } function put_CumeKilometers(newValue) { CumeKilometers = newValue; } function MilesToKilometers(Miles) { CumeMiles += Miles; CumeKilometers += Miles * 5 / 2; return Miles *5 / 2; } function KilometersToMiles(Kilometers) { CumeKilometers += Kilometers; CumeMiles += Kilometers * 2 / 5; return Kilometers * 2 / 5; } ]]> </script> </component>
Главное различие между компонентами на VBScript и JScript обнаруживается в скрипт-секции WSC-файла.
Думаю, вам не составит труда собственноручно написать файл, запускающий этот компонент.
Теперь, когда у вас есть эти два файла с неким кодом скрипта внутри, как сделать из них СОМ-объекты? На этот вопрос проще всего ответить, посмотрев вхождения реестра для компонента, написанного на скриптовом языке. Но предварительно его надо зарегистрировать. О том, как это сделать, можно прочесть в разделе "Регистрация скрипт-объекта", а пока расмотрим описание, полученое из нашего реестра:
HKCR CLSID {F3DA80D0-9B63-11D3-8069-367A6D000000} InprocServer32 = c:\WinNT\System32\srcobj.dll ThreadingModel = "Apartment" ProgID = "DistanceConverterJScript.WSC.1.00" ScriptletURL = "file://D:DistanceConverterJScript.wsc"
За превращение XML-файла в СОМ-объект отвечает DLL с названием SRCOBJ.DLL. Когда скрипт-компоненты регистрируются как СОМ-объекты - наиболее важен здесь ключ реестра InprocServer32. Чтобы заставить скрипт работать, это ключ должен указывать на SRCOBJ.DLL. В дополнение к ключу InprocServer32 имеется ключ ProgID и ключ ScripletURL. В ключе ScripletURL хранится путь к WSC-файлу.
Для использования скриптового объекта ваш код просто вызывает обычные методы активации СОМ (CoCreateInstance для разработчиков, использующих C++, CreateObject - для использующих Visual Basic и скрипты) - так же, как и для любого другого СОМ-объекта. COM-runtime просматривает ключ CLSID, находит DLL, которую нужно загрузить, используя значение, ассоциированное с ключом InprocServer32, и загружает оболочку скрипт-объекта (SRCOBJ.DLL). Затем DLL-оболочка скрипт-объекта загружает файл, указанный в ключе ScriptletURL и перенаправляет вызовы методов в скрипт (реализуя Idispatch-интерфейс на базе описания из wsc-файла. Все это делается на базе технологии Active Scripting. То есть, SCROBJ.DLL реализует IActiveScriptSite и управляет выбранной машиной через ее интерфейсы IActiveScript и IActiveScriptParse.
Написав файл скрипт-объекта, нужно внести информацию в реестр. Это можно сделать несколькими способами. Можно использовать regsvr32 - утилиту используемую для регистрации любого СОМ-класса - или контекстное меню в Windows Explorer.
Новая версия regsvr32 поставляется со скрипт-компонентами. Командная строка выглядит примерно так:
regsvr32 file:\\d:\DistanceConverterJScript.wsc
Если новой версии у вас нет, вы можете использовать старую версию этой утилиты следующим образом:
regsvr32 scrobj.dll /n /i:file:\\d:\DistanceConverterJScript.wsc
Но самый простой путь зарегистрировать компонент - найти WSC-файл в Windows Explorer, щелкнуть по нему правой кнопкой мыши и выбрать Register из контекстного меню.
Клиенту обычно все равно, что делается за завесой интерфейсов. Скрипт-компоненты - это просто обычные СОМ-компоненты в одеждах скриптов. Естественно, если у вас есть компонент, раньше или позже у вас появиться клиент, желающий использовать библиотеку типов, описывающую ваш компонент.
Когда это случится, вы должны будете создать для вашего скрипт-компонента библиотеку типов одним из трех способов. Первый - программно создать библиотеку типов используя runtime-генератор библиотеки типов скрипт-компонента. В скрипт-компонент можно встроить генератор библиотеки типов с помощью CreateObject, дающей указатель на генератор библиотеки типов. Затем вы указываете генератору библиотеки типов на WSC-файл, присваиваете ей имя и просите генератор создать ее. Когда вы помещаете код регистрации в секцию <registration> WSC-файла, библиотека типов будет создана при регистрации скрипт-компонента обычным путем.
Второй способ - попросить WSC-файл создать библиотеку типов, используя утилиту RUNDLL32. SCROBJ.DLL имеет точку входа с именем GenerateTypeLib, которая сгенерирует библиотеку типов на основе данных параметров командной строки. Например, следующая командная строка создаст библиотеку типов для скрипт-компонента:
rundll32.exe c:\winnt\system32\scrobj.dll,GenerateTypeLib -name:DistanceConverterJScriptTLib -file:d:\DistanceConverterJScript.tlb -doc:\"DistanceConverterJScript typelib\" -guid:{6576834a-a252-11d1-9fa1-00a0c90fffc0} -major:1 -minor:0 -URL:d:\components\MyComponent.wsc
Наконец, третий и простейший путь - найти файл скрипта в Windows Explorer, щелкнуть по нему правой кнопкой мыши и выбрать Generate Type Library из контекстного меню.
Как всегда с СОМ, создание скрипт-компонентов связано с написанием прорвы кода. Это достаточная причина, чтобы использовать Wizard для создания скрипт-компонентов. Его можно получить с WWW-сайта Microsoft: http://msdn.microsoft.com/scripting.
Первый экран просит ввести имя вашего компонента и указать каталог для размещения исходного текста. Второй экран предлагает выбрать используемый скриптовый язык. Вы можете выбрать VBScript, JScript или любой другой скриптовый язык, если у вас, разумеется, установлена соответствующая скрипт-машина.
Вы можете также приказать wizard'у добавить к объекту образ действий Dynamic HTML или обработчик ASP. Можно включить или отключить отладку и обработку ошибок. Обработка ошибок заставляет скрипт-машину генерировать сообщения об ошибках исполнения и синтаксиса, а опция отладки вызывает отладчик скрипта в случае ошибки.
Наконец, Script Component Wizard выводит три экрана для добавления к компоненту свойств, методов и событий. В результате получается простыня кода, что экономит массу времени при разработке.
СОМ позволяет совместно использовать ПО, даже если оно было написано на разных языках. Почему скрипты должны отличаться? Технология Windows Script Components позволяет писать код скрипта, а представлять его как COM-объект. Все, что нужно - простой XML-файл с нужными тегами и некий код скрипта, позволяющий скрипт-машине (SCROBJ.DLL) разобрать файл и представить код скрипта как объект. Если добавить в тег <registration> "remotable=true", то вы получите полноценный DCOM-объект, который можно вызывать на удаленном компьютере.
Copyright © 1994-2016 ООО "К-Пресс"