Как загрузить и вызвать функцию VBScript из C ++? - PullRequest
7 голосов
/ 21 сентября 2011

У нас есть клиенты, которые просят вызвать функции VBScript, когда в нашем продукте происходят определенные действия.Я пытался исследовать технологии Windows Scripting, но мне трудно найти именно то, что мне нужно.Надеюсь, что некоторые из вас могут помочь.

Наш продукт является родным продуктом C ++ для Windows.Клиент указывает файл VBScript, который мы загружаем, и всякий раз, когда происходит определенное событие, мы вызываем определенную функцию в VBScript и позволяем ему делать свое дело.Мы можем предоставить объекты в пространстве имен скрипта, чтобы он также мог получить доступ к информации о нашем продукте.

Я нашел некоторую информацию на MSDN об интерфейсе IActiveScript и некоторых связанных вещах, но не могу найти примеров создания экземпляра COMобъект, который реализует этот интерфейс для VBScript.

Я знаю, что PowerShell, вероятно, будет лучшим вариантом для этого в наши дни, но наши клиенты застряли во многих устаревших системах, и VBScript - это то, что они знают.

Буду признателен за любую помощь, которую вы можете предоставить (ссылки или иное)!

Ответы [ 2 ]

12 голосов
/ 05 февраля 2012

Я собрал консольное приложение IActiveScript C ++ ATL «Hello World», которое:

  • Определить CSimpleScriptSite класс
    • Реализация интерфейса IActiveScriptSite (обязательно)
    • Реализация интерфейса IActiveScriptSiteWindow (необязательно)
    • Минимальная реализация с большинством функций, реализованных с заглушкой
    • Не имеет обработки ошибок.Обратитесь к MSDN IActiveScriptError .
  • Использование CoCreateInstance нового IActiveSite объекта
    • Создание экземпляров как VBScript, так и JScript
    • Свяжите IActiveSite с IActiveScriptSite, используя IActiveSite::SetScriptSite
    • Позвоните QueryInterface, чтобы получить IActiveScriptParse интерфейс
    • Используйте IActiveScriptParse для выполнения VBScriptили JScript код
  • Пример:
    • Оценивает выражение в JScript
    • Оценивает выражение в VBScript
    • Запускает команду в VBScript

Код:

#include "stdafx.h"
#include <atlbase.h>
#include <activscp.h>

class CSimpleScriptSite :
    public IActiveScriptSite,
    public IActiveScriptSiteWindow
{
public:
    CSimpleScriptSite() : m_cRefCount(1), m_hWnd(NULL) { }

    // IUnknown

    STDMETHOD_(ULONG, AddRef)();
    STDMETHOD_(ULONG, Release)();
    STDMETHOD(QueryInterface)(REFIID riid, void **ppvObject);

    // IActiveScriptSite

    STDMETHOD(GetLCID)(LCID *plcid){ *plcid = 0; return S_OK; }
    STDMETHOD(GetItemInfo)(LPCOLESTR pstrName, DWORD dwReturnMask, IUnknown **ppiunkItem, ITypeInfo **ppti) { return TYPE_E_ELEMENTNOTFOUND; }
    STDMETHOD(GetDocVersionString)(BSTR *pbstrVersion) { *pbstrVersion = SysAllocString(L"1.0"); return S_OK; }
    STDMETHOD(OnScriptTerminate)(const VARIANT *pvarResult, const EXCEPINFO *pexcepinfo) { return S_OK; }
    STDMETHOD(OnStateChange)(SCRIPTSTATE ssScriptState) { return S_OK; }
    STDMETHOD(OnScriptError)(IActiveScriptError *pIActiveScriptError) { return S_OK; }
    STDMETHOD(OnEnterScript)(void) { return S_OK; }
    STDMETHOD(OnLeaveScript)(void) { return S_OK; }

    // IActiveScriptSiteWindow

    STDMETHOD(GetWindow)(HWND *phWnd) { *phWnd = m_hWnd; return S_OK; }
    STDMETHOD(EnableModeless)(BOOL fEnable) { return S_OK; }

    // Miscellaneous

    HRESULT SetWindow(HWND hWnd) { m_hWnd = hWnd; return S_OK; }

public:
    LONG m_cRefCount;
    HWND m_hWnd;
};

