对静态库的非托管 C# 调用
Unmanaged C# calls to a static library
我正在使用 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'
选项以让它工作。
不幸的是,我收到如下链接错误:
"_DMGameMode_timeLimit_set", referenced from:
RegisterMonoModules() in RegisterMonoModules.o
(maybe you meant: _CSharp_DMGameMode_timeLimit_set)
确实,我的意思是 "CSharp_DMGameMode_timeLimit_set",但这就是 "entrypoint" 论点的重点?
因此,由于此错误是由 Unity 生成的 Xcode 项目引发的,我不太确定失败的根源是什么。静态库会失败吗?这是要在 Unity 端还是 swig 端修复的问题?
更新:在深入研究之后,我想我对这里发生的事情有了一些了解..
主要问题似乎来自 AOT 编译器,它试图将所有 CS 代码编译为 ARM 程序集。这似乎是 iOS 所必需的,因此在 Unity 的 AOT 编译期间,它生成了一个文件 RegisterMonoModules.cpp
,该文件试图定义对本机代码的访问函数。 RegisterMonoModules.cpp
不遵守入口点参数,这会导致抛出未定义的符号错误...
仍在尝试找到合适的解决方法。
主要问题似乎来自 Unity,而不是 Swig 或 Mono。如上所述,Unity 执行不支持入口点参数的 AOT 编译。这会生成调用函数名称而不是入口点名称的 cpp 代码..
我已经通过将脚本后端切换到 IL2cpp 来确认这一点,并且入口点名称在那里得到了尊重。
让我们切换到回调。与问题不完全相关,但它绝对符合 Unity + Native plugins + iOS.
的上下文
AFAIK,您无法使用 Mono 2x 将托管方法编组到 iOS 上的本土。我以前不得不从 swig 生成的文件中删除所有字符串回调和异常处理程序。幸运的是,IL2Cpp 支持回调,稍作调整后:
- 添加
using AOT;
- 使用
[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)
我正在使用 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'
选项以让它工作。
不幸的是,我收到如下链接错误:
"_DMGameMode_timeLimit_set", referenced from:
RegisterMonoModules() in RegisterMonoModules.o
(maybe you meant: _CSharp_DMGameMode_timeLimit_set)
确实,我的意思是 "CSharp_DMGameMode_timeLimit_set",但这就是 "entrypoint" 论点的重点?
因此,由于此错误是由 Unity 生成的 Xcode 项目引发的,我不太确定失败的根源是什么。静态库会失败吗?这是要在 Unity 端还是 swig 端修复的问题?
更新:在深入研究之后,我想我对这里发生的事情有了一些了解..
主要问题似乎来自 AOT 编译器,它试图将所有 CS 代码编译为 ARM 程序集。这似乎是 iOS 所必需的,因此在 Unity 的 AOT 编译期间,它生成了一个文件 RegisterMonoModules.cpp
,该文件试图定义对本机代码的访问函数。 RegisterMonoModules.cpp
不遵守入口点参数,这会导致抛出未定义的符号错误...
仍在尝试找到合适的解决方法。
主要问题似乎来自 Unity,而不是 Swig 或 Mono。如上所述,Unity 执行不支持入口点参数的 AOT 编译。这会生成调用函数名称而不是入口点名称的 cpp 代码..
我已经通过将脚本后端切换到 IL2cpp 来确认这一点,并且入口点名称在那里得到了尊重。
让我们切换到回调。与问题不完全相关,但它绝对符合 Unity + Native plugins + iOS.
的上下文AFAIK,您无法使用 Mono 2x 将托管方法编组到 iOS 上的本土。我以前不得不从 swig 生成的文件中删除所有字符串回调和异常处理程序。幸运的是,IL2Cpp 支持回调,稍作调整后:
- 添加
using AOT;
- 使用
[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)