Calling C# natively from Rust.

Yes, it’s an electron app, but most of the heavy lifting is Rust.

Preparing C# for Native Interop

<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>
<NativeLib>Static</NativeLib>
</PropertyGroup>
dotnet new nuget
<add key="dotnet-core" value="https://dotnet.myget.org/F/dotnet-core/api/v3/index.json" />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
dotnet add package Microsoft.DotNet.ILCompiler -v 1.0.0-alpha-*

Exposing C# methods to the C FFI

namespace System.Runtime.InteropServices
{
[AttributeUsage(AttributeTargets.Method)]
public sealed class NativeCallableAttribute : Attribute
{
public string EntryPoint;
public CallingConvention CallingConvention;
public NativeCallableAttribute() { }
}
}
[NativeCallable(EntryPoint = "add_dotnet", CallingConvention = CallingConvention.Cdecl)]
public static int Add(int a, int b) {
return a + b;
}
dotnet publish /t:LinkNative /p:NativeLib=Static -c Release -r win-x64
#[link(name = "bootstrapperdll", kind = "static")] 
#[link(name = "Runtime", kind = "static")]
#[link(name = "mycsharplibrary", kind = "static")]
extern "C" {
pub fn add_dotnet(a: i32, b: i32) -> i32;
}
let result = add_dotnet(1, 2);
println!("{}", result);
println!("cargo:rustc-link-search=native={}", "./lib");
println!("cargo:rustc-link-search=native={}", "/path/to/bootstrapperdll");

Returning structs from C#

[StructLayout(LayoutKind.Sequential)]
public struct MyStruct
{
...
}
#[repr(C)]
pub struct MyStruct {
pub ...,
}

Strings

[NativeCallable(EntryPoint = "hello_world", CallingConvention = CallingConvention.Cdecl)]
public IntPtr HelloWorld()
{
return Marshal.StringToCoTaskMemUTF8("Hello World");
}
[NativeCallable(EntryPoint = "free_corert", CallingConvention = CallingConvention.Cdecl)]
public static void Free(IntPtr ptr) {
Marshal.FreeCoTaskMem(ptr);
}

Reflection, or more appropriately RTTI

<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata"> 
<Application>
<Assembly Name="[name of your assembly]" Dynamic="Required All"/>
</Application>
</Directives>
<Assembly Name="Newtonsoft.Json" Dynamic="Required All"/>
<ItemGroup>
<RdXmlFile Include="rd.xml" />
<!--Include stack trace data -->
<IlcArg Include="--stacktracedata" />
</ItemGroup>

Putting it all together

Conclusion

Final Remarks

--

--

--

I like making things. Computer Science and East Asian Studies student at the University of Toronto. https://chyyran.moe

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Recursion /Tail Recursion in Scala

Plan on long sales cycles for software designed to enable enterprise migrations to the cloud

How to Receive and Respond to Incoming SMS Messages in Ruby with Rails and Plivo

Ways to install Laravel 8.0 on macOS

Meet our Organizing Team

Blockchain in Supplychain :)

GN! Working with our project turned ON ☕️ We are in stage of full redesign and token concept change…

Deploy celery and celery beat in production with Django (Ubuntu)

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Ronny Chan

Ronny Chan

I like making things. Computer Science and East Asian Studies student at the University of Toronto. https://chyyran.moe

More from Medium

Fundamentals of functional programming

Formatting, Linting, and Documenting with Rust

Dynamic Library In C

Rust 002 — Variables, Functions, Conditionals, Loops