Как загрузить и вызвать функцию VBScript из C++?
У нас есть клиенты, которые просят вызывать функции VBScript, когда в нашем продукте происходят определенные действия. Я пытался исследовать технологии Windows Scripting, но мне трудно найти именно то, что мне нужно. Надеюсь, некоторые из вас могут помочь.
Наш продукт является родным C++ для Windows. Клиент указывает файл VBScript, который мы загружаем, и всякий раз, когда происходит определенное событие, мы вызываем определенную функцию в VBScript и позволяем ему делать свое дело. Мы можем предоставить объекты в пространстве имен скрипта, чтобы он также мог получить доступ к информации о нашем продукте.
Я нашел некоторую информацию на MSDN об интерфейсе IActiveScript и некоторых связанных с этим вещах, но не могу найти примеров создания экземпляра COM-объекта, который реализует этот интерфейс для VBScript.
Я знаю, что PowerShell, вероятно, будет лучшим вариантом для этого в наши дни, но наши клиенты застряли во многих устаревших системах, и VBScript - это то, что они знают.
Мы будем благодарны за любую помощь, которую вы можете предоставить (ссылки или иное)
2 ответа
Взгляните на это для примера кода https://web.archive.org/web/20111115133054/http://support.microsoft.com/kb/221992
Существует файл msscript.ocx, который может помочь вам, но, вероятно, он доставит вам больше хлопот, чем стоит, если вы сможете написать на C++ http://msdn.microsoft.com/en-us/magazine/cc302278.aspx
Я собрал консольное приложение 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;
}
IActiveScript
и связанные интерфейсы работают очень хорошо. Я использую их в моем продукте точно так же, как вы описали. Некоторые из наших клиентов пишут свои собственные сценарии VBScript и JScript для анализа и обновления данных приложения до их публикации в базе данных.
Ты используешь CoCreateInstance()
создать экземпляр IActiveScript
Как и любой другой объект COM. Затем вы бы назвали его QueryInterface()
метод получения IActiveScriptParse
интерфейс для загрузки фрагментов кода сценария, а затем вы обновляете IActiveScript
состояние для выполнения кода.
Вы можете добавить пользовательские объекты в скрипт, реализовав IDispatch
классы, а затем передать их в движок с помощью IActiveScript::AddNamedItem()
и IActiveScriptSite::GetItemInfo()
Перезвоните.
Есть примеры IActiveScript
использование доступно на MSDN.