在介绍了应用系统和脚本引擎所实现的一些关键接口之后,我们
再进一步看看应用系统和脚本引擎的协作过程,如图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码的
转换。
ActiveX Scripting技术(二)
最新推荐文章于 2021-04-02 13:55:00 发布
本文介绍了一个通用类CScriptHost的设计,该类实现了IActiveScriptSite和IActiveScriptSiteWindow接口,用于创建和维护脚本引擎,并详细解释了如何加载脚本文件及与脚本引擎交互的过程。
摘要由CSDN通过智能技术生成