对静态库的非托管C#调用 [英] Unmanaged C# calls to a static library

查看:83
本文介绍了对静态库的非托管C#调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm using swig to generate C# wrappers for some C code base to be used from C#. When I run swig, it generates a wrapper c file that exposes all the functionality to the generated PInvoke C# file... For example:

// 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'选项以使其正常工作.

This works great when I am building a dynamic library. However, I need to support iOS now, so I've prepared a static library, and passed in the -dllimport '__Internal' option to swig for that to work.

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

Unfortunately, I am getting linking errors such as:

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

确实,我的意思是"CSharp_DMGameMode_timeLimit_set",但这就是"entrypoint"参数的意义所在?

Indeed, I did mean "CSharp_DMGameMode_timeLimit_set", but that's the point of the "entrypoint" argument?

因此,由于此错误是由Unity生成的Xcode项目引发的,因此我不太确定失败的根源是什么.静态库会失败吗?这是要固定在Unity端还是swig端的东西?

So, since this error is thrown by the Xcode project Unity generated, I am not quite sure what's the source of the failure. Does it fail for static libraries? Is this something to be fixed on Unity side or swig side?

更新:在对此进行了深入研究之后,我认为我对这里发生的事情有了一点了解.

Update: After digging more into this, I think I have a slight idea of what's going on here..

主要问题似乎来自AOT编译器,后者试图将所有CS代码编译为ARM程序集.这似乎是iOS所必需的,因此在Unity的AOT编译期间,它会生成文件RegisterMonoModules.cpp,该文件尝试定义对本机代码的访问功能. RegisterMonoModules.cpp不接受entrypoint参数,这会引发未定义的符号错误...

The main issue seems to be from the AOT compiler, which tries to compile all the CS code to an ARM assembly. This seems to be required for iOS, so during Unity's AOT compilation, it generates a file RegisterMonoModules.cpp, which attempts to define access functions to the native code. RegisterMonoModules.cpp doesn't honor the entrypoint parameter, which causes undefined symbol errors to be thrown...

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

Still attempting to find a proper workaround.

推荐答案

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

The main issue seems to be from Unity, and not Swig nor Mono. As mentioned above, Unity performs AOT compilation that doesn't honor the entry point argument. This produces cpp code that calls the function name, not the entry point name..

我已经通过将脚本后端切换到IL2cpp来确认了这一点,并且在此处使用了入口点名称.

I've confirmed this by switching the scripting backend to IL2cpp, and the entry point name was honored there.

让我们切换到回调.与该问题不完全相关,但是它绝对适合Unity + Native插件+ iOS的环境.

Let's switch over to callbacks. Not exactly related to the question, but it definitely fits the context of Unity + Native plugins + iOS.

AFAIK,您无法使用Mono 2x将托管方法编组到iOS上的本机土地.我以前不得不从swig生成的文件中删除所有字符串回调和异常处理程序.幸运的是,经过一些调整,IL2Cpp支持回调:

AFAIK, you can't have a managed method marshaled to native land on iOS using Mono 2x. I previously had to delete all the string callback and exception handlers from the swig generated files. Fortunately, IL2Cpp supports callbacks, after a little tweaking:

  1. 添加using AOT;
  2. 使用[MonoPInvokeCallback(typeof(method_signature))]
  3. 装饰回调
  1. Add using AOT;
  2. Decorate callbacks with [MonoPInvokeCallback(typeof(method_signature))]

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

You can use this script, just use it to process the generated swig files:

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)


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


For anyone looking for help converting their generated swig CSharp PInvoke file to something mono 2x scripting backend will allow, stick this somewhere in your build process, after the CSharp files are generated:

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)

这篇关于对静态库的非托管C#调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