Unity 无法使用 IL2CPP 后端为 UWP 构建 GRPC 项目
Unity cannot build GRPC Project for UWP with IL2CPP Backend
Here, or here 对于完整版本,您可以找到适用于 Unity 的示例 GRPC "Hello World" 项目。只有为 Unity 构建并包装在 DLL 中的第一个版本在 Unity IDE 和 独立构建 中完美运行 fine .原始 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方法的方法。它们被命名为 static 和 shared libs.
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 文件带有假方法,因此链接器不再抱怨 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.
在我用谷歌搜索之后,我知道了我的选择,但问题是,我应该用哪种方法呢?!
看起来您的插件使用“__Internal”P/Invoke 来调用这些原生函数:
但是,链接器无法定位这些函数,因此失败。您应该更改该代码以指定实现函数的 DLL 文件名,或者将包含这些函数定义的源文件放入您的 Unity 项目中。或者,如果实际上没有调用该代码路径(因为你说它适用于独立播放器),#ifdef 从 UWP build 中取出它。
您可以在此处找到有关“__Internal”P/Invoke 的更多信息:
https://docs.unity3d.com/Manual/windowsstore-plugins-il2cpp.html
感谢我的同事Alice,@Sunius和@jsmouret,最后,grpc通过以下步骤在Unity Platform上实现UWP:
- 从 Google Grpc Github 下载 Grpc.Core 文件夹。
- 从官方下载 Grpc Unity 插件 site。
- 将运行时文件夹复制到您的 Grpc.Core 文件夹。请删除您从 Grpc Unity 插件获得的 Grpc.Core.dll,因为我们使用的是他们的源代码。
- grpc应该在Unity的Plugins文件夹下,否则无法识别。
- 将此 file 包含在您的运行时文件夹中。
- 也包括来自 WSA 的 Unity 插件检查器的存根。
- 找到 Windows 的运行时 .dll,并从 Unity Plugin Inspector 将它们包含在 WSA 中。
现在,您应该会收到 _dlopen 错误。
- 在您的 Unity 解决方案中搜索 IDE“__Internal”。没有那么多地方,还是注释掉吧。还有一些依赖于“__Internal”的方法,比如 dlopen 和 dlsym。
到目前为止,您不会再遇到构建错误,但您需要让 Grpc 正常工作。
搜索类似 "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);
//}
}
}
}
搜索类似 "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,请告诉我,也许我可以提供帮助。 :)
Here, or here 对于完整版本,您可以找到适用于 Unity 的示例 GRPC "Hello World" 项目。只有为 Unity 构建并包装在 DLL 中的第一个版本在 Unity IDE 和 独立构建 中完美运行 fine .原始 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方法的方法。它们被命名为 static 和 shared libs.
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 文件带有假方法,因此链接器不再抱怨 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.
在我用谷歌搜索之后,我知道了我的选择,但问题是,我应该用哪种方法呢?!
看起来您的插件使用“__Internal”P/Invoke 来调用这些原生函数:
但是,链接器无法定位这些函数,因此失败。您应该更改该代码以指定实现函数的 DLL 文件名,或者将包含这些函数定义的源文件放入您的 Unity 项目中。或者,如果实际上没有调用该代码路径(因为你说它适用于独立播放器),#ifdef 从 UWP build 中取出它。
您可以在此处找到有关“__Internal”P/Invoke 的更多信息:
https://docs.unity3d.com/Manual/windowsstore-plugins-il2cpp.html
感谢我的同事Alice,@Sunius和@jsmouret,最后,grpc通过以下步骤在Unity Platform上实现UWP:
- 从 Google Grpc Github 下载 Grpc.Core 文件夹。
- 从官方下载 Grpc Unity 插件 site。
- 将运行时文件夹复制到您的 Grpc.Core 文件夹。请删除您从 Grpc Unity 插件获得的 Grpc.Core.dll,因为我们使用的是他们的源代码。
- grpc应该在Unity的Plugins文件夹下,否则无法识别。
- 将此 file 包含在您的运行时文件夹中。
- 也包括来自 WSA 的 Unity 插件检查器的存根。
- 找到 Windows 的运行时 .dll,并从 Unity Plugin Inspector 将它们包含在 WSA 中。
现在,您应该会收到 _dlopen 错误。
- 在您的 Unity 解决方案中搜索 IDE“__Internal”。没有那么多地方,还是注释掉吧。还有一些依赖于“__Internal”的方法,比如 dlopen 和 dlsym。
到目前为止,您不会再遇到构建错误,但您需要让 Grpc 正常工作。
搜索类似 "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); //} } } }
搜索类似 "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,请告诉我,也许我可以提供帮助。 :)