.netstaticaot

Can a static library be linked to a .NET AOT compiled executable?


As seen e.g. here, we can export a function from .NET to be statically linked to another executable later.

using System;
using System.Runtime.InteropServices;

public static class NativeExports
{
    [UnmanagedCallersOnly(EntryPoint = "multiply")]
    public static int Multiply(int a, int b)
    {
        return a * b;
    }
}

Is the other direction also possible?

Something like this (pseudo code), is what I'm looking for:

using System;
using System.Runtime.InteropServices;

public static class NativeImports
{
    [UnmanagedImpl(EntryPoint = "multiply")]
    public static extern int Multiply(int a, int b);
}

Later on linked with this statically:

// multiply.c
int multiply(int a, int b)
{
    return a*b;
}

The overall goal is to have a single, statically linked, dependency-free executable mainly written in C#.

I know about P/Invoke et. al. — that's my current workaround.


Solution

  • Answering my own question after getting some help (https://github.com/dotnet/runtime/issues/89044):

    Minimal example for direct P/Invoke calls with static linking

    This is the function we would like to statically link against our executable written in C#

    #include <stdio.h>
    #include <string.h>
    
    double UnmanagedTest (const char* name, double num)
    {
        printf("Hello, %s\n", name);
        return num * strlen(name);
    }
    

    This is our C# application. External functions to be linked statically are declared like normal P/Invoke functions.

    using System.Runtime.InteropServices;
    
    internal static class App
    {
        [DllImport("nativelib", EntryPoint = "UnmanagedTest", CallingConvention = CallingConvention.Cdecl)]
        public static extern double UnmanagedTest ([MarshalAs(UnmanagedType.LPStr)] string name, double num);
    
        public static void Main()
        {
            Console.WriteLine("Hello, World!");
            var val = UnmanagedTest("World!", 7);
            Console.WriteLine($"I got the number '{val}' back");
        }
    }
    
    

    This part of the project file is responsible for generating the direct calls and linking statically against the library written in C:

    app.csproj

    
    <ItemGroup>
        <!-- Generate direct PInvokes for Dependency -->
        <DirectPInvoke Include="nativelib" />
        <!-- Specify library to link against -->
        <NativeLibrary Include="nativelib.lib" Condition="$(RuntimeIdentifier.StartsWith('win'))" />
        <!-- Specify the path to search for libraries -->
        <LinkerArg Include="/LIBPATH:..\\clib" Condition="$(RuntimeIdentifier.StartsWith('win'))" />
    </ItemGroup>
    
    

    References