C#、C、Lua分别有不同的异常处理机制,在跨语言函数调用的时候,必须要正确的处理异常,否则会导致堆栈错误、内存泄露、程序崩溃等问题。tolua对此做了非常全面的安全处理,值得我们去学习。如果我们要自己去做一些C层面的扩展,也必须要对这些底层原理熟记于心,才能避免各种诡异问题。

本文作者游蓝海,未经许可禁止转载。

文章目录

C#调用Lua函数

LuaException

Lua调用C#方法

注册C#函数

调用C#函数

C#调用Lua函数

C#调用Lua函数的时候,必须使用lua_pcall接口进行调用。因为lua_pcall会拦截C语言层面的异常,确保异常不会破坏掉C#解释器的堆栈。

lua_pcall API说明:

int lua_pcall (lua_State *L, int nargs, int nresults, int error);

参数名称

说明

L

Lua解释器指针

nargs

参数数量

nresults

返回值数量

error

异常处理函数句柄。也就是处理函数在Lua栈上的索引

如果函数调用成功,返回0,并且在Lua栈push上nresults个结果(如果实际结果不足,会push nil);否则,返回相应的错误码,并且在Lua栈push上错误原因的字符串。不管是否执行成功,lua_pcall都会把push的函数和nargs个参数从栈顶清除。

如果指定了错误处理函数,在函数调用失败的时候,Lua会调用该错误处理函数来处理错误原因。通常情况下,我们可以传入debug.traceback函数,这样可以得到函数调用堆栈。

ToLua在BeginPCall的时候,会将错误处理函数traceback和要调用的函数压栈。在PCall的时候,如果函数调用出错,则向C#层面抛出异常LuaException。

C# LuaState类部分源码:

// 将错误处理函数和要调用的函数压栈

public int BeginPCall(int reference)

{

return LuaDLL.tolua_beginpcall(L, reference);

}

// 执行函数调用

public void PCall(int args, int oldTop)

{

if (LuaDLL.lua_pcall(L, args, LuaDLL.LUA_MULTRET, oldTop) != 0)

{

string error = LuaToString(-1);

throw new LuaException(error, LuaException.GetLastError());

}

}

// 清理堆栈

public void EndPCall(int oldTop)

{

LuaDLL.lua_settop(L, oldTop - 1);

}

LuaException

LuaException同时记录了Lua层的异常信息和C#层的信息,如果要做异常上报和分析,需要了解LuaException的结构。

public class LuaException : Exception

{

public LuaException(string msg, Exception e = null);

//返回异常产生时的C#堆栈

public override string StackTrace;

//返回最近一次的Lua异常

public static Exception GetLastError();

}

构造函数参数

说明

msg

通常是错误原因加上Lua堆栈。如果是Lua端出了异常,msg为Lua端的错误原因和Lua堆栈。如果是C#端出了异常,msg是C#异常名称和Lua堆栈,如果要看C#端的堆栈,需要修改C#的toluaL_exception,从异常对象中获取完整的异常信息。

e

上一个异常对象。可以是C#异常,也可以是Lua抛出的异常。因为函数可能会递归调用,底层的异常需要一层层的向外抛。

Lua调用C#方法

C#方法在注册到Lua的时候,有两个步骤:

先在C#层生成一个静态的C# Wrap函数,并传递给C语言层;

然后在C语言层面经过一层函数包装(tolua_closure),生成一个新的闭包函数,然后在注册到Lua表中。

当Lua层调用C#函数的时候,也是两个步骤:

先调用C函数tolua_closure(之前注册时生成的的闭包函数);

tolua_closure内部会调用真正的C# Wrap函数。

如果调用C#的过程中出现了异常,则tolua_closure会将这个C#的异常信息,用lua_error抛给Lua。

注册C#函数

C#函数在push给Lua的时候,会调用接口tolua_pushcfunction,将执行结果标记和C#函数作为tolua_closure的闭包参数(upvalue)。

tolua_pushcfunction源码:

//hack for luac, 避免luac error破坏包裹c#函数的异常块(luajit采用的是类似c++异常)

LUA_API int tolua_pushcfunction(lua_State *L, lua_CFunction fn)

{

// 第1个闭包参数:结果标记。为true表示函数执行过程中出了异常

lua_pushboolean(L, 0);

// 第2个闭包参数:要包装的C#函数

lua_pushcfunction(L, fn);

// 生成闭包函数

lua_pushcclosure(L, tolua_closure, 2);

return 0;

}

lua_pushcclosure API说明:

void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n);

参数名称

说明

L

Lua解释器指针

fn

C函数指针

n

n个闭包变量(upvalue)

生成一个新的闭包函数放到栈顶。可以指定n个闭包参数,参数需要先push到栈顶(第一个参数要第一个push)。在闭包函数中,通过函数lua_upvalueindex(index)来获取闭包参数的索引,然后调用其他lua api来获取或设置值。

调用C#函数

Lua中调用C#函数的时候,实际上调用的是闭包函数tolua_closure。tolua_closure取到tolua_pushcfunction传入的闭包参数,也就是真正的C#函数指针,然后调用C#函数。

tolua_closure源码:

int tolua_closure(lua_State *L)

{

// 拿到闭包变量2,也就是真正的C#函数

lua_CFunction fn = (lua_CFunction)lua_tocfunction(L, lua_upvalueindex(2));

// 调用C#函数

int r = fn(L);

// 如果C#函数中出现异常

if (lua_toboolean(L, lua_upvalueindex(1)))

{

// 恢复异常标记,确保不会影响下一次函数调用

lua_pushboolean(L, 0);

lua_replace(L, lua_upvalueindex(1));

// 将异常抛给Lua解释器

return lua_error(L);

}

return r;

}

