【问题标题】:Unity cannot build GRPC Project for UWP with IL2CPP BackendUnity 无法使用 IL2CPP 后端为 UWP 构建 GRPC 项目
【发布时间】:2018-11-26 20:31:52
【问题描述】:

Herehere 完整版本,您可以找到适用于 Unity 的示例 GRPC“Hello World”项目。只有为 Unity 构建并包装在 DLL 中的第一个版本在 Unity IDE 和 Standalone build 中完美运行fine。 Raw Grpc.Core 文件在 IDE 中正确引用了所有内容,但它们存在编组问题。

不幸的是,它无法为带有 IL2CPP 后端的 UWP 构建。 Unity 构建项目并创建一个 .sln 项目。但是 Visual Studio 总是在最终编译时为 GRPC 属性提供 LNK2001。

这里是第一个错误代码:

LNK2001 unresolved external _grpccsharp_init@0
LNK2001 unresolved external _grpccsharp_shutdonw@0
LNK2001 unresolved external _grpccsharp_version_string@0
...

好的,感谢@Sunius,我更深入地研究了它。有几点,我要补充一下问题:

在 GRPC C# 包中引用 extern 方法有两种方法。它们被命名为 staticshared 库。

internal class DllImportsFromStaticLib
{
    private const string ImportName = "__Internal";

    [DllImport(ImportName)]
    public static extern void grpcsharp_init();

    [DllImport(ImportName)]
    public static extern void grpcsharp_shutdown();

    ...
}

internal class DllImportsFromSharedLib
{
    private const string ImportName = "grpc_csharp_ext";

    [DllImport(ImportName)]
    public static extern void grpcsharp_init();

    [DllImport(ImportName)]
    public static extern void grpcsharp_shutdown();

    ...
}

我尝试使用共享文件对其进行测试,但我得到了另一个链接错误文件,它有点不同。

LNK2001 unresolved external _dlopen@8
LNK2001 unresolved external _dlsym@8
...

在两个单独的方法中,外部方法连接到内部接口:

public NativeMethods(DllImportsFromStaticLib unusedInstance) 
{
    this.grpccsharp_init = DllImportsFromStaticLib.grpccsharp_init; 
    this.grpccsharp_shutdown = DllImportsFromStaticLib.grpccsharp_shutdonw;
    ...
}

public NativeMethods(DllImportsFromSharedLib unusedInstance) 
{
    this.grpccsharp_init = DllImportsFromSharedLib.grpccsharp_init; 
    this.grpccsharp_shutdown = DllImportsFromSharedLib.grpccsharp_shutdonw;
    ...
}

这里定义了调用哪个方法:

private static NativMethods LoadNativeMethodsUnity()
{
    switch(PlatformApis.GetUnityRuntimePlatform())
    {
        case "IPhonePlayer":
            return new NativeMethods(new NativeMethods.DllImportsFromStaticLib());
        default:
            return new NativeMethods(new NativeMethods.DllImportsFromSharedLib());  
    }
}

一些更新:

感谢@jsmouret,his Grpc Github 中有 Stub.c 文件,其中包含虚假方法,因此 Linker 不再抱怨 Grpc_init 方法。

下一个错误:dlopen、dlsym、dlerror:

首先,我尝试使用相同的 Stub 技术,但在这种情况下它没有帮助,或者我做错了。

感谢@Sunius,我注释掉了所有“__Internal”dll 导入代码。所以我没有收到任何 dlopen、dlsym 和 dlerror 错误。

下一个错误:它发生在应用程序内部,而不是 Visual Studio 调试器。它告诉我:“异常:要编组托管方法,请在方法定义中添加一个名为 'MonoPInvokeCallback' 的属性。

exception: error loading the embedded resource "Grpc.Core.roots.pem"

exception: To marshal a managed method, please add an attribute named 'MonoPInvokeCallback' to the method definition.

在我用谷歌搜索之后,我知道了我的选择,但问题是,我应该使用哪种方法?!

【问题讨论】:

  • 考虑编辑这个问题和下面的答案,以用文本代替/替换错误消息和代码的屏幕截图。文本更易于阅读、可编入索引且更易于访问。
  • @chwarr 感谢您的评论。我会处理的。

标签: visual-studio unity3d uwp grpc il2cpp


【解决方案1】:

感谢我的同事 Alice、@Sunius 和 @jsmouret,最后,grpc 通过以下步骤在 Unity 平台上的 UWP 上工作:

  1. 从 Google Grpc Github 下载 Grpc.Core 文件夹。
  2. 从他们的官方site 下载 Grpc Unity 插件。
  3. 将运行时文件夹复制到您的 Grpc.Core 文件夹。请删除您从 Grpc Unity 插件获得的 Grpc.Core.dll,因为我们正在使用他们的源代码。
  4. Grpc 应该在 Unity 中名为 Plugins 的文件夹中,否则将无法识别。
  5. 将此file 包含在您的运行时文件夹中。
  6. 还包括来自 Unity Plugin Inspector for WSA 的存根。
  7. 找到适用于 Windows 的运行时 .dll 并将它们从 Unity Plugin Inspector 包含在 WSA 中。

