grpc: Unable to compile with .NET Native tool chain (UWP project)

What version of gRPC and what language are you using?

gRPC 1.19.0, C#

What operating system (Linux, Windows,…) and version?

Windows 10 1803 (17134.590)

What runtime / compiler are you using (e.g. python version or version of gcc)

Visual Studio Professional 2017 (15.9.6)

What did you do?

  1. Created a new UWP project that references a new .NET Standard 2.0 library.
  2. Add the Grpc.Auth (1.19.0) nuget to the .NET Standard library.
  3. Build with .NET Native tool chain enabled.

What did you expect to see?

A successful build

What did you see instead?

1> C:\Program Files (x86)\Microsoft SDKs\UWPNuGetPackages\microsoft.net.native.compiler\2.2.1\tools\Microsoft.NetNative.targets(792,5): error : ILT0014: Failed to compile interop source code. See the build log for error details.

More specifically:

1>  Compiling interop code
1>  Compiling generated source code: C:\Program Files (x86)\Microsoft SDKs\UWPNuGetPackages\microsoft.net.native.compiler\2.2.1\tools\csc\csc.exe /noconfig @"<path>\ReproGrpc\ReproGrpc\obj\x86\Debug\ilc\intermediate\ReproGrpc.McgInterop.rsp"
1>  obj\x86\Debug\ilc\intermediate\ReproGrpc.McgInterop\ImplTypes.g.cs(630,14): error CS1593: Delegate 'NativeMethods_Delegates_grpcsharp_batch_context_create_delegate__Grpc_Core' does not take 1 arguments
1>  obj\x86\Debug\ilc\intermediate\ReproGrpc.McgInterop\ImplTypes.g.cs(1421,14): error CS1593: Delegate 'NativeMethods_Delegates_grpcsharp_request_call_context_create_delegate__Grpc_Core' does not take 1 arguments
1>  obj\x86\Debug\ilc\intermediate\ReproGrpc.McgInterop\ImplTypes.g.cs(1502,14): error CS1593: Delegate 'NativeMethods_Delegates_grpcsharp_request_call_context_call_delegate__Grpc_Core' does not take 2 arguments
1>  obj\x86\Debug\ilc\intermediate\ReproGrpc.McgInterop\ImplTypes.g.cs(2069,14): error CS1593: Delegate 'NativeMethods_Delegates_grpcsharp_composite_call_credentials_create_delegate__Grpc_Core' does not take 3 arguments
1>  obj\x86\Debug\ilc\intermediate\ReproGrpc.McgInterop\ImplTypes.g.cs(3950,14): error CS1593: Delegate 'NativeMethods_Delegates_grpcsharp_call_get_peer_delegate__Grpc_Core' does not take 2 arguments
1>  obj\x86\Debug\ilc\intermediate\ReproGrpc.McgInterop\ImplTypes.g.cs(4088,14): error CS1593: Delegate 'NativeMethods_Delegates_grpcsharp_channel_args_create_delegate__Grpc_Core' does not take 2 arguments
1>  obj\x86\Debug\ilc\intermediate\ReproGrpc.McgInterop\ImplTypes.g.cs(4548,14): error CS1593: Delegate 'NativeMethods_Delegates_grpcsharp_ssl_credentials_create_delegate__Grpc_Core' does not take 4 arguments
1>  obj\x86\Debug\ilc\intermediate\ReproGrpc.McgInterop\ImplTypes.g.cs(4658,14): error CS1593: Delegate 'NativeMethods_Delegates_grpcsharp_composite_channel_credentials_create_delegate__Grpc_Core' does not take 3 arguments
1>  obj\x86\Debug\ilc\intermediate\ReproGrpc.McgInterop\ImplTypes.g.cs(4827,14): error CS1593: Delegate 'NativeMethods_Delegates_grpcsharp_insecure_channel_create_delegate__Grpc_Core' does not take 3 arguments
1>  obj\x86\Debug\ilc\intermediate\ReproGrpc.McgInterop\ImplTypes.g.cs(4948,14): error CS1593: Delegate 'NativeMethods_Delegates_grpcsharp_secure_channel_create_delegate__Grpc_Core' does not take 4 arguments
1>  obj\x86\Debug\ilc\intermediate\ReproGrpc.McgInterop\ImplTypes.g.cs(5099,14): error CS1593: Delegate 'NativeMethods_Delegates_grpcsharp_channel_create_call_delegate__Grpc_Core' does not take 8 arguments
1>  obj\x86\Debug\ilc\intermediate\ReproGrpc.McgInterop\ImplTypes.g.cs(5422,14): error CS1593: Delegate 'NativeMethods_Delegates_grpcsharp_channel_get_target_delegate__Grpc_Core' does not take 2 arguments
1>  obj\x86\Debug\ilc\intermediate\ReproGrpc.McgInterop\ImplTypes.g.cs(5617,14): error CS1593: Delegate 'NativeMethods_Delegates_grpcsharp_completion_queue_create_async_delegate__Grpc_Core' does not take 1 arguments
1>  obj\x86\Debug\ilc\intermediate\ReproGrpc.McgInterop\ImplTypes.g.cs(5688,14): error CS1593: Delegate 'NativeMethods_Delegates_grpcsharp_completion_queue_create_sync_delegate__Grpc_Core' does not take 1 arguments
1>  obj\x86\Debug\ilc\intermediate\ReproGrpc.McgInterop\ImplTypes.g.cs(6109,14): error CS1593: Delegate 'NativeMethods_Delegates_grpcsharp_metadata_array_create_delegate__Grpc_Core' does not take 2 arguments
1>  obj\x86\Debug\ilc\intermediate\ReproGrpc.McgInterop\ImplTypes.g.cs(6732,14): error CS1593: Delegate 'NativeMethods_Delegates_grpcsharp_metadata_credentials_create_from_plugin_delegate__Grpc_Core' does not take 2 arguments
1>  obj\x86\Debug\ilc\intermediate\ReproGrpc.McgInterop\ImplTypes.g.cs(7023,14): error CS1593: Delegate 'NativeMethods_Delegates_grpcsharp_ssl_server_credentials_create_delegate__Grpc_Core' does not take 6 arguments
1>  obj\x86\Debug\ilc\intermediate\ReproGrpc.McgInterop\ImplTypes.g.cs(7206,14): error CS1593: Delegate 'NativeMethods_Delegates_grpcsharp_server_create_delegate__Grpc_Core' does not take 2 arguments
1>  obj\x86\Debug\ilc\intermediate\ReproGrpc.McgInterop\ImplTypes.g.cs(8020,14): error CS1593: Delegate 'NativeMethods_Delegates_grpcsharp_call_auth_context_delegate__Grpc_Core' does not take 2 arguments
1>C:\Program Files (x86)\Microsoft SDKs\UWPNuGetPackages\microsoft.net.native.compiler\2.2.1\tools\Microsoft.NetNative.targets(792,5): error : ILT0014: Failed to compile interop source code. See the build log for error details.
1>
1>Build FAILED.

