На главную

Библиотека Интернет Индустрии I2R.ru

Rambler's Top100

Малобюджетные сайты...

Продвижение веб-сайта...

Контент и авторское право...

Забобрить эту страницу! Забобрить! Блог Библиотека Сайтостроительства на toodoo
  Поиск:   
Рассылки для занятых...»
I2R » И2Р Программы » Программирование » VBasic, VBS

Как скриптуются приложения. Акт первый.

Идея

Очень часто в различных задачах возникала потребность в реализации того или иного миниязыка, описывающего какие-то процессы или, чаще, реакцию на какие-то события. По-правде говоря, дело это довольно сложное и неблагодарное. Написанию парсеров и интерпретаторов посвящены горы документации и книг. А хочется всего навсего две-три функции, складывающие пару параметров, да возвращающе их назад приложению. Идея создания базиса для поддержки макроязыков (скриптов) возникла вместе с появлением COM. Причем идея эта была проработана довольно фундаментально: можно использовать уже имеющиеся модули скрипт-языков, такие как JavaScript или VBScript, а можно и определить любой другой язык или использовать модули третьих фирм. И автоматизация проявляется во всей красе: достаточно лишь пронаследовать свои объекты от IDispatch (CCmdTarget), добавить их в список глобальных переменных пространства скрипта, а дальше хоть всю логику на скрипте пиши.  Конечно, медленно, но некоторые задачи и нацелены на максимальную масштабируемость. Ну а ежели логику жалко, то можно просто пару событий обрабатывать.

В общем, идея получила название ActiveScripting и перелилась в отдельную технологию, базирующуюся на нескольких механизме COM.

Активное скриптование во плоти

Реализация была поделена на две части: Active Scripting Engine и Active Scripting Host. В качестве Active Scripting Host выступает прикладное приложение, использующее возможности скрипт-парсеров. А Active Script Engine - это компонент, который экспортирует некоторое количество COM интерфейсов и понимает, как обрабатывать синтаксис определенного скрипт-языка. Обычно, в составе Windows имеется два таких компонента : парсер JavaScript и парсер VBScript. Они лежат соответственно в библиотеках jscript.dll и vbscript.dll. Модуль VBScript реализует интерфейсы: IActiveScript, IActiveScriptDebug, IActiveScriptParse, IActiveScriptStats, IObjectSafety, IRemoteApplicationDebugEvents и IVariantChangeType. Как видите, довольно много, поэтому лучшим помощником при реализации своего скрипт-модуля будет документация MSDN. Большинству программистов это даже и не понадобится, поскольку VBScript удовлетворят большинству потребностей автоматизации. Хочется только обратить внимание на интерфейс IActiveScript и IActiveScriptParse, поскольку они и являются связующим звеном между Active Scripting Host и Active Scripting Engine.

  • IActiveScript
    Этот интерфейс первым запрашивается у модуля скрипт-языка и используется для инициализации. Я рассмотрю методы SetScriptSite, AddNamedItem, SetScriptState и GetScriptDispatch при реализации скрипт-хоста.
  • IActiveScriptParse
    Реализация этого интерфейса отвечает за обработку текста самого скрипта через метод ParseScriptText.

Active Scripting Engine создается как обычный СOM объект:

#include <activscp.h> DEFINE_GUID(CLSID_VBScript, 0xb54f3741, 0x5b07, 0x11cf, 0xa4, 0xb0, 0x0, 0xaa, 0x0, 0x4a, 0x55, 0xe8); . CComPtr<IActiveScript> pScriptEngine; CComQIPtr<IActiveScriptParse> pScriptParse; pScriptEngine->CoCreateInstance(CLSID_VBScript); pScriptParse = pScriptEngine; .

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

Как реализуется хост

Итак, необходимо реализовать IActiveScriptSite и укзать скрипт-модулю что ему есть с чем работать.

IActiveScriptSite : IUnknown { HRESULT GetLCID(LCID* plcid) = 0; HRESULT GetItemInfo(LPCOLESTR pstrName, DWORD dwReturnMask, IUnknown** ppiunkItem, ITypeInfo** ppti) = 0; HRESULT GetDocVersionString(BSTR* pbstrVersion) = 0; HRESULT OnScriptTerminate(VARIANT* pvarResult, EXCEPINFO* pexcepinfo) = 0; HRESULT OnStateChange(SCRIPTSTATE ssScriptState) = 0; HRESULT OnScriptError(IActiveScriptError* pscripterror) = 0; HRESULT OnEnterScript(void) = 0; HRESULT OnLeaveScript(void) = 0; };

Реализовывать можно по схеме MFC с BEGIN_INTERFACE_MAP/ INTERFACE_PART или же по обычной схеме, расписывая AddRef, Release и QueryInterface. Поскольку объект должен экспортировать только один интерфейс, IActiveScriptSite, то все СOM- внутренности можно довольно просто расписать без помощи MFC. Подробный код для этого прилагается в примере.

После того, как скрипт-модуль проинициализирован, ему необходимо передать указатель на ActiveScriptSite :

pSite = new CScriptSite; pScriptEngine->SetScriptSite(pSite);

