Baike.dev
All toolsOnline toolsAI codingTrendingOpen sourceNewsSubmit
Log in
< Back to tools
C

csbindgen

> 编程语言
Open source

Generate C# FFI from Rust for automatically brings native code and C native library to .NET and Unity.

937 stars0 likes0 views
WebsiteGitHub

About

Generate C# FFI from Rust for automatically brings native code and C native library to .NET and Unity.

csbindgen

Generate C# FFI from Rust for automatically brings native code and C native library to .NET and Unity.

Automatically generates C# DllImport code from Rust extern "C" fn code. Whereas DllImport defaults to the Windows calling convention and requires a lot of configuration for C calls, csbindgen generates code optimized for "Cdecl" calls. Also .NET and Unity have different callback invocation methods (.NET uses function pointers, while Unity uses MonoPInvokeCallback), but you can output code for either by configuration.

When used with Rust's excellent C integration, you can also bring C libraries into C#. rust-bindgen is a proven .h parsing library with extensive track record. By using it together with csbindgen, you can generate code to call C code. In this case, you don't need a Rust native library - all you need is the C/C++ binary and C#.

Additionally, Rust has an excellent toolchain for cross-platform builds, and the cc crate and cmake crate allow C source code to be integrated into the build. When used together with these, it also supports consolidating multiple native calls and simplifying them into Rust calls.

showcase:

  • lz4_bindgen.cs : LZ4 compression library C# binding
  • zstd_bindgen.cs : Zstandard compression library C# binding
  • quiche_bindgen.cs : cloudflare/quiche QUIC and HTTP/3 library C# binding
  • bullet3_bindgen.cs : Bullet Physics SDK C# binding
  • sqlite3_bindgen.cs : SQLite C# binding
  • Cysharp/YetAnotherHttpHandler : brings the power of HTTP/2 (and gRPC) to Unity and .NET Standard
  • Cysharp/NativeCompressions : LZ4 and Zstandard binding to Unity and .NET Standard
  • Cysharp/MagicPhysX : .NET PhysX 5 binding to all platforms(win, osx, linux)

Getting Started

Install on Cargo.toml as build-dependencies and set up bindgen::Builder on build.rs.

toml
[package]
name = "example"
version = "0.1.0"

[lib]
crate-type = ["cdylib"]

[build-dependencies]
csbindgen = "1.9.6"

Rust to C#.

You can bring Rust FFI code to C#.

rust
// lib.rs, simple FFI code
#[no_mangle]
pub extern "C" fn my_add(x: i32, y: i32) -> i32 {
    x + y
}

Setup csbindgen code to build.rs, use builder and input_extern_file.

rust
fn main() {
    csbindgen::Builder::default()
        .input_extern_file("lib.rs")
        .csharp_dll_name("example")
        .generate_csharp_file("../dotnet/NativeMethods.g.cs")
        .unwrap();
}

csharp_dll_name is for specifying [DllImport({DLL_NAME}, ...)] on the C# side, which should match the name of the dll binary. See #library-loading section for how to resolve the dll file path.

[!NOTE] In this example, the value of csharp_dll_name is output by the Rust project you set up. In the above, package.name in the Cargo.toml is set to "example". By default, the following binaries should be output to the target/ folder of the Rust project.

  • Windows: example.dll
  • Linux: libexample.so
  • macOS: libexample.dylib

The filename without the extension should be specified to DllImport. Be careful that by default, rust compiler prefixes filenames with "lib" in some environments. So if you want to try this example as is on macOS, csharp_dll_name would be "libexample".

Then, let's run cargo build it will generate this C# code.

csharp
// NativeMethods.g.cs
using System;
using System.Runtime.InteropServices;

namespace CsBindgen
{
    internal static unsafe partial class NativeMethods
    {
        const string __DllName = "example";

        [DllImport(__DllName, EntryPoint = "my_add", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
        public static extern int my_add(int x, int y);
    }
}

C (to Rust) to C#

For example, build lz4 compression library.

C to C#

It's almost the same as Rust to C#, but add a reference to bindgen, and first add bindgen generation to build.rs. Then in csbindgen's builder, specify input_bindgen_file to load the file.

toml
[package]
name = "example"
version = "0.1.0"

[lib]
crate-type = ["cdylib"]

[build-dependencies]
csbindgen = "1.9.6"
bindgen = "0.72.1"
…

In this case, you won't use the Rust binary built. Instead, build the C/C++ library separately using make, cmake, or other tools, and place it accordingly. When targeting OSS libraries, they typically come with make/cmake configurations, so it's best to follow their build procedures to generate the library.

bindgen's default_enum_style generates consts. When handling in C#, it would be more practical to generate them as enums. Also, csbindgen does not generate consts by default. By enabling csharp_generate_const_filter, consts will be generated on the C# side.

Additionally, types and enums that exist in the input file are all trimmed and not generated if they are not used in method definitions. If you want to generate types or enums on the C# side, you explicitly specify them using always_included_types. For example, .always_included_types(["ZL_StandardGraphID", "ZL_StandardNodeID"]).

C to Rust to C#

You can incorporate and build C code into a Rust library by using the cc crate or cmake crate. In that case, you need to create a flow where C# calls Rust, and Rust calls C. You can automate this entire flow by generating both Rust and C# files with generate_to_file.

…

It will generates like these code.

rust
// lz4_ffi.rs

#[allow(unused)]
use ::std::os::raw::*;

use super::lz4::*;

#[no_mangle]
pub unsafe extern "C" fn csbindgen_LZ4_compress_default(src: *const c_char, dst: *mut c_char, srcSize:  c_int, dstCapacity:  c_int) ->  c_int
{
    LZ4_compress_default(src, dst, srcSize, dstCapacity)
}
csharp
// NativeMethods.lz4.g.cs

using System;
using System.Runtime.InteropServices;

namespace CsBindgen
{
    internal static unsafe partial class NativeMethods
    {
        const string __DllName = "liblz4";