Anything else we should know about your project / environment?

I actually get a different error in ImplTypes.g.cs with my actual project (I think because we use version 2.0.3 of the .NET Native compiler, but the repro project is using 2.2.1)

The errors were:

2>  <path>\ilc\intermediate\Snap.WindowsUWP.Full.McgInterop\ImplTypes.g.cs(12976,9): error CS0165: Use of unassigned local variable 'metadataArray'
2>  <path>\ilc\intermediate\Snap.WindowsUWP.Full.McgInterop\ImplTypes.g.cs(13006,9): error CS0165: Use of unassigned local variable 'metadataArray'
2>  <path>\ilc\intermediate\Snap.WindowsUWP.Full.McgInterop\ImplTypes.g.cs(10955,9): error CS0165: Use of unassigned local variable 'channelArgs'
2>  <path>\ilc\intermediate\Snap.WindowsUWP.Full.McgInterop\ImplTypes.g.cs(10984,9): error CS0165: Use of unassigned local variable 'channelArgs'

Let me know if this should instead be reported against the ilc. Build log and repro project attached.

ReproGrpc.zip

repro_buildlog.txt

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 6
  • Comments: 68 (7 by maintainers)

Commits related to this issue

Most upvoted comments

It’s insane to me that Microsoft has locked the above thread.

