ios - 对静态库的非托管 C# 调用

标签 ios unity-game-engine mono pinvoke swig

我正在使用 swig 生成 C# 包装器,以便从 C# 使用一些 C 代码库。当我运行 swig 时,它会生成一个包装器 c 文件,该文件向生成的 PInvoke C# 文件公开所有功能...例如:

// This is in KodLogic_wrap.c
SWIGEXPORT void SWIGSTDCALL CSharp_DMGameMode_timeLimit_set(void * jarg1, unsigned short jarg2) { ... }

// This is in KodLogicPInvoke.cs
[global::System.Runtime.InteropServices.DllImport("KodLogic", EntryPoint="CSharp_DMGameMode_timeLimit_set")]

当我构建动态库时,这非常有用。不过,我现在需要支持 iOS,所以我准备了一个静态库,并传入 -dllimport '__Internal' 选项来 swig 使其工作。

不幸的是,我遇到了链接错误,例如:

"_DMGameMode_timeLimit_set", referenced from:
  RegisterMonoModules() in RegisterMonoModules.o
  (maybe you meant: _CSharp_DMGameMode_timeLimit_set)

确实,我的意思是“CSharp_DMGameMode_timeLimit_set”,但这就是“入口点”参数的重点?

所以,由于这个错误是由 Unity 生成的 Xcode 项目引发的,所以我不太确定失败的根源是什么。静态库会失败吗?这是需要在 Unity 端还是 swig 端修复的问题?

更新:在深入研究之后,我想我对这里发生的事情有了一些了解..

主要问题似乎来自 AOT 编译器,它尝试将所有 CS 代码编译为 ARM 程序集。这似乎是 iOS 所需要的,因此在 Unity 的 AOT 编译过程中,它会生成一个文件 RegisterMonoModules.cpp,该文件尝试定义对原生代码的访问函数。 RegisterMonoModules.cpp 不支持入口点参数,这会导致抛出 undefined symbol 错误...

仍在尝试寻找适当的解决方法。

最佳答案

主要问题似乎来自 Unity,而不是 Swig 或 Mono。如上所述,Unity 执行的 AOT 编译不支持入口点参数。这会生成调用函数名称而不是入口点名称的 cpp 代码。

我已经通过将脚本后端切换到 IL2cpp 来确认这一点,并且入口点名称在那里得到认可。


让我们切换到回调。与问题不完全相关,但它绝对适合 Unity + Native 插件 + iOS 的上下文。

据我所知,您无法使用 Mono 2x 将托管方法编码(marshal)到 iOS 上的本地。我以前必须从 swig 生成的文件中删除所有字符串回调和异常处理程序。幸运的是,IL2Cpp 经过一些调整后支持回调:

  1. 使用 AOT 添加
  2. 使用 [MonoPInvokeCallback(typeof(method_signature))] 修饰回调

你可以使用这个脚本,只需用它来处理生成的swig文件:

def process_csharp_callbacks(pinvoke_file):
  """Process PInvoke file by fixing the decorators for callback methods to use:
  [MonoPInvokeCallback(typeof(method_signature))]
  """
  # prepare requirements
  with open(pinvoke_file) as f:
    content = f.read()

  callback_methods_regex = re.compile(r"( +)static (?:void|string) (?:SetPending|CreateString)\w*\([\s\w\,]+\)")
  callback_decorator = "[MonoPInvokeCallback(typeof(ExceptionDelegate))]"
  callback_arg_decorator = "[MonoPInvokeCallback(typeof(ExceptionArgumentDelegate))]"
  callback_str_decorator = "[MonoPInvokeCallback(typeof(SWIGStringDelegate))]"
  # add use AOT
  content = content.replace("\n\n", "\nusing AOT;\n", 1)
  # fix callback methods
  def method_processor(match):

    match_string = match.group()
    indentation = match.captures(1)[0]

    if match_string.find(",") != -1:
      fix = callback_arg_decorator
    elif match_string.find("static string") != -1:
      fix = callback_str_decorator
    else:
      fix = callback_decorator

    return indentation + fix + "\n" + match_string

  content = callback_methods_regex.sub(method_processor, content)
  # write it back
  with open(pinvoke_file, "w+") as f:
    f.write(content)

对于任何寻求帮助将生成的 swig CSharp PInvoke 文件转换为 mono 2x 脚本后端允许的文件的人,请在生成 CSharp 文件后将其粘贴到构建过程中的某个位置:

pinvoke_template = """{extern_prefix} CSharp_{method_signature};
  {normal_prefix} {method_signature} {{
    {return_statement}CSharp_{method_name}({method_args});
  }}"""

def process_csharp_wrapper(csharp_dir):
  """Reads the PINVOKE csharp file, and performs the following:
  1. Remove EntryPoint="xxx" from the decorators
  2. Make the methods match their native counterpart name
  3. Add a C# method with the original name, for compatability
  """
  # prepare requirements
  pinvoke_file = os.path.join(csharp_dir, "KodLogicPINVOKE.cs")
  with open(pinvoke_file) as f:
    content = f.read()

  decorator_regex = re.compile(r', EntryPoint=".*?"')
  method_regex = re.compile(r"(public static extern \w+[\w:\.]+)\s(([^S]\w+)\((?:([\w:\. ]+)\,?)*\));")
  # fix decorators
  content = decorator_regex.sub("", content)
  # fix method definitions
  def method_processor(match):
    extern_prefix = match.captures(1)[0]
    return pinvoke_template.format(
      extern_prefix=extern_prefix,
      normal_prefix=extern_prefix.replace("extern ", ""),
      method_signature=match.captures(2)[0],
      return_statement=("return " if extern_prefix.find("void") == -1 else ""),
      method_name=match.captures(3)[0],
      method_args=", ".join(map(lambda s: s.strip().split()[1], match.captures(4)))
    )

  content = method_regex.sub(method_processor, content)
  # write it back
  with open(pinvoke_file, "w+") as f:
    f.write(content)

关于ios - 对静态库的非托管 C# 调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37448882/

相关文章:

kinect - 从 kinect 保存的图像是黑色的

bash - 如何强制 F# interactive 默认引用 Gtk#?

macos - Mono DLL 打开适当的 Windows/Mac DLL/DyLib

ios - 如何旋转 CAShapeLayer 及其图案?

ios - ionic 框架ios构建失败社会共享|偏爱

c# - Hololens 上的 C# Unity 中未找到模块 C++ .dll

c# - 使游戏对象在有限的时间内出现和消失

ios - 无法从联系人中获取 ABPerson 的图像

iphone - 如何让从相机拍摄的图像占据整个屏幕

ios - 存储 CLLocation 并保留注释