首页 > 解决方案 > Unity 无法使用 IL2CPP 后端为 UWP 构建 GRPC 项目

问题描述

在这里,或在这里查看完整版本,您可以找到适用于 Unity 的示例 GRPC“Hello World”项目。只有为 Unity 构建并包装在 DLL 中的第一个版本在 Unity IDE 和Standalone build中运行良好。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# 包中引用外部方法有两种方法。它们被命名为静态共享库。

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,他的 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.

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

标签: visual-studiounity3duwpgrpcil2cpp

解决方案


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

  1. 从 Google Grpc Github下载Grpc.Core文件夹。
  2. 从他们的官方网站下载 Grpc Unity 插件。
  3. 将运行时文件夹复制到您的 Grpc.Core 文件夹。请删除您从 Grpc Unity 插件获得的 Grpc.Core.dll,因为我们正在使用他们的源代码。
  4. Grpc 应该在 Unity 中名为 Plugins 的文件夹中,否则将无法识别。
  5. 将此文件包含在您的运行时文件夹中。
  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,请告诉我,也许我可以提供帮助。:)


推荐阅读