Will someone fix this issue. How in the world can Microsoft say that GRPC is an alternative to WCF if it cannot be submitted to the Windows Store in a UWP application.

Everyone knows Microsoft is to blame here. Their quality control of their products is getting worse every year.

@tommcdon What is the status here? We lost ya bud.

You know what, I’m going to try this again, months later, with a blank slate and see if fixes posted by @Noemata work.

…the ILT0014 error when compiling gRPC is a .Net Native compiler bug…

Thank you for your feedback. The issue is now fixed and will be shipping in a future UWP update. We are deeply appreciative for your commitment to help us build better products, and we hope this fix improves your experience with our tools and technologies. UWP release notes are published here: https://github.com/microsoft/dotnet/blob/master/releases/UWP/net-native2.2/README.md, and we will include this issue in the notes when we release the update.

@andygikling @Noemata we are working with the store team to finalize our release date.

Any updates on this?

Note: This is 1 full year after this was first reported.

I finally had some time to dig into the details of this. The problem is still the .Net Native compiler. Here’s just one place where the managed C# layer of gRPC gets corrupted by the .Net Native compiler.

This bit of code in the Grpc.Core module of NativeExtension.cs:

        private static string GetAssemblyPath()
        {
            var assembly = typeof(NativeExtension).GetTypeInfo().Assembly;
#if NETSTANDARD1_5 || NETSTANDARD2_0
            // Assembly.EscapedCodeBase does not exist under CoreCLR, but assemblies imported from a nuget package
            // don't seem to be shadowed by DNX-based projects at all.
            return assembly.Location;
#else
            // If assembly is shadowed (e.g. in a webapp), EscapedCodeBase is pointing
            // to the original location of the assembly, and Location is pointing
            // to the shadow copy. We care about the original location because
            // the native dlls don't get shadowed.

            var escapedCodeBase = assembly.EscapedCodeBase;
            if (IsFileUri(escapedCodeBase))
            {
                return new Uri(escapedCodeBase).LocalPath;
            }
            return assembly.Location;
#endif
        }

Crashes at this line:

var assembly = typeof(NativeExtension).GetTypeInfo().Assembly;

Because the namespace “NativeExtension” does not get resolved correctly for a Release build. This crash is happening before gRPC fires up as it is still trying to load the C++ native DLLS at this stage and won’t be able to build the path for where the native assemblies are located.

As for ARM builds of gRPC, the problems are worse, so it’s going to be impossible to get certain categories of UWP apps built for Windows ARM devices, like the HoloLens 2. Not sure how that will work out for the DOD? Or the Surface X??

What’s frustrating about this is that it took less than a couple hours to get to a working build of gRPC once I got past the reticence of building all the C++ bits. So one has to wonder what resources, if any, Microsoft is putting toward these sorts of issues?

It’s disappointing that Microsoft keeps pointing the finger elsewhere (@MichalStrehovsky), when the problem is with Microsoft tooling. We are months and months into this, when it really could have been resolved in a few hours of serious digging. Handing off the gRPC layer to Google is yet another curiosity? Even with .Net Native compiler issues, Microsoft could easily correct gRPC managed layer design choices that could break the .Net Native compiler.

And now, rather than doubling down on fixing core issues like this, Microsoft is onto yet another course correction with WinUI and a new AOT compiler implementation at the expense of making UWP look stupid. Please, just finish one framework all the way. Forget xplatform, or .Net 5, or rejuvenating WPF. Fix Windows 10 UWP first or have the decency to tell us we’re stupid to think this might happen.

Lastly, if it builds and works in Debug, Release should be better, given you can only push a Release build into the Microsoft store. Always make Microsoft staff build for Release and this problem will go away.

@aosyatnik That solution works great if you can control your deployments and environment (sounds like it’s working well for you!). However, packages shipped and managed through the Microsoft Store currently have “.NET Native” as a shipping requirement.