Теперь скрипт-модулю есть как общаться с нашим приложением, но нечего запускать. Настала пора передать придумать какой-нибудь скрипт и передать его через интерфейс IScriptParse на обработку и запуск.

LPWSTR pCode; EXCEPINFO pException = { 0 }; pCode = L"Sub Test(str)\r\nMsgBox str & Date()\r\nEnd Sub"; pScriptParse->InitNew(); pScriptParse->ParseScriptText (pCode, 0, NULL, NULL, 0, 0, 0, NULL, &pException); piScript->SetScriptState(SCRIPTSTATE_CONNECTED);

Это еще не все, жизнь становится немного сложнее :). Теперь нужно запустить сложнейшую процедуру Test. Кстати, если оформить код вне процедуры, то дальнейших шагов не нужно - скрипт автоматически бы запустит все, что не лежит в какой-либо процедуре. Говоря языком C, все, что у вас не разложено по процедурам и функциям, попадает в void main().


Для начала нужно получить указатель на Dispatch интерфейс, представляющий элемент внутреннего пространства скрипта. Он создается Script Engine после того, как вызвана функция ParseScriptText.

IDispatch* piDisp; piScriptEngine->GetScriptDispatch(L"", &piDisp);

Первый параметр, как видите, пустой. Здесь можно указать название объекта во внутреннем пространстве скрипта. Пустой параметр означает пространство всех функций.

Получив dispatch, нужно произвести вызов через метод Invoke , при этом передав необходимые для функции параметры. В нашем случае это строка для функции Test.

OLECHAR* szMember; DISPID dispid; VARIANTARG* pvarArgs; DISPPARAMS dispArgs; pvarArgs = new VARIANTARG[1]; pvarArgs[0].vt = VT_BSTR; pvarArgs[0].bstrVal = SysAllocString(L"Today is "); dispArgs.rgvarg = pvarArgs; dispArgs.cArgs = 1; dispArgs.cNamedArgs = 0; dispArgs.rgdispidNamedArgs = NULL; szMember = L"Test"; piDisp->GetIDsOfNames(IID_NULL, &szMember, 1,LOCALE_USER_DEFAULT, &dispid); piDisp->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispArgs, NULL, NULL, NULL); piDisp->Release(); delete pvarArgs[];

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

Интерфейс IActiveScript имеет метод AddNamedItem, который позволяет добавлять различные идентификаторы в пространство имен скрипта.

m_iActiveScript->AddNamedItem(L"MyObject", SCRIPTITEM_ISVISIBLE | SCRIPTITEM_ISSOURCE);

Таким образом мы добавили идентификатор, который пока что никакой информации не несет. А вот если что-то внутри VB-скрипта обратится к этому идентификатору, то Engine автоматически вызовет метод хоста IActiveScriptSite:: GetItemInfo, который должен уже быть нами реализован.

virtual HRESULT _stdcall GetItemInfo(LPCOLESTR pstrName, DWORD dwReturnMask, IUnknown **ppunkItem, ITypeInfo **ppti) { // Cпрашивает о ITypeInfo? if(ppti) { *ppti = NULL; // Его у нас нет. if(dwReturnMask & SCRIPTINFO_ITYPEINFO) return TYPE_E_ELEMENTNOTFOUND; } // Engine запрашивает наш объект if(ppunkItem) { *ppunkItem = NULL; if(dwReturnMask & SCRIPTINFO_IUNKNOWN) { // Проверим, наш ли это объект if (!_wcsicmp(L"MyObject", pstrName)) { // Это наш объект ? *ppunkItem = m_pScriptObject; // Увеличить счетчик ссылок m_pScriptObject->AddRef(); } } } return S_OK; }

Объект m_pScriptObject - указатель на нашего CCmdTarget наследника, который создается по технологии MFC Automation.

Теперь в скрипте можно совершенно смело обращаться к automation свойствам и методам MyObject. А в случае runtime-ошибки будет вызван метод IActiveScriptSite::OnScriptError.

Итак, тех, то сумел реализовать VBScript-автоматизацию "натуральным" методом, могу поздравить, а тех, кто не разобрался поспешу утешить: Microsoft выпустила ActiveX компонент, под названием ScriptControl, который упрощает все вышеперечисленные функции и организует их в более приемлемом виде. Не без ущерба в скорости, конечно, :).

Продолжение следует.

Николай Куртов
Софтерра

Другие разделы
C, C++
Java
PHP
VBasic, VBS
Delphi и Pascal
Новое в разделе
Базы данных
Общие вопросы
Теория программирования и алгоритмы
JavaScript и DHTML
Perl
Python
Active Server Pages
Программирование под Windows
I2R-Журналы
I2R Business
I2R Web Creation
I2R Computer
рассылки библиотеки +
И2Р Программы
Всё о Windows
Программирование
Софт
Мир Linux
Галерея Попова
Каталог I2R
Партнеры
Amicus Studio
NunDesign
Горящие путевки, идеи путешествийMegaTIS.Ru

2000-2008 г.   
Все авторские права соблюдены.
Rambler's Top100