Unity无法使用IL2CPP后端为UWP构建GRPC项目

时间:2018-11-26 20:31:52

标签: visual-studio unity3d uwp grpc il2cpp

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

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

以下是第一个错误代码:

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

好吧,感谢@Sunius,我对其进行了更多研究。有几点,我要补充一点:

在GRPC C#程序包中,有两种有关引用 extern 方法的方法。它们被命名为静态共享库。

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,我注释掉了所有“ __内部” 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.

在我搜索完它之后,我知道了我的选择,但问题是,我应该针对哪种方法进行操作?!

2 个答案:

答案 0 :(得分:2)

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

  1. 从Google Grpc Github下载Grpc.Core文件夹。
  2. 从其官方site下载Grpc Unity插件。
  3. 将运行时文件夹复制到您的Grpc.Core文件夹。请删除从Grpc Unity插件获得的Grpc.Core.dll,因为我们正在使用它们的源代码。
  4. Grpc应该位于一个名为Unity中的插件的文件夹中,否则它将无法被识别。
  5. 将此file包含在您的运行时文件夹中。
  6. 还包括用于WSA的Unity插件检查器中的存根。
  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,请告诉我,也许我可以帮忙。 :)

答案 1 :(得分:1)

您的插件似乎使用“ __Internal” P / Invoke来调用这些本机函数:

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

但是,链接器无法找到这些功能,因此会失败。您应该更改该代码以指定实现功能的DLL文件名,或者将带有这些功能定义的源文件拖放到Unity项目中。或者,如果实际上未调用该代码路径(因为您说过它可在独立播放器上使用),请从UWP构建中#ifdef。

您可以在此处找到有关“ __内部” P /调用的更多信息:

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