I was able to fix the remaining issues with these additional changes:

  1. I removed wsock32 from templates/CMakeLists.txt.template and from CMakeLists.txt:
  if(WIN32 AND MSVC)
    set(_gRPC_BASELIB_LIBRARIES ws2_32)
  endif()

I noticed also the component third_party/cares is referencing wsock32 but changing it was not required.

  1. Store rejects the app if a x86 package contains x64 binaries. Nuget package should be configured so that only the required binaries are included (currently both grpc_csharp_ext.x86.dll and grpc_csharp_ext.x64.dll are included always). To fix this for my app I restructured nuget folders are follows:
runtimes/win10-x64/native/grpc_csharp_ext.x64.dll
runtimes/win10-x86/native/grpc_csharp_ext.x86.dll

More info about the available ids here: https://docs.microsoft.com/en-us/dotnet/core/rid-catalog

  1. WACK tests fail to Binary analyzer errors:
File grpc_csharp_ext.x64.dll has failed the AppContainerCheck check.
File grpc_csharp_ext.x86.dll has failed the AppContainerCheck check.
- Impact if not fixed: If the app doesn’t use the available Windows protections, it can increase the vulnerability of the customer's computer to malware.
- How to fix: Apply the required linker options - SAFESEH, DYNAMICBASE, NXCOMPAT, and APPCONTAINER - when you link the app. See links below for more information:

I guess proper fix is to modify cmake scripts to include the required linker options, but for simplicity I used the following command line (execute in Developer Command Prompt for VS2017):

editbin /dynamicbase /nxcompat /appcontainer grpc_csharp_ext.x64.dll
editbin /dynamicbase /nxcompat /appcontainer grpc_csharp_ext.x86.dll

With all these changes my app finally passed Store certification and it is now available in the Store. And what is best it seems to work too.

I hope these findings help implementing the final official fixes. Thanks to Michal from .NET Native team for helpful comments.

@andygikling @Noemata Just a quick update - the release mechanics are in motion and we are working with the store team to finialize our release date.

@noypi I suggest using the latest version of Visual Studio 2017 or 2019. The Community edition of Visual Studio is supported with UWP. Please see https://docs.microsoft.com/en-us/nuget/quickstart/install-and-use-a-package-in-visual-studio for instructions on how to upgrade/install nuget packages. Once the next version of UWP 6.2 releases, you will see a blue arrow next to “Microsoft.NetCore.UniversalWindowsPlatform” in the nuget package manager for your solution:

image

To upgrade, click the “Update” button to download and upgrade the project to the new version of UWP. Note that if you have more than one UWP project in your solution, I suggest upgrading all of the projects by right clicking on the solution and choosing “Manage NuGet packages for Solution”:

image

@tylersouthard, the ILT0014 error when compiling gRPC is a .Net Native compiler bug. Given Microsoft is now recommending gRPC as a replacement for WCF (this was stated a few times at Build 2019), one would think this issue should be a high priority item. Unfortunately, the very limited stage time given to UWP during build 2019 suggests there are now other priorities within Microsoft. Hopefully Microsoft realizes this time around that the development community is expecting another Silverlight scenario, consequently the current optics for UWP will be dramatically improved. Microsoft exec Richard Lander said: “After .NET Core 3.0 we will not port any more features from .NET Framework. If you are a Web Forms developer and want to build a new application on .NET Core, we would recommend Blazor which provides the closest programming model. If you are a remoting or WCF developer and want to build a new application on .NET Core, we would recommend either ASP.NET Core Web APIs or gRPC (Google RPC, which provides cross platform and cross programming language contract based RPCs).”

Some of us were sold on UWP, and promptly let go of WPF. Going back to WPF is a non-starter for us! Please get the messaging right this time around. Silverlight still casts a long and dark shadow.

Still broken Stale Bot.

@kinex has the right idea to fix this, but I think there needs to be a separate UWP track for loading the native libs. UWP should not attempt to load like .NET desktop does, and instead rely on the DLL existing with the given DllImport name. That is, grcp_csharp_ext.x86.dll gets copied as grpc_csharp_ext.dll for x86 builds, and the x64 variant gets copied as the same name for x64 builds. UWP has to specify an architecture anyway so it will not be choosing which to load at runtime.

