Writing Native Libraries in C# and using them in other languages
Tags: csharp, dotnet, corert
Published: 2019-05-03 19:21:46

---
title: Writing Native Libraries in C# and using them in other languages
published: true
canonical_url: https://mazeez.dev/posts/writing-native-libraries-in-csharp
description: Write libraries in C# and compile them using CoreRT to be able to re-use them in other programming languages that support the standard FFI.
tags: csharp, dotnet, corert
---
> Note: Stefan Hausotte has ported [this little experiment to F#](https://secanablog.wordpress.com/2020/02/01/writing-a-native-library-in-f-which-can-be-called-from-c/).

Recently I stumbled upon [this article](https://medium.com/@MStrehovsky/fight-the-global-warming-compile-your-c-apps-ahead-of-time-9997e953645b) from [Michal Strehovsky](https://twitter.com/MStrehovsky). It was a great introduction to CoreRT, it made me curious, can you write native libraries with CoreRT? and the answer was Yes!

{% twitter 1123859217654460424 %}

I have a little library that I want to be available for multiple languages, so I was quite interested in it. So I tried out the [official sample](https://github.com/dotnet/corert/tree/master/samples/NativeLibrary) and was delighted with the results.

I compiled the library using `dotnet publish /p:NativeLib=Shared -r win-x64 -c Release` and it produced a 4.52 MB dll. With the help of Michal and by following the steps of [this article](https://github.com/dotnet/corert/blob/master/Documentation/using-corert/optimizing-corert.md), I was able to get the size down to 1.67 MB, which is good enough for me.

The official sample has [a class](https://github.com/dotnet/corert/blob/master/samples/NativeLibrary/Class1.cs) that contains two methods: `Add` and `WriteLine` which demonstrate how to take primitives and strings as parameters. By default, CoreRT only allows primitives as parameter types, you'll have to marshal anything else that's more complex. However, `System.Runtime.InteropServices.Marshal` does some have helpful methods.


```csharp
public class Class1
{
    [NativeCallable(EntryPoint = "add", CallingConvention = CallingConvention.StdCall)]
    public static int Add(int a, int b)
    {
        return a + b;
    }

    [NativeCallable(EntryPoint = "write_line", CallingConvention = CallingConvention.StdCall)]
    public static int WriteLine(IntPtr pString)
    {
        // The marshalling code is typically auto-generated by a custom tool in larger projects.
        try
        {
            // NativeCallable methods only accept primitive arguments. The primitive arguments
            // have to be marshalled manually if necessary.
            string str = Marshal.PtrToStringAnsi(pString);

            Console.WriteLine(str);
        }
        catch
        {
            // Exceptions escaping out of NativeCallable methods are treated as unhandled exceptions.
            // The errors have to be marshalled manually if necessary.
            return -1;
        }
        return 0;
    }
}

```

The methods that are decorated with `NativeCallable` cannot be called through normal C# methods, but they can be called through P/Invoke 😈.

To do that, you have to:
1. Build the native library by running `dotnet publish /p:NativeLib=Shared -r win-x64 -c Release` in its folder. 
1. Create a new console app (I created a dotnet core console app, but it doesn't matter).
1. Right click on the project and click `Add => Exisiting Item`.
1. Browse to `bin\Release\netcoreapp2.2\win-x64\native` folder of the native library project and then select the dll and click on `Add As Link`.
1. Right Click on the dll in Solution Explorer and click on properties.
1. Change `Copy to Output Directory` to `Copy if newer`.
1. Change Solutions Platform to `x64`
![Solutions Platform](https://thepracticaldev.s3.amazonaws.com/i/czdhpxbk35a55er415n3.PNG)
1. And then change the code in `Program.cs` as follows:

```csharp
class Program
{
    [DllImport("NativeLibrary.dll", EntryPoint = "add", CallingConvention = CallingConvention.StdCall)]
    public static extern int Add(int a, int b);

    [DllImport("NativeLibrary.dll", EntryPoint = "write_line", CallingConvention = CallingConvention.StdCall)]
    public static extern void WriteLine(string text);

    static void Main(string[] args)
    {
        var result = Add(1, 2);
        WriteLine(result.ToString());
        WriteLine("Hello World!");
    }
}

```

Now run the console app and you'll get an output like this:

```
3
Hello World!
```

![But Why?](https://thepracticaldev.s3.amazonaws.com/i/84kbvfvss1xwiy7vnvbx.gif)

Now p/invoking the library might not be very useful, but compiling a class library as a native library opens doors for other languages to call the library.

It was a long and painful process, but I was eventually able to reference the library from the C++ app. This [video](https://youtu.be/or1dAmUO8k0) and this [article](https://docs.microsoft.com/en-us/cpp/build/walkthrough-creating-and-using-a-dynamic-link-library-cpp?view=vs-2019) were super helpful.

Here are the steps:
1 . Add an empty C++ project to the solution.

2 . Add a source file and paste in this code snippet:

```cpp
#include <iostream>
#include <NativeLibrary.h>
using namespace std;

void main()
{
    int result = add(1, 2);
    cout << result << endl;
    write_line("Hello World!");
}
```
3 . Create a new header file called `NativeLibrary.h` (That's the name of the library) and paste in this code snippet:

```cpp
#pragma once
extern "C" int __stdcall add(int a, int b);
extern "C" void __stdcall write_line(const char* pString);
```

As you can see have written the signatures of the functions that are exported from `NativeLibrary`.

4 . Right Click on the C++ project and Click on Properties.

5 . Choose `All Configurations` from `Configuration:`. This will make sure that the changes apply to both `Release` and `Debug` configurations (And any other configuration you might have).

6 . Go to General and change `Output Directory` to `$(ProjectDir)bin\$(Platform)\$(Configuration)\`. This is not necessary, but I felt more at home like this.

7 . Go to `C\C++` > `Linker` > `General` and add `$(SolutionDir)NativeLibrary\bin\Release\netcoreapp2.2\win-x64\native` to `Additional Library Directories`. This allows the linker to discover `NativeLibrary.lib`.

8 . Go to `C\C++` > `Linker` > `Input` and add `NativeLibrary.lib` to the list of `Additional Dependencies`.

9 . Go to `Build Events` > `Post-Build Event` and paste in this code snippet to `Command Line`:

```batch
xcopy /y /d "$(SolutionDir)NativeLibrary\bin\Release\netcoreapp2.2\win-x64\native\NativeLibrary.dll" "$(OutDir)"
```

This will copy `NativeLibrary.dll` to the output dir whenever you build the C++ project.

10 . Build and run the application and you should see this output:

```
3
Hello World!
```
If this is not cool, I don't know what is.

The source code is [available on GitHub](https://github.com/encrypt0r/CoreRTDemo).