You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Android嵌入式Mono中使用Marshal.GetDelegateForFunctionPointer遇栈异常

Android下Mono嵌入中Marshal.GetDelegateForFunctionPointer栈异常的解决思路

我之前刚好碰过几乎一模一样的场景——在C++应用里嵌入Mono,通过Marshal.GetDelegateForFunctionPointer实现C#调用C原生函数,Windows上完全正常,到Android就触发调用栈异常。结合你的推测,问题核心确实是调用约定不匹配,下面具体拆解原因和可行的解决办法:

问题本质:平台调用约定的冲突

  • Windows环境下,Marshal.GetDelegateForFunctionPointer默认适配的是__stdcall调用约定——这种约定下,被调用的函数负责清理栈,和Windows原生函数的常见约定匹配,所以运行正常。
  • 但Android的JNI环境里,原生函数默认遵循的是JNICALL(本质对应ARM平台的AAPCS调用约定,或者x86上的__cdecl),这种约定是调用者负责清理栈。两种栈管理逻辑完全不同,直接混用就会导致函数调用后栈指针无法正确恢复,进而触发异常。

可行的解决办法

1. 统一调用约定为C标准约定

这是最直接的修复方式:

  • C++端:显式声明原生函数使用__cdecl(跨平台场景下可以用extern "C"配合__attribute__((cdecl)),ARM平台会自动映射到AAPCS):
    extern "C" __attribute__((cdecl)) void MyNativeFunc(int arg) {
        // 你的函数实现
    }
    
  • C#端:在声明委托时,通过UnmanagedFunctionPointer特性明确指定CallingConvention.Cdecl:
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void MyNativeDelegate(int arg);
    
    这样Marshal.GetDelegateForFunctionPointer创建委托时,就会按照Android平台的栈规则处理,和原生函数的约定完全匹配。

2. 验证Mono运行时的平台适配性

确保你嵌入的Mono是针对Android平台编译的版本,有些自定义编译的Mono可能会在调用约定处理上存在偏差。另外,初始化Mono运行时的时候,检查有没有误设置Windows相关的调用约定参数。

3. 备选方案:改用JNI桥接

如果上面的方法还是有问题,可以绕过Marshal.GetDelegateForFunctionPointer,直接用JNI实现C#到C的调用:

  • 在C++端注册JNI方法,让C#通过JNI接口调用原生函数。这种方式完全遵循Android的JNI调用约定,从根源上避免栈管理的冲突。

总结

核心就是让C#委托的调用约定和Android原生函数的调用约定严格对齐,Cdecl(对应ARM的AAPCS)是Android平台的正确选择,替代Windows上的StdCall。只要两边约定统一,栈异常的问题就能解决。

内容的提问来源于stack exchange,提问作者mbelew

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 04:16:37