Here is the targets file I made as a reference for the UWP native library loading in my company’s project. It has served me well for over a year of production now.

EDIT This removes the need for doing any unmanaged library loading at runtime at all. All UWP needs is simply new NativeMethods(new NativeMethods.DllImportsFromSharedLib());

I’m running into this issue now… Does anyone know when the fix will be released? I love UWP, but it need for a greater improvements-alignment-stability from Microsoft!

Path handling changes to correct the issue are outlined above. It’s working for me with those changes in place.

Here’s what the Greeter sample looks like as a UWP console app:

        static void Main(string[] args)
        {
            GrpcEnvironment.AssemblyLocation = AppContext.BaseDirectory;

            if (!System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable())
            {
                Console.WriteLine("No network connection available.");
                return;
            }

            GrpcEnvironment.SetLogger(new Grpc.Core.Logging.ConsoleLogger());

            // standard gRPC channel
            string localIpAddress = GetLocalIPv4(NetworkInterfaceType.Ethernet);
            //string localIpAddress = GetLocalIPv4(NetworkInterfaceType.Wireless80211);

            Console.WriteLine($"IP Address -- {localIpAddress}");

            var channel = new Channel(localIpAddress, 5000, ChannelCredentials.Insecure);

            var client = new Greeter.GreeterClient(channel);
            String user = "you";

            var reply = client.SayHello(new HelloRequest { Name = user });
            Console.WriteLine("Greeting: " + reply.Message);

            channel.ShutdownAsync().Wait();
            Console.WriteLine("Cancelling...");
            Console.ReadLine();
        }

Almost the same as the .Net Core version.

Here’s a complete breaking code sample as a UWP console app built for Release:

using GetTypeofFailureInReleaseBuild;
using System;
using System.IO;
using System.Reflection;

// This example code shows how you could implement the required main function for a 
// Console UWP Application. You can replace all the code inside Main with your own custom code.

// You should also change the Alias value in the AppExecutionAlias Extension in the 
// Package.appxmanifest to a value that you define. To edit this file manually, right-click
// it in Solution Explorer and select View Code, or open it with the XML Editor.

namespace GetTypeofFailureInReleaseBuild
{
    public static class GetThisAssembly
    {
        public static readonly string BadPath;

        // Next variable is here to assist with debugging in a Release build.
        public static readonly string n1;

        static GetThisAssembly()
        {
            Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();

            if (assemblies != null)
            {
                var a1 = assemblies[0];

                if (a1 != null)
                {
                    n1 = a1.FullName;
                }
            }


            var thistype = typeof(GetThisAssembly);

            // Why is this null in Release build?  Though the VS IDE says it's null, it's actually not?
            if (thistype != null)
            {
                var assembly = thistype.GetTypeInfo().Assembly;
                BadPath = assembly.Location;
                // Should crash here, like in gRPC, but the BadPath string is empty?
                var buildpath = Path.Combine(BadPath, "\\root");
            }
        }
    }
}

namespace BadPath
{
    class Program
    {
        static void Main(string[] args)
        {
            var pathlike_gRPC = GetThisAssembly.BadPath;

            var n2 = pathlike_gRPC;

            if (n2 == null)
            {
                Console.WriteLine($"Null path.");
            }
            else if (n2 == string.Empty)
            {
                Console.WriteLine($"Empty path.");
            }

            Console.WriteLine($"-------- Bad Path {pathlike_gRPC}");

            Console.ReadLine();
        }
    }
}

Notice in the Autos window the VS IDE shows thistype as being “null”:

image

That said, you are right @MichalStrehovsky, in this case BadPath acquires an empty string, so the Path.Combin() call succeeds. Not that this is correct. I disagree with the premise that this is as it should be. I suppose the IDE is wrong in showing thistype as null at the breakpoint above.

Yet, in the grpc code you get:

image

