ActiveX Scripting技术(二)

在介绍了应用系统和脚本引擎所实现的一些关键接口之后,我们
再进一步看看应用系统和脚本引擎的协作过程,如图2所示。
    图中给出了8个步骤,下面逐一介绍。
图2 应用系统与脚本引擎的协作过程
    (1)创建必要的受控对象,这些受控对象是指将要在脚本文件中引
用到的Automation 对象,通常是应用系统的文档对象,也可以是某些A
ctiveX控制;
    (2)创建引擎对象,不同的脚本语言使用不同的引擎对象,通常我
们使用VBScript引擎或者JavaScript引擎,创建得到的接口指针是应
用系统控制引擎的惟一途径;
    (3)装入脚本文件,调用引擎的IActiveScriptParse接口的ParseS
criptText成员函数把脚本代码装入到引擎中,注意ParseScriptText
成员函数只接收UNICODE字符串,如果程序中用到了ANSI字符串,则需
要进行转换才能装入到引擎中;
    (4)加入名字项,凡是应用系统中要暴露给脚本文件的所有对象都
需要加入到引擎的名字空间中,可以通过调用IActiveScript接口的Ad
dNamedItem成员函数来完成;
    (5)启动引擎,以便运行脚本,直接调用IActiveScript::SetScrip
tState成员函数使其进入连接状态(运行状态)即可;
    (6)引擎在执行脚本时,首先处理其名字空间中的名字项,调用应
用系统IActiveScri ptSite接口的GetItemInfo成员函数获取每一个
名字所对应的受控对象的信息,主要是CO M接口;如果在脚本中有事件
控制函数的话,则还要获取受控对象的类型信息;
    (7)在脚本执行过程中,当特定的事件发生时,引擎中的事件控制
函数就要被调用;
    (8)在脚本执行过程中,有可能会调用到受控对象的属性和方法,
则引擎会通过它所获取的对象接口调用IDispatch::Invoke成员函数;
    如果应用系统希望终止引擎的执行过程,可以调用IActiveScript
::SetScriptState 成员函数使其进入非运行状态即可。
    以上的步骤基本上反映了应用系统和引擎之间的协作过程。在实
际使用过程中,可以根据情况的不同灵活应用。
    三、ActiveX Scripting实现
    因为Microsoft或者其他的软件厂商已经为我们提供了脚本引擎(
如果机器上安装了Internet Explorer,则VBScript和JavaScript的引
擎就已经被安装在机器上了),所以我们只需要在应用系统中直接使用
就可以。为了使应用系统更好地支持这种脚本特性,我们对实现脚本
特性过程中的一些要点作一分析。
    首先我们定义一个通用的类CScriptHost,它实现了两个接口IAct
iveScriptSite和I ActiveScriptSiteWindow,类的定义如下:
    class CScriptHost : public IActiveScriptSite , public IA
ctiveScriptSiteW indow
    {
    public:
    CScriptHost(LPUNKNOWN lpUnkCtrl, LPCOLESTR
    pNamedItem, HWND hWnd);
    virtual ~CScriptHost();
    HRESULT CreateScriptEngine();
    HRESULT ParseFile(const char*pszFileName,LPCOLESTR pstrI
temName);
    public:
    //IUnknown members
    STDMETHOD(QueryInterface)(REFIID riid,void** ppvObj);
    STDMETHOD_(unsigned long,AddRef)(void);
    STDMETHOD_(unsigned long,Release)();
    //IActiveScriptSite members
    STDMETHOD(GetLCID)(LCID *plcid) ;
    STDMETHOD(GetItemInfo)(LPCOLESTR pstrName,DWORDdwReturnM
ask,IUnknown **p piunkItem,ITypeInfo * *ppti) ;
    STDMETHOD(GetDocVersionString)(BSTR *pbstrVersion) ;
    STDMETHOD(OnScriptTerminate)(const VARIANT
    *pvarResult,const EXCEPINFO *pexcepinfo) ;
    STDMETHOD(OnStateChange)(SCRIPTSTATE ssScriptState) ;
    STDMETHOD(OnScriptError)(IActiveScriptError *pscripterro
r) ;
    STDMETHOD(OnEnterScript)(void) ;
    STDMETHOD(OnLeaveScript)(void) ;
    //IActiveScriptSiteWindow members
    STDMETHOD(GetWindow)(HWND *phwnd) ;
    STDMETHOD(EnableModeless)(BOOL fEnable);
    public:
    IActiveScript* m_ps;
    IActiveScriptParse* m_psp;
    private:
    UINT m_cref;
    CLSID m_clsidEngine;
    LPUNKNOWN m_lpUnkCtrl;
    LPOLESTR m_pNamedItem;
    HWND m_Wnd;
    };
    类CScriptHost通过多继承的方法实现了两个接口,并负责脚本引
