菜鸟教程小白 发表于 2022-12-12 20:13:25

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


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

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

<hr/>

<pre><code>// This is in KodLogicPInvoke.cs

</code></pre>

<p>这在我构建动态库时非常有用。但是,我现在需要支持 iOS,所以我准备了一个静态库,并传入了 <code>-dllimport '__Internal'</code> 选项以使其工作。</p>

<p>很遗憾,我遇到了链接错误,例如:</p>

<pre><code>&#34;_DMGameMode_timeLimit_set&#34;, referenced from:
RegisterMonoModules() in RegisterMonoModules.o
(maybe you meant: _CSharp_DMGameMode_timeLimit_set)
</code></pre>

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

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

<p><strong>更新:</strong>在深入研究之后,我想我对这里发生的事情有了一点了解..</p>

<p>主要问题似乎来自 AOT 编译器,它试图将所有 CS 代码编译为 ARM 程序集。这似乎是 iOS 所必需的,因此在 Unity 的 AOT 编译期间,它会生成一个文件 <code>RegisterMonoModules.cpp</code>,该文件尝试定义对 native 代码的访问功能。 <code>RegisterMonoModules.cpp</code> 不遵守入口点参数,这会导致抛出 undefined symbol 错误...</p>

<p>仍在尝试寻找合适的解决方法。</p></p>
                                    <br><hr><h1><strong>Best Answer-推荐答案</ strong></h1><br>
                                            <p><p>主要问题似乎来自 Unity,而不是 Swig 或 Mono。如上所述,Unity 执行不支持入口点参数的 AOT 编译。这会生成调用函数名称的 cpp 代码,而不是入口点名称。</p>

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

<hr/>

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

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

<ol>
<li>添加<code>使用AOT;</code></li>
<li>使用 <code></code></li> 装饰回调
</ol>

<p>你可以使用这个脚本,只是用它来处理生成的swig文件:</p>

<pre><code>def process_csharp_callbacks(pinvoke_file):
&#34;&#34;&#34;Process PInvoke file by fixing the decorators for callback methods to use:

&#34;&#34;&#34;
# prepare requirements
with open(pinvoke_file) as f:
    content = f.read()

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

    match_string = match.group()
    indentation = match.captures(1)

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

    return indentation + fix + &#34;\n&#34; + match_string

content = callback_methods_regex.sub(method_processor, content)
# write it back
with open(pinvoke_file, &#34;w+&#34;) as f:
    f.write(content)
</code></pre>

<hr/>

<p>对于任何寻求帮助的人,将他们生成的 swig CSharp PInvoke 文件转换为单声道 2x 脚本后端允许的东西,请在生成 CSharp 文件后将其粘贴到构建过程中的某个位置:</p>

<pre><code>pinvoke_template = &#34;&#34;&#34;{extern_prefix} CSharp_{method_signature};
{normal_prefix} {method_signature} {{
    {return_statement}CSharp_{method_name}({method_args});
}}&#34;&#34;&#34;

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

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

content = method_regex.sub(method_processor, content)
# write it back
with open(pinvoke_file, &#34;w+&#34;) as f:
    f.write(content)
</code></pre></p>
                                   
                                                <p style="font-size: 20px;">关于ios - 对静态库的非托管 C# 调用,我们在Stack Overflow上找到一个类似的问题:
                                                        <a href="https://stackoverflow.com/questions/37448882/" rel="noreferrer noopener nofollow" style="color: red;">
                                                                https://stackoverflow.com/questions/37448882/
                                                        </a>
                                                </p>
                                       
页: [1]
查看完整版本: ios - 对静态库的非托管 C# 调用