【问题标题】:How to load & call a VBScript function from within C++?如何从 C++ 中加载和调用 VBScript 函数?
【发布时间】:2011-09-20 20:12:42
【问题描述】:

我们有客户要求在我们的产品中发生特定操作时调用 VBScript 函数。我一直在尝试研究 Windows 脚本技术,但我很难找到我真正需要的东西。希望你们中的一些人能提供帮助。

我们的产品是原生 C++ Windows 产品。客户将指定一个 VBScript 文件,我们将加载该文件,并且每当发生特定事件时,我们将调用 VBScript 中的特定函数并让它执行其操作。我们可以在脚本的命名空间中提供对象,以便它也可以访问有关我们产品的信息。

我在 MSDN 上找到了一些关于 IActiveScript 接口的信息,以及一些相关的东西,但找不到任何实例化实现 VBScript 接口的 COM 对象的示例。

我知道现在 PowerShell 可能是更好的选择,但我们的客户被困在许多遗留系统中,而 VBScript 就是他们所知道的。

您可以提供任何帮助(链接或其他方式)将不胜感激!

【问题讨论】:

  • 这是一个很好的问题,但可能更适合 *。这个网站真的是为了更主观的话题。 imo,IActiveScript 是您研究的正确方向。

标签: c++ windows


【解决方案1】:

我已经组装了一个“Hello World”IActiveScript C++ ATL 控制台应用程序:

  • 定义CSimpleScriptSite
    • 实现IActiveScriptSite接口(强制)
    • 实现IActiveScriptSiteWindow接口(可选)
    • 使用虚拟存根实现的大多数功能的最小实现
    • 没有错误处理。请咨询 MSDN IActiveScriptError
  • 使用CoCreateInstance 一个新的IActiveSite 对象
    • 创建VBScriptJScript 的实例
    • 使用IActiveSite::SetScriptSiteIActiveSite 链接到IActiveScriptSite
    • 调用QueryInterface获取IActiveScriptParse接口
    • 使用IActiveScriptParse执行VBScriptJScript代码
  • 样品:
    • 计算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】:

IActiveScript 及相关接口运行良好。我在我的产品中使用它们的方式与您描述的完全相同。有些客户编写自己的 VBScript 和 JScript 脚本来分析和更新应用程序数据,然后再将其发布到数据库。

您使用CoCreateInstance() 来实例化IActiveScript,就像您使用任何其他COM 对象一样。然后调用它的QueryInterface()方法获取IActiveScriptParse接口用于加载脚本代码的sn-ps,然后更新IActiveScript的状态来执行代码。

您可以通过实现IDispatch 派生类向脚本添加自定义对象,然后使用IActiveScript::AddNamedItem()IActiveScriptSite::GetItemInfo() 回调将它们传递给引擎。

MSDN 上提供了IActiveScript 用法的示例。

【讨论】:

  • 谢谢,虽然这基本上是我已经理解的。请注意,我说我在 MSDN 上找到了此信息,但找不到示例。谢谢你告诉我有例子,但没有指向任何例子。 (理查德的回答为我提供了这个)
最近更新 更多