        [DllImport(__DllName, EntryPoint = "csbindgen_LZ4_compress_default", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
        public static extern int LZ4_compress_default(byte* src, byte* dst, int srcSize, int dstCapacity);
    }
}

Finally import generated module on lib.rs.

rust
// lib.rs, import generated codes.
#[allow(dead_code)]
#[allow(non_snake_case)]
#[allow(non_camel_case_types)]
#[allow(non_upper_case_globals)]
mod lz4;

#[allow(dead_code)]
#[allow(non_snake_case)]
#[allow(non_camel_case_types)]
mod lz4_ffi;

Builder options(configure template)

Builder options: Rust to C#

Rust to C#, use the input_extern_file -> setup options -> generate_csharp_file.

…

csharp_* configuration will be embedded in the placeholder of the output file.

…

csharp_dll_name_if is optional. If specified, #if allows two DllName to be specified, which is useful if the name must be __Internal at iOS build. This option allows multiple call.

csharp_disable_emit_dll_name is optional, if set to true then don't emit const string __DllName. It is useful for generate same class-name from different builder.

csharp_generate_const_filter is optional, if set a filter fun, then generate filter C# const field from Rust const.

input_extern_file and input_bindgen_file allow mulitple call, if you need to add dependent struct, use this.

rust
csbindgen::Builder::default()
    .input_extern_file("src/lib.rs")
    .input_extern_file("src/struct_modules.rs")
    .generate_csharp_file("../dotnet-sandbox/NativeMethods.cs");

also csharp_imported_namespaces can call multiple times.

always_included_types is optional. Types and enums that exist in the input file are all trimmed and not generated if they are not used in method definitions. If set name, do not trim and generate type to C#.

Unity Callback

csharp_use_function_pointer configures how generate function pointer. The default is to generate a delegate*, but Unity does not support it; setting it to false will generate a Func/Action that can be used with MonoPInvokeCallback.

…

Builder options: C (to Rust) to C#

input_bindgen_file -> setup options -> generate_csharp_file to use C to C# workflow. input_bindgen_file -> setup options -> generate_to_file to use C to Rust to C# workflow.

…

It will be embedded in the placeholder of the output file.

rust
#[allow(unused)]
use ::std::os::raw::*;

{rust_file_header}

#[no_mangle]
pub unsafe extern "C" fn {rust_method_prefix}LZ4_versionNumber() ->  c_int
{
    {rust_method_type_path}::LZ4_versionNumber()
}

csharp_* option template is same as Rust to C#, see above documentation.

Adjust rust_file_header for match your module configuration, recommend to use ::*, also using rust_method_type_path that add explicitly resolve path.

method_filter allows you to specify which methods to exclude; if unspecified, methods prefixed with _ are excluded by default. C libraries are usually published with a specific prefix. For example, LZ4 is LZ4, ZStandard is ZSTD_, quiche is quiche_, Bullet Physics SDK is b3.

rust_method_prefix and csharp_method_prefix or csharp_entry_point_prefix must be adjusted to match the method name to be called.

Library Loading

If the file path to be loaded needs to be changed depending on the operating system, the following load code can be used.

…

The naming convention {platform}-{arch}/native/ is used in NuGet packaging for native libraries. When pushing to NuGet, if files are arranged according to this naming convention, the DllImportResolver mentioned above is unnecessary and the load path is automatically specified. If not using NuGet, even if files are arranged with the same naming convention, the Resolver mentioned above is still needed.

If Unity, configure Platform settings in each native library's inspector.

Grouping Extension Methods

In an object-oriented style, it is common to create methods that take a pointer to a state (this) as their first argument. With csbindgen, you can group these methods using extension methods by specifying a Source Generator on the C# side.

Install csbindgen from NuGet, and specify [GroupedNativeMethods] for the partial class of the generated extension methods.

PM> Install-Package csbindgen

csharp
// create new file and write same type-name with same namespace
namespace CsBindgen
{
    // append `GroupedNativeMethods` attribute
    [GroupedNativeMethods]
    internal static unsafe partial class NativeMethods
    {

Issues· 0 open

View all issuesOpen on GitHub

No open issues yet, or sync has not completed.

> Tags

Rust

No comments yet. Be the first to share.

> Details

PublishedAug 1, 2026
UpdatedSep 17, 2026
Category编程语言
PricingOpen source

> Related tools

T
TypeScript
JavaScript 的超集,为前端与全栈提供静态类型
P
Python
通用编程语言,广泛用于 Web、数据与 AI
G
Go
Google 推出的简洁高效系统语言