Як скрипт додатки

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

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

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

Active Scripting Engine створюється як звичайний СOM об'єкт:

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

Як реалізується хост

Отже, необхідно реалізувати IActiveScriptSite і укзано скрипт-модулю що йому є з чим працювати.

Реалізовувати можна за схемою MFC з BEGIN_INTERFACE_MAP / INTERFACE_PART або ж за звичайною схемою, розписуючи AddRef, Release і QueryInterface. Оскільки об'єкт повинен експортувати тільки один інтерфейс, IActiveScriptSite, то все СOM- нутрощі можна досить просто розписати без допомоги MFC. Докладний код для цього додається в прикладі.

Після того, як скрипт-модуль ініціалізованим першим, йому необхідно передати покажчик на ActiveScriptSite.

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

Це ще не все, життя стає трохи складніше :). Тепер потрібно запустити складну процедуру Test. До речі, якщо оформити код поза процедурою, то подальших кроків не потрібно - скрипт автоматично б запустить все, що не лежить в будь-якої процедури. Якщо говорити мовою C, все, що у вас не розкладено по процедурам і функціям, потрапляє в void main ().

Перший параметр, як бачите, порожній. Тут можна вказати назву об'єкта у внутрішньому просторі скрипта. Порожній параметр означає простір всіх функцій.

Отримавши dispatch, потрібно зробити виклик через метод Invoke. при цьому передавши необхідні для функції параметри. У нашому випадку це рядок для функції Test.

Навіть при всьому при тому, що функція наша запустилася і видала віконце, такий скрипт зовсім нікому не потрібен. Адже все це чаклунство було влаштовано заради роботи з внутрішніми об'єктами програми.

Інтерфейс IActiveScript має метод AddNamedItem, який дозволяє додавати різні ідентифікатори в простір імен скрипта.

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

Об'єкт m_pScriptObject - покажчик на нашого CCmdTarget спадкоємця, який створюється за технологією MFC Automation.

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

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

Скрипт-компонент можна додати абсолютно стандартним методом VB: з діалогового вікна Components. Якщо ж такого пункту в списку немає, то досить знайти його на диску за допомогою Browse.

Помістивши скрипт-компонент на якусь форму програми (від цього місця будемо кликати його ScriptControl1), ви виявите, що сам компонентік-то не так вже й багато властивостей має: AllowUI, Language і Timeout.

Властивість AllowUI дозволяє визначити, чи може скрипт показувати свої діалогові вікна, такі як повідомлення про помилки, різні MsgBox і InputBox.

Властивість Language за замовчуванням встановлено в VBScript, але з рівним успіхом може містити ім'я JScript.

Властивість TimeOut задає максимальний інтервал, після якого скрипт буде примусово завершений. Це корисно в тому випадку, якщо скрипт "ненавмисно" зациклився.

Тепер можна звернутися до нутрощів компонента, при цьому виявивши деякі функції, що дозволяють вже щось запускати, без болісної підготовки: Eval і ExecuteStatment. Як неважко здогадатися, перша обчислює вираз, введене у вигляді рядка, а друга виконує завершений оператор.

Обчислюватися можуть будь-які вирази: в цілих числах, строкові, логічні, повернення функцій. Результат повертається у вигляді VARIANT змінної, яка, як відомо, може містити всі.

Навряд чи взявшись за освоєння Script Control, можна задовольнитися єдиною оброблюваної рядком. Метод AddCode дозволяє додати кілька рядків, які потім можна запустити методом Run. Якщо функція приймає на вхід параметри, то ці параметри можна також передати через Run.

Код, який передається в якості параметра методу AddCode повинен бути коректним і містити одну або кілька процедур або функцій. У разі, якщо код, що додається за допомогою AddCode некоректний, компонент викидає виключення. Завдання розробника - перехопити цю помилку і дати користувачеві зрозуміти, що завдання користувача - писати скрипти грамотніше.

Взагалі кажучи, у Script Control є властивість Error і однойменне подія Error, яке викликається в разі помилки. У разі помилок часу виконання найкраще скористатися саме цією властивістю, оскільки воно несе більше інформації про помилку, ніж стандартний варіант з Err.

Що стосується внутрішніх об'єктів програми, то їх експортування в простір імен скрипта набагато простіше, ніж в Visual C ++. Метод AddObject дозволяє розширювати кількість доступних скрипту об'єктів. Для цього потрібно створити Class Module і оголосити всередині нього необхідні функції і public властивості.

Тепер усередині скрипта можна абсолютно спокійно звертатися до оголошеного Object1.

Таким чином, можна організувати якийсь proxy-об'єкт для доступу до елементів управління в свій програмі. Наприклад, можна в YourClassModule створити функцію, що додає якусь сходинку в списку, toolbar або меню.

Слід враховувати, що якщо потрібно передати якусь внутрішню змінну скрипта на обробку в ваш ClassModule, то працювати доведеться тільки зі змінними типу Variant.

Можливості стають воістину безмежні, якщо видавати через властивості YourClassModule якісь внутрішні компоненти. Напрмер, Form.

Схожі статті