擎的创建和维护工作。其数据成员m_ps和m_psp用于保存引擎的IActi
veScript和IActiveScriptParse接口指针;数据成员m_clsidEngine记
录了引擎的类ID;m_Wnd记录了应用系统提供给引擎的主窗口;m_lpUnk
Ctrl记录了应用系统的惟一的一个受控对象,m_pNamedItem记录了受
控对象的名字。在CScriptHost类的构造函数中初始设置m_lpUnkCtrl
、m_pNamedItem和m_Wnd成员变量。构造函数和析构函数代码如下:
    CScriptHost::CScriptHost(LPUNKNOWN lpUnkCtrl,LPCOLESTR p
NamedItem , HWND  hWnd)
    : m_ps(NULL),m_psp(NULL),m_cref(0)
    {
    m_lpUnkCtrl = lpUnkCtrl;
    m_pNamedItem = pNamedItem;
    m_Wnd = hWnd;
    // the clsid of VBScript Engine
    static CLSID const clsid = {0xb54f3741, 0x5b07, 0x11cf,{
0xa4, 0xb0, 0x0,  0xaa, 0x0, 0x4a, 0x55, 0xe8}};
    // Default to VBScript
    m_clsidEngine = clsid;
    }
    CScriptHost::~CScriptHost()
    {
    if(m_psp)
    m_psp- Release();
    // we must first close the script engine
    if(m_ps)
    {
    m_ps- Close();
    m_ps- Release();
    }
    }
    在构造函数中,我们指定使用缺省的VBScript脚本引擎对象,并设
置其相应的CLSID。在析构函数中我们不能释放所有引擎的接口指针,
因为这会导致脚本引擎对象释放这个指针时出错。在析构函数中调用
IActiveScript::Close关闭与脚本引擎的连接。
    成员函数CreateScriptEngine是由应用系统调用的函数,代码如
下:
    HRESULT CScriptHost::CreateScriptEngine()
    {
    HRESULT hr = S_OK;
    hr = ::CoCreateInstance(m_clsidEngine,NULL,CLSCTX_INPROC
_SERVER,IID_IAct iveScript,(void**)&m_ps);
    if (SUCCEEDED(hr))
    {
    // QI the IActiveScriptParse pointerhr = m_ps- QueryInte
rface(IID_IActiv eScriptParse,(void**)&m_psp);
    if (FAILED(hr) )
    {
    m_ps- Release();
    return hr;
    }
    // set the script site
    hr = m_ps- SetScriptSite(this);
    if ( FAILED( hr ) )
    return hr;
    m_ps- SetScriptState(SCRIPTSTATE_INITIALIZED);
    // initiate the script engine
    hr = m_psp- InitNew();
    if (FAILED(hr))
    return hr;
    hr = m_ps- AddNamedItem(m_pNamedItem,SCRIPTITEM_ISVISIBL
E|SCRIPTITEM_ISS OURCE);
    }
    return hr;
    }
    CreateScriptEngine函数首先创建引擎对象,然后把引擎对象的I
ActiveScript和IA ctiveScriptParse接口指针分别赋给数据成员m_p
s和m_psp,最后把m_pNamedItem名字加入到引擎的名字空间中。
    成员函数ParseFile可以把脚本文件装入到引擎中,代码如下:
    HRESULT CScriptHost::ParseFile(const char * pszFileName,
LPCOLESTR pstrIt emName)
    {
    HRESULT hr = S_OK;
    struct _stat stat;
    size_t cch;
    EXCEPINFO ei;
    FILE *pfile;
    if(_stat(pszFileName,&stat))
    return E_FAIL;
    cch = stat.st_size;
    char* pszAlloc = new char;
    pszAlloc = '/0';// this is vitally important
    if (pszAlloc==NULL)
    return E_OUTOFMEMORY;
    memset(pszAlloc,0,cch);
    //get the script text into a memory block
    pfile = fopen(pszFileName,"rb");
    if (!pfile)
    {
    hr = E_FAIL;
    return hr;
    }
    fread(pszAlloc,cch,1,pfile);
    fclose(pfile);
    LPOLESTR pwszCode;
    int CharCount = MultiByteToWideChar(CP_ACP,0,pszAlloc,-1
,NULL,0);
    pwszCode = new WCHAR;
    MultiByteToWideChar(CP_ACP,0,pszAlloc,-1,pwszCode,CharCo
unt);
    size_t t = wcslen(pwszCode);
    hr = m_psp- ParseScriptText(pwszCode, pstrItemName,NULL,
NULL, 0,0,0L,NUL L,&ei);
    delete pwszCode;
    delete pszAlloc;
    return hr;
    }
    ParseFile函数首先确定脚本文件的长度,然后打开文件并装入到
内存中,最后把内存中脚本代码通过引擎的IActiveScriptParse接口
成员函数ParseScriptText成员函数装入到引擎中。需要注意的是,我
们这里调用MuitiByteToWide函数完成了代码从ANSI到UNIC ODE码的
转换。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值