给duilib增加脚本支持(三)

这一章介绍脚本插件的编写,现在才是划重点的时候了。angelscrript的作者提供了很完整的文档,由于本人英语有限,我错误恳请指出。angelscript简直就是给C++程序量身订造的脚本语言,和C++语言基本无差别,还是那句话,可惜没有流行起来。不说闲话,开始上代码。

先定义两个导出函数。供duilib回调创建脚本引擎和删除脚本引擎。

extern "C" __declspec(dllexport) IScriptEngine* __stdcall CreateScriptEngine()
{
	return new CScriptEngine;
}

extern "C" __declspec(dllexport) void __stdcall DeleteScriptEngine(IScriptEngine *pEngine)
{
	if(pEngine)
	{
		delete (CScriptEngine *)pEngine;
		pEngine = NULL;
	}
}

增加一个CScriptEngine类,继承接口IScriptEngine,嗯,其实就那个纯虚类。

ScriptEngine.h
class CScriptEngine : public IScriptEngine
{
public:
	CScriptEngine(void);
	virtual ~CScriptEngine(void);

	virtual bool AddScriptCode(LPCTSTR pScriptCode);
	virtual bool AddScriptFile(LPCTSTR pstrFileName);
	virtual bool ExecuteScript(LPCTSTR funName, CControlUI *pControl);
	virtual bool ExecuteScript(LPCTSTR funName, CControlUI *pControl, TEventUI *ev);

protected:
	void MessageCallback(const asSMessageInfo &msg);
private:
	asIScriptEngine  *engine;
	asIScriptContext *ctx;

	int m_nModuleCount;

	CStdStringPtrMap m_mapContent;
};
//ScriptEngine.cpp
static void ScriptLineCallback(asIScriptContext *ctx, DWORD *timeOut)
{
	// If the time out is reached we abort the script
	if( *timeOut < timeGetTime() )
		ctx->Abort();
}

//
//CScriptEngine
CScriptEngine::CScriptEngine(void) : m_nModuleCount(0)
{
	ctx = NULL;

	engine = asCreateScriptEngine();

	int r = 0;
	r = engine->SetMessageCallback(asMETHOD(CScriptEngine, MessageCallback), this, asCALL_THISCALL); assert( r >= 0 );

	//脚本代码的字符编码  0 - ASCII, 1 - UTF8. Default: 1 (UTF8). 
	r = engine->SetEngineProperty(asEP_SCRIPT_SCANNER, 0); assert( r >= 0 );

#ifdef _UNICODE
	//脚本内部字符串的字符编码 0 - UTF8/ASCII, 1 - UTF16. Default: 0 (UTF8) 
	r = engine->SetEngineProperty(asEP_STRING_ENCODING, 1); assert( r >= 0 );
#endif

	CScriptRegister reg(engine);
	reg.RegisterAll();
}


CScriptEngine::~CScriptEngine(void)
{
	if(ctx)
	{
		ctx->Release();
		ctx = NULL;
	}
}

void CScriptEngine::MessageCallback(const asSMessageInfo &msg)
{
	if( msg.type == asMSGTYPE_ERROR )
	{
		ATL::CStringA temp;
		temp.Format("row = %d\r\ncol = %d\r\nsection=%s \r\nmessage = %s\r\n", 
			msg.row, msg.col, msg.section, msg.message);
		MessageBoxA(NULL, temp, "complie error", MB_OK);
	}
}

bool CScriptEngine::AddScriptCode(LPCTSTR pScriptCode)
{
	m_nModuleCount++;
	
	char module[255];
	sprintf(module, "module%d", m_nModuleCount);

	int r = 0;
	CScriptBuilder builder;
	r = builder.StartNewModule(engine, module);
	if( r < 0 )
		return false;

	LSSTRING_CONVERSION;
	builder.AddSectionFromMemory("section1", LST2A(pScriptCode));

	r = builder.BuildModule();
	if( r < 0 ) return false;

	//保存脚本函数地址
	asIScriptModule *pModule = builder.GetModule();
	int funCount = pModule->GetFunctionCount();
	for (int i=0; i<funCount; i++)
	{
		asIScriptFunction *pFun = pModule->GetFunctionByIndex(i);
		USES_CONVERSION;
		m_mapContent.Set( A2T((LPSTR)pFun->GetName()),  pFun);
	}

	return true;
}

bool CScriptEngine::AddScriptFile(LPCTSTR pstrFileName)
{
	LPBYTE pData = NULL;
	DWORD dwSize = CRenderEngine::LoadImage2Memory(STRINGorID(pstrFileName), 0, pData);
	if(dwSize == 0U || !pData)
		return false;
	bool rbool = false;
	rbool = AddScriptCode((LPCTSTR)pData);
	CRenderEngine::FreeMemory(pData); 

	return rbool;
}

