如何在vc程序中嵌入脚本语言初稿.docx
《如何在vc程序中嵌入脚本语言初稿.docx》由会员分享,可在线阅读,更多相关《如何在vc程序中嵌入脚本语言初稿.docx(13页珍藏版)》请在冰点文库上搜索。
如何在vc程序中嵌入脚本语言初稿
如何在vc程序中嵌入脚本语言
今天很多大型程序中都能够见到内嵌脚本进行二次开发的功能,例如msword,excel,visualstudio等。
一直以来我都希望能在自己的程序中加入同样的功能,经过前一段时间的研究,终于有所心得与大家分享。
在研究过程中,我查找了发现一篇比较有价值的文章(
其原理如下
1.首先使用CoCreateInstance()创建某种脚本语言(javascript,vbscript)的引擎,获得某种语言的脚本引擎的接口IActiveScript。
2.实现回调站点接口IActiveScriptSite通过IActiveScript->SetScriptSite()交由脚本引擎回调,在site中可以取得引擎的状态信息,并提供用户的自定义变量的自动化对象。
3.通过IActiveScript->QueryInterface()取得IActiveScriptParse接口,IActiveScriptParse用于解释执行用户的脚本代码。
幸运的是这一系列接口和操作已经被文章的作者封装成CActiveScriptHost类,只需要掌握CreateEngine()(创建脚本引擎),AddScriptCode()(加入用户脚本代码),AddScriptItem()(加入用户自定义变量)四个常用的方法即可。
下面描述如何在自己mfc程序中使用上述类嵌入脚本和自定义脚本对象的过程,步骤如下。
1.首先将文章所附例子工程中ActiveScriptHost.cpp,ActiveScriptHost.h,Host_Proxy.cpp,Host_Proxy.h,MFCScriptHost.odl拷贝到当前工程中。
2.在当前工程的xxx.rc中加入以下内容,即将类型库加入到程序资源文件中
#ifdef_DEBUG
1TYPELIB"Debug\\MFCScriptHost.tlb"
#else
1TYPELIB"Release\\MFCScriptHost.tlb"
#endif
3.在需要使用的类成员中加入CHost_Proxym_ScriptProxy;成员,在OnCreate或OnInitDialog中加入
m_ScriptProxy.CreateEngine(L"JavaScript");//创建脚本引擎
m_ScriptProxy.AddScriptItem(L"test",m_ptestObject->GetUnknown());//加入名称为test的IDispatch对象
4.
com对象的生成有两种方案,一种是使用MFC方式生成,即对象从CmdTarget继承,并选中automation的radiobutton(如图表1),并通过ClassWizard中的自动化标签加入方法和属性(如图表2)。
这种对象的缺点是无法自定义事件源供脚本程序接收。
。
图表1
图表2
5.
另一种是使用ATL产生com对象,这种方式可以生成带事件功能的对象,此外功能灵活、方便。
我更倾向于ATL方式生成com对象。
在Classes点击右键选择NewATLObject(如图表3),出现ATLObjectWizard(如图表4)。
选中SimpleObject,出现ATLObjectWizard属性对话框(如图表5),按要求填入shortname(即组件名称),如果欲支持组件的事件功能一定选中SupportConnectionPoints(如图表6),点击确定后vc会自动生成代码。
图表3
图表4
图表5
图表6
6.在vc生成的InitATL()后,一定要手工加入_Module.RegisterTypeLib()用来注册组件的类型库。
这是因为TL的IDispatch实现会将自身调用委托给相应组件的类型库接口ITypeInfo去执行(如图表7)。
图表7
7.如欲实现组件的事件功能还需在相应对象点击ImplementConnectionPoint菜单项(如图表8),选择实现事件源接口(如图表9),点击确定后系统会生成发送出发事件的委托类,并添加相应代码(如图表10)。
图表8
图表9
图表10
8.
因为com对象发出的事件需要在脚本环境下使用,脚本环境需要通过对象的IProvideClassInfo2接口获得默认事件源(即sourcedefaultdispinterface),所以com对象还需实现IProvideClassInfo2接口(如图表11),加入红框内内容即可。
图表11
9.脚本内调用的codesample例子如下,假设对象test有方法hello,有事件OnRun()
javaScript例子
test.hello()//调用test对象的hello方法
functiontest:
:
OnRun()//test对象事件OnRun()的回调函数
{
}
vbScript例子
test.hello‘调用test对象的hello方法
Subtest_OnRun()‘test对象事件OnRun()的回调函数
endSub
Jscript与VbScript详细用法见
msdn/platformSDKdocumentation/ToolsandScripting/Scripting
10.如果使用mfc方式生成的com对象,用以下代码将对象作为脚本变量加入到脚本环境中
m_ScriptProxy.AddScriptItem(L"testctrl",m_ctrl.GetIDispatch(FALSE));
11.如果使用ATL方式生成的com对象,用以下代码将对象作为脚本变量加入到脚本环境中
CComObject:
:
CreateInstance(&m_peventObject);
//因为多步骤构造需要调用finalContruct
m_ScriptProxy.AddScriptItem(L"event",m_peventObject->GetUnknown());
12.脚本代码的接入方法,strScriptText为代码脚本
CStringstrScriptText;
m_ctlScriptText.GetWindowText(strScriptText);
if(strScriptText.GetLength()>0)
{
BSTRbstrText=strScriptText.AllocSysString();
m_ScriptProxy.AddScriptCode(bstrText);
SysFreeString(bstrText);
}
下面简要介绍将一个MFC的CButton变为脚本中可用组件的方法
用上述步骤建立CAtlButton,CAtlRect
classATL_NO_VTABLECAtlButton:
publicCComObjectRootEx,
publicCComCoClass,
publicIConnectionPointContainerImpl,
publicIDispatchImpl
{
//IAtlButton
public:
STDMETHOD(Move)(/*[in]*/IAtlRect*rect);//移动button
STDMETHOD(get_Rect)(/*[out,retval]*/IAtlRect**pVal);//得到buttonsize
};
classATL_NO_VTABLECAtlRect:
publicCComObjectRootEx,
publicCComCoClass,
publicIDispatchImpl
{
public:
CRectm_rc;
public:
STDMETHOD(get_Bottom)(/*[out,retval]*/long*pVal);
STDMETHOD(put_Bottom)(/*[in]*/longnewVal);
STDMETHOD(get_Right)(/*[out,retval]*/long*pVal);
STDMETHOD(put_Right)(/*[in]*/longnewVal);
STDMETHOD(get_Left)(/*[out,retval]*/long*pVal);
STDMETHOD(put_Left)(/*[in]*/longnewVal);
STDMETHOD(get_Top)(/*[out,retval]*/long*pVal);
STDMETHOD(put_Top)(/*[in]*/longnewVal);
};
classCButtonWithAtl:
publicCButton,publicCComObjectGlobal
{
public:
STDMETHOD(get_Rect)(/*[out,retval]*/IAtlRect**pVal);
STDMETHOD(Move)(/*[in]*/IAtlRect*rect);
};
CComObjectGlobal表示com对象全局生存不需要引用计数,CComObject表示在堆上创建对象,通过引用计数控制生命周期。
STDMETHODIMPCButtonWithAtl:
:
get_Rect(IAtlRect**pVal)
{
CRectrc;
this->GetWindowRect(rc);
this->GetParent()->ScreenToClient(rc);
CComObject*pRect;
CComObject:
:
CreateInstance(&pRect);
pRect->m_rc=rc;
pRect->QueryInterface(pVal);
returnS_OK;
}
STDMETHODIMPCButtonWithAtl:
:
Move(IAtlRect*rect)
{
CRectrc;
CComPtrptr(rect);
ptr->get_Left(&rc.left);
ptr->get_Top(&rc.top);
ptr->get_Right(&rc.right);
ptr->get_Bottom(&rc.bottom);
this->MoveWindow(rc);
returnS_OK;
}
加入代码
CButtonWithAtlm_btnRun;
m_ScriptProxy.AddScriptItem(L"runbtn",m_btnRun.GetUnknown());
javaScripttestcode为
varrc=runbtn.Rect;
rc.right+=10;//按钮宽度每次增加10pixel
runbtn.Move(rc);