STDMETHODIMP_(ULONG) CSimpleScriptSite::AddRef()
{
    return InterlockedIncrement(&m_cRefCount);
}

STDMETHODIMP_(ULONG) CSimpleScriptSite::Release()
{
    if (!InterlockedDecrement(&m_cRefCount))
    {
        delete this;
        return 0;
    }
    return m_cRefCount;
}

STDMETHODIMP CSimpleScriptSite::QueryInterface(REFIID riid, void **ppvObject)
{
    if (riid == IID_IUnknown || riid == IID_IActiveScriptSiteWindow)
    {
        *ppvObject = (IActiveScriptSiteWindow *) this;
        AddRef();
        return NOERROR;
    }
    if (riid == IID_IActiveScriptSite)
    {
        *ppvObject = (IActiveScriptSite *) this;
        AddRef();
        return NOERROR;
    }
    return E_NOINTERFACE;
}

int _tmain(int argc, _TCHAR* argv[])
{
    HRESULT hr = S_OK;
    hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);

    // Initialize
    CSimpleScriptSite* pScriptSite = new CSimpleScriptSite();
    CComPtr<IActiveScript> spJScript;
    CComPtr<IActiveScriptParse> spJScriptParse;
    hr = spJScript.CoCreateInstance(OLESTR("JScript"));
    hr = spJScript->SetScriptSite(pScriptSite);
    hr = spJScript->QueryInterface(&spJScriptParse);
    hr = spJScriptParse->InitNew();
    CComPtr<IActiveScript> spVBScript;
    CComPtr<IActiveScriptParse> spVBScriptParse;
    hr = spVBScript.CoCreateInstance(OLESTR("VBScript"));
    hr = spVBScript->SetScriptSite(pScriptSite);
    hr = spVBScript->QueryInterface(&spVBScriptParse);
    hr = spVBScriptParse->InitNew();

    // Run some scripts
    CComVariant result;
    EXCEPINFO ei = { };
    hr = spJScriptParse->ParseScriptText(OLESTR("(new Date()).getTime()"), NULL, NULL, NULL, 0, 0, SCRIPTTEXT_ISEXPRESSION, &result, &ei);
    hr = spVBScriptParse->ParseScriptText(OLESTR("Now"), NULL, NULL, NULL, 0, 0, SCRIPTTEXT_ISEXPRESSION, &result, &ei);
    hr = spVBScriptParse->ParseScriptText(OLESTR("MsgBox \"Hello World! The current time is: \" & Now"), NULL, NULL, NULL, 0, 0, 0, &result, &ei);

    // Cleanup
    spVBScriptParse = NULL;
    spVBScript = NULL;
    spJScriptParse = NULL;
    spJScript = NULL;
    pScriptSite->Release();
    pScriptSite = NULL;

    ::CoUninitialize();
    return 0;
}

Версия приведенного выше кода можно найти здесь:

2 голосов
/ 21 сентября 2011

IActiveScript и связанные интерфейсы работают очень хорошо. Я использую их в моем продукте точно так же, как вы описали. Некоторые из наших клиентов пишут свои собственные сценарии VBScript и JScript для анализа и обновления данных приложения до его публикации в базе данных.

Вы используете CoCreateInstance() для создания экземпляра IActiveScript, как и любой другой COM-объект. Затем вы вызываете метод QueryInterface(), чтобы получить интерфейс IActiveScriptParse для загрузки фрагментов кода сценария, а затем обновляете состояние IActiveScript для выполнения кода.

Вы можете добавить пользовательские объекты в сценарий, реализовав классы IDispatch и затем передать их в обработчик, используя IActiveScript::AddNamedItem() и IActiveScriptSite::GetItemInfo() callback.

В MSDN доступны примеры использования IActiveScript.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...