bool CScriptEngine::ExecuteScript(LPCTSTR funName, CControlUI *pControl)
{
	asIScriptFunction *pFun = static_cast<asIScriptFunction *>(m_mapContent.Find(funName));
	if(!pFun) return false;

	if(!ctx) ctx = engine->CreateContext();

	DWORD dwTime = timeGetTime() + 5000; //设置脚本运行超时时间

	int r = ctx->SetLineCallback(asFUNCTION(ScriptLineCallback), &dwTime, asCALL_CDECL); if( r < 0 ) return false;

	r = ctx->Prepare(pFun);	if( r < 0 ) return false;

	//传入入口参数
	r = ctx->SetArgObject(0, pControl); if( r < 0 ) return false;

	r = ctx->Execute();
	if(r == asEXECUTION_FINISHED)
	{
		return true;
	}
	else if( r == asEXECUTION_EXCEPTION )
	{
		CStringA temp;
		temp.Format("Exception:%s \r\nFunction: %s \r\nLine: %d", 
			ctx->GetExceptionString(), 
			ctx->GetExceptionFunction()->GetDeclaration(), 
			ctx->GetExceptionLineNumber());
		MessageBoxA(NULL, temp, "script error", MB_OK);
		return false;
	}

	return true;
}

bool CScriptEngine::ExecuteScript(LPCTSTR funName, CControlUI *pControl, TEventUI *ev)
{
	asIScriptFunction *pFun = static_cast<asIScriptFunction *>(m_mapContent.Find(funName));
	if(!pFun) return false;

	if(!ctx) ctx = engine->CreateContext();

	DWORD dwTime = timeGetTime() + 5000; //设置脚本运行超时时间

	int r = ctx->SetLineCallback(asFUNCTION(ScriptLineCallback), &dwTime, asCALL_CDECL); if( r < 0 ) return false;

	r = ctx->Prepare(pFun);	if( r < 0 ) return false;

	//传入入口参数
	r = ctx->SetArgObject(0, pControl); if( r < 0 ) return false;
	r = ctx->SetArgObject(1, ev); if( r < 0 ) return false;

	r = ctx->Execute();
	if(r == asEXECUTION_FINISHED)
	{
		return true;
	}
	else if( r == asEXECUTION_EXCEPTION )
	{
		CStringA temp;
		temp.Format("Exception:%s \r\nFunction: %s \r\nLine: %d", 
			ctx->GetExceptionString(), 
			ctx->GetExceptionFunction()->GetDeclaration(), 
			ctx->GetExceptionLineNumber());
		MessageBoxA(NULL, temp, "script error", MB_OK);
		return false;
	}

	return true;
}

这里面用到了一个类CScriptRegister,注册duilib的各种控件到脚本引擎中,我还没有找到angelscript注册父类的方法,所以需要把脚本中需要调用的控件成员函数都注册进去,包括它的父类函数,要用到的函数都要注册进去。不仅如此,还需要注册比如RECT,SIZE等结构体,define的常量,枚举类型enum,这里面将会有巨大的烦躁的重复性的工作。有这精力,我不如直接在程序中自定义控件了是不是???唉,话不是这么说的。打个比方,如果duilib要做到语言无关化呢?如果可以在脚本中自定义控件,是不是很爽?

我们先把CControlUI注册进去,只注册一个成员函数SetBkColor()。

//
CControlUI *CControlUI_Ref_Factory()
{
	// The class constructor is initializing the reference counter to 1
	return new CControlUI();
}

bool CRegCControlUI::Register(asIScriptEngine *engine)
{
	int r =0;

	//Registering DuiLib Class
	r = engine->RegisterObjectType("CControlUI", 0, asOBJ_REF|asOBJ_NOCOUNT); 

	// Registering the factory behaviour
	r = engine->RegisterObjectBehaviour("CControlUI", asBEHAVE_FACTORY, "CControlUI@ f()", asFUNCTION(CControlUI_Ref_Factory), asCALL_CDECL);  assert( r >= 0 );

	// Register class method functions
	r = engine->RegisterObjectMethod("CControlUI", "void SetBkColor(int dwBackColor)", asMETHOD(CControlUI, SetBkColor), asCALL_THISCALL);  assert( r >= 0 );


	return r >= 0;
}

好了。大功告成!

代码共享地址:

https://gitee.com/Liqs99/DuiLib_DuiEditor

https://github.com/xfcanyue/DuiLib_DuiEditor

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值