现在,您应该会收到 _dlopen 错误。

  1. 使用 IDE 在 Unity 解决方案中搜索“__Internal”。没有那么多地方,但注释掉。还有一些依赖于“__Internal”的方法,例如 dlopen 和 dlsym。

到目前为止,您不会再遇到构建错误,但您需要让 Grpc 工作。

  1. 搜索“DefaultSslRootsOverride”之类的内容并如下注释掉:

    internal static class DefaultSslRootsOverride
    {
        const string RootsPemResourceName = "Grpc.Core.roots.pem";
        static object staticLock = new object();
    
        /// <summary>
        /// Overrides C core's default roots with roots.pem loaded as embedded resource.
        /// </summary>
        public static void Override(NativeMethods native)
        {
            lock (staticLock)
            {
                //var stream = typeof(DefaultSslRootsOverride).GetTypeInfo().Assembly.GetManifestResourceStream(RootsPemResourceName);
                //if (stream == null)
                //{
                //    throw new IOException(string.Format("Error loading the embedded resource \"{0}\"", RootsPemResourceName));   
                //}
                //using (var streamReader = new StreamReader(stream))
                //{
                //    var pemRootCerts = streamReader.ReadToEnd();
                //    native.grpcsharp_override_default_ssl_roots(pemRootCerts);
                //}
            }
        }
    }
    
  2. 搜索“static void HandWrite”之类的内容并添加如下所示的属性:

    [MonoPInvokeCallback(typeof(GprLogDelegate))]
    private static void HandleWrite(IntPtr fileStringPtr, int line, ulong threadId, IntPtr severityStringPtr, IntPtr msgPtr)
    {
        try
        {
            var logger = GrpcEnvironment.Logger;
            string severityString = Marshal.PtrToStringAnsi(severityStringPtr);
            string message = string.Format("{0} {1}:{2}: {3}",
                threadId,
                Marshal.PtrToStringAnsi(fileStringPtr), 
                line, 
                Marshal.PtrToStringAnsi(msgPtr));
    
            switch (severityString)
            {
                case "D":
                    logger.Debug(message);
                    break;
                case "I":
                    logger.Info(message);
                    break;
                case "E":
                    logger.Error(message);
                    break;
                default:
                    // severity not recognized, default to error.
                    logger.Error(message);
                    break;
            }
        }
        catch (Exception e)
        {
            Console.WriteLine("Caught exception in native callback " + e);
        }
    }
    

我想,你已经完成了。如果它不适用于您的 UWP,请告诉我,也许我可以提供帮助。 :)

【讨论】:

  • 感谢您抽出宝贵时间发布此兄弟
【解决方案2】:

看起来您的插件使用“__Internal”P/Invoke 来调用这些原生函数:

https://github.com/grpc/grpc/blob/befc7220cadb963755de86763a04ab6f9dc14200/src/csharp/Grpc.Core/Internal/NativeMethods.Generated.cs#L542

但是,链接器无法找到这些函数,因此会失败。您应该更改该代码以指定实现函数的 DLL 文件名,或者将包含这些函数的定义的源文件拖放到 Unity 项目中。或者,如果实际上没有调用该代码路径(因为您说它适用于独立播放器),则 #ifdef 它从 UWP 构建中取出。

您可以在此处找到有关“__Internal”P/Invoke 的更多信息:

https://docs.unity3d.com/Manual/windowsstore-plugins-il2cpp.html

【讨论】:

  • 1.指定 DLL 而不是“__Internal”听起来并不容易:GRPC 团队没有我可以添加到 Unity 项目并引用它的 DLL。
  • 2.如果您能给我一个指向 GRPC 源文件的链接,那就太好了,这样我就可以将它放到 Unity 中并手动为这些外部方法提供参考。不幸的是,我在他们的 GitHub 页面上找不到类似本地方法定义的东西。
  • 3.调用路径并且“__Internal”在独立构建中按预期工作。问题是 IL2CPP 中的 C++ 机器转换在代码编译时无法在其引用中包含 GRPC 核心。
  • 我敢打赌,“__Internal”代码实际上不适用于独立播放器,否则它也适用于 UWP。我建议在独立播放器上单步执行该代码,看看它实际上是如何调用这些方法的。
  • 我会深入研究这一点,再深入一点,然后回复你。再次感谢。
猜你喜欢
  • 1970-01-01
  • 2021-04-18
  • 2020-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-31
  • 2021-06-04
  • 1970-01-01
相关资源
最近更新 更多