如何从 Lua 调用 C# 委托,委托可以抛出异常

Posted

技术标签:

【中文标题】如何从 Lua 调用 C# 委托,委托可以抛出异常【英文标题】:How do I invoke a C# delegate from Lua where the delegate can throw an exception 【发布时间】:2021-10-30 22:29:42 【问题描述】:

在基于 Linux 的 docker 映像中运行以下代码会导致 CLR 崩溃。

static void Main(string[] args)
    
        System.Console.WriteLine("Starting program");
        using (NLua.Lua luaState = new NLua.Lua())
        
            System.Action invoke = () => throw new System.Exception("message");
            luaState["invoke"] = invoke;
            try
            
                System.Console.WriteLine("Invoking delegate");
                luaState.DoString(@"invoke()");
            
            catch (System.Exception ex)
            
                // We never get here
                System.Console.WriteLine("Exception caught");
                throw;
            
        

    

我是不是做错了什么?

Dockerfile 是 Visual Studio 的默认建议,进程是从 Visual Studio 启动的。

在 Docker 之外运行程序,即。在 Windows 主机上,按预期工作。 (命中 catch 块,不会导致 CLR 上的致命情况)

将调用包装在 pcall 中并不能解决问题。

可以通过注册函数而不是发送委托来避免这个问题。 如果委托没有抛出异常,它会按预期被调用。

来自图像的控制台日志;

Starting program
Invoking delegate
Unhandled exception. System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
---> System.Exception: message
at LuaTest.Program.<>c.<Main>b__0_0() in C:\Source\Tests\LuaTest\Program.cs:line 10
--- End of inner exception stack trace ---
at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
at NLua.MetaFunctions.CallDelegateInternal(Lua luaState)
at NLua.MetaFunctions.CallDelegate(IntPtr state)
at KeraLua.NativeMethods.lua_pcallk(IntPtr luaState, Int32 nargs, Int32 nresults, Int32 errorfunc, IntPtr ctx, IntPtr k)
at KeraLua.Lua.PCall(Int32 arguments, Int32 results, Int32 errorFunctionIndex)
at NLua.Lua.DoString(String chunk, String chunkName)
at LuaTest.Program.Main(String[] args) in C:\Source\Tests\LuaTest\Program.cs:line 15
Fatal error. Internal CLR error. (0x80131506)
at KeraLua.NativeMethods.lua_pcallk(IntPtr, Int32, Int32, Int32, IntPtr, IntPtr)
at KeraLua.NativeMethods.lua_pcallk(IntPtr, Int32, Int32, Int32, IntPtr, IntPtr)
at KeraLua.Lua.PCall(Int32, Int32, Int32)
at NLua.Lua.DoString(System.String, System.String)
at LuaTest.Program.Main(System.String[])

【问题讨论】:

你能确认你在 Mono 上是否有同样的行为吗? Linux 上的 IIRC CLR 处理 SEH 的方式与 Windows 不同。这会导致 setjmp/longjmp 在堆栈展开时使 CLR 崩溃。 @ViniciusJarina Mono 一直抱怨缺少 dll Lua54。 - 不知道我在这里的构建过程做错了什么。关于 longjmp 的问题,您可能是对的。 【参考方案1】:

自我回答;

这似乎是 NLua 的 MetaFunctions.CallDelegate 中的一个错误。委托的调用不在 try-catch 块中。对比LuaMethodWrapper.Call

致命的可能是由于尝试通过调用 longjmp 的非托管代码传播异常造成的。请参阅exceptions interoperability 上的 dotnet 核心说明

最直接的解决方法是使用注册函数注册委托。

Action action=()=>throw new Exception()
luastate.RegisterFunction("invoke",action.Target,action.Method);

【讨论】:

以上是关于如何从 Lua 调用 C# 委托,委托可以抛出异常的主要内容,如果未能解决你的问题,请参考以下文章

c# 从delegate.begininvoke 捕获异常而不调用delegate.endinvoke

C#图解教程 第十三章 委托

如何将委托或函数指针从 C# 传递到 C++ 并使用 InternalCall 调用它

C# 笔记——委托

C# 委托及各种写法

C# 中的委托数组