C# Wrap函数都必须处理所有的C#异常,然后将异常信息通过toluaL_exception传递给C层:

public static int toluaL_exception(IntPtr L, Exception e)

{

LuaException.luaStack = new LuaException(e.Message, e, 2);

return tolua_error(L, e.ToString());

}

tolua_error向当前正在执行的C闭包函数写入错误标记,当前函数执行完毕后,由tolua_closure来检查执行结果:

int tolua_error(lua_State *L, const char *msg)

{

// 将错误标记(true)赋值给第一个闭包变量

lua_pushboolean(L, 1);

lua_replace(L, lua_upvalueindex(1));

// 返回错误原因

lua_pushstring(L, msg);

return 1;

}

tolua unity 报错_unity tolua异常处理相关推荐

  1. Unity 报错之 ToLua打包:Unable to find tolua DllNotFoundException: tolua

    Unity 报错之 ToLua打包:Unable to find tolua DllNotFoundException: tolua 最近在学习使用LuaFramework框架,使用其打出的安卓包运行 ...

  2. tolua unity 报错_Unity3D热更新之LuaFramework篇[01]--从零开始

    解压刚刚下载好的压缩包,发现里面是一个Unity工程(如图2-1),于是用 unity打开此工程. 图2-1 我使用的Unity版本为5.5.5f1,会提示需要升级,是否备份,点"Go He ...

  3. Unity报错之 No Sprite Editor Window registered. Please download 2D Sprite package from Package Manager.

    Unity报错之 No Sprite Editor Window registered. Please download 2D Sprite package from Package Manager. ...

  4. Unity报错:Setting the parent of a transform which resides in a Prefab......解决方案(强行设置)

    @[TOC](Setting the parent of a transform which resides in a Prefab Asset is disabled to prevent data ...

  5. Unity报错Unsupported D3D format 0x58

    Unity报错Unsupported D3D format 0x58 描述错误 每当新的视频开始播放,或者视频中的分辨率发生变化(使用HLS)时,使用DX11时都会引发错误"不支持的D3D格 ...

  6. Unity 报错之 Scope was not disposed, You should use the ‘using‘ keyword or manually call Dispose.

    Unity 报错之 Scope was not disposed! You should use the 'using' keyword or manually call Dispose. 发生错误 ...

  7. VS2019附加到Unity报错 未能完成操作,未指定的错误

    问题:Visual Studio2019 附加到Unity 报错未能完成操作,未指定的错误.编译也没有失败都成功了. 解决方法:在Unity工程中看是不是生成了.vsconfig文件,把这个删除后,再 ...

  8. Unity 报错之 The same field name is serialized multiple times in the class or its parent class.

    Unity 报错之 The same field name is serialized multiple times in the class or its parent class. Unity 报 ...

  9. Unity报错 CS0619: ‘GUITexture‘ is obsolete的解决办法

    unity加载旧版本的项目报错:Unity报错Assets\Standard Assets\Utility\ForcedReset.cs(6,27): error CS0619: 'GUITextur ...

  10. Unity报错之【发布UWP显示“Could not find any supported UWP SDK installations”】

    这是由于UWP发布需要借助VisualStudio来发布,VisualStudio需要安装UWP SDK支持. 打开VisualStudioInstaller,点击修改,在单个组件列表中找到 下载安装 ...

最新文章

  1. 送你38个常用的Python库,数值计算、可视化、机器学习等8大领域都有了
  2. The number of Oracle redo threads (2) is not the same as the number of checkpoint threads (1)
  3. neo4j中文社区 php,neo4j 社区版搭建教程
  4. 常考数据结构与算法:查找第K大元素算法
  5. php怎么看回调的异步通知的数据_php app支付宝回调(异步通知)详解
  6. max格式转obj小工具_Python写图片格式批量处理工具!你还一张一张转格式吗?
  7. 消除switch语句以获得更好的代码结构
  8. spring2.5.4+hibernate3.2.6+struts2+jbpm3.2.2
  9. 计算机编程学英语词汇,计算机编程英语词汇大全
  10. markdown编辑数学公式
  11. HALCON 21.11:深度学习笔记---Data(数据)(3)
  12. Android系统匿名共享内存(Anonymous Shared Memory)C++调用接口分析(7)
  13. 网站开发笔记【四】css hack技术
  14. 181101新闻:午后阳光下集思广益,课例研修尝试与挑战并存
  15. JSP详细教学新手必看
  16. Eclipse中打开文件时单击和双击的设置
  17. MATLAB机器学习决策树网格法优化参数学习笔记
  18. fw325r没有虚拟服务器,FAST FW325R配置教程
  19. 06-图3 六度空间
  20. CAD填充块Hatch的深度解析

热门文章

  1. Java实现简易版金山打字
  2. 群联固态硬盘修复工具_固态硬盘有三大要素,那么你的固态硬盘买对了吗?
  3. 代码整洁之道之总结篇
  4. 余弦 matlab 幅度谱,数字图像处理_旋转与幅度谱(含MATLAB代码)
  5. LeapFTP 使用指南
  6. java简体字转换繁体字_java代码实现简体繁体转换
  7. 真实遇到的产品经理面试题
  8. 无缝拼接屏是怎样实现的?有什么特点!
  9. System Toolkit 3.3.3 中文版 系统维护工具箱
  10. 移动端UI设计尺寸规范以及iPhone尺寸大小