I can’t explain the difference, since “Location” in one case returns an empty string, and null in the other. Frankly I’m running out of steam on this back and forth. In Microsoft land everything is as it should be I suppose. Out here in the real world our apps are crashing because of the way the .Net Native compiler generates code. We’re a year out from when this was first reported and now I’m patching Microsoft endorsed tech choices to work around problems. The good news, Microsoft is going Open Source, so at least we can help ourselves.

@borrrden, I needed to make changes to the gRPC managed layer:

https://github.com/grpc/grpc/tree/master/src/csharp

It’s working for my purposes, but I only qualified the code paths that I’m using. There may be things I’ve missed in my set of changes.

Here’s just one example of the sorts of changes I made:

In NativeExtensions.cs

...
    internal sealed class NativeExtension
    {
        static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<NativeExtension>();
        static readonly object staticLock = new object();
        static volatile NativeExtension instance;

        readonly NativeMethods nativeMethods;

        // MP! Added:
        /// <summary>
        /// Allow for alternate specification of native assembly file location.
        /// </summary>
        public static string AssemblyLocation = null;
        private static UnmanagedLibrary LoadUnmanagedLibrary()
        {
            // TODO: allow customizing path to native extension (possibly through exposing a GrpcEnvironment property).
            // See https://github.com/grpc/grpc/pull/7303 for one option.

            // MP! Added support for directly specifying assembly location.
            string assemblyDirectory = AssemblyLocation;

            if (assemblyDirectory == null)
                assemblyDirectory = Path.GetDirectoryName(GetAssemblyPath());

In PlatformApis.cs

        public static void GetPlatformPaths(string platformpath = null)
        {
            string classicPath = null;
            string assemblyDirectory = platformpath;

            // MP! Added: support for specifying alternate path.
            if (assemblyDirectory == null)
                assemblyDirectory = Path.GetDirectoryName(GetAssemblyPath());

            if (n1 != null && n2 != null)
                classicPath = Path.Combine(assemblyDirectory, GetNativeLibraryFilename());

            string runtimesDirectory = string.Format("runtimes/{0}/native", GetPlatformString());
            var netCorePublishedAppStylePath = Path.Combine(assemblyDirectory, runtimesDirectory, GetNativeLibraryFilename());
            var netCoreAppStylePath = Path.Combine(assemblyDirectory, "../..", runtimesDirectory, GetNativeLibraryFilename());
            string[] paths = new[] { classicPath, netCorePublishedAppStylePath, netCoreAppStylePath };
        }

And so forth. The changes above address just the path problem issue, but get you started toward fixing the other issues that might affect you. You’ll problably need to do a couple hours of slogging correcting crashes as you hit them in Release to get to a working state for the way you are using gRPC.

The biggest challenge will be coping with the way Release build traverses the code. I had to bounce back and forth between Release and Debug to figure out what was supposed to happen where crashes were happening.

Hi, I guess we had some similar problems. That’s our solution structure: project structure

UWP that uses .Net Standard 2.0. (which has GRPC).

Everything was good unless we started building package for appcenter.ms. We wanted to send an install package to the customer and it was crashing somewhere in GRPC code.

So the solution, that we’ve found was to disable .Net toolchain in the UWP project for release configuration. project structure

DISCLAIMER: I have no idea if it’s a good solution, but it worked for us. Maybe this comment will help someone.

6.2.9 is now available on Nuget. Certainly let us know how it goes.

Release notes: https://github.com/microsoft/dotnet/tree/master/releases/UWP/net-native2.2

We’ve been waiting for this fix for awhile. Please provide some ETA information.

@daltonks I work around this issue by using a full trust component, and only referencing the gRPC nuget from that side. See https://docs.microsoft.com/en-us/uwp/api/windows.applicationmodel.fulltrustprocesslauncher and https://stefanwick.com/2018/04/06/uwp-with-desktop-extension-part-1/.

Obviously, in order to use that capability, you’d have to get special dispensation from Microsoft before releasing on the store.

I have a Microsoft Premier Support ticket active with them. The latest response is that they have an issue filed with the project team. I’m not holding my breath.