Skip to content

Instantly share code, notes, and snippets.

@fiddyschmitt
Created November 4, 2025 03:41
Show Gist options
  • Select an option

  • Save fiddyschmitt/ad1d89804074c78dc518c4068878b780 to your computer and use it in GitHub Desktop.

Select an option

Save fiddyschmitt/ad1d89804074c78dc518c4068878b780 to your computer and use it in GitHub Desktop.
Use a native windows dll in Linux
Say you have a native Windows library called libfoo.dll that you'd like to use in Linux. The following instructions describe how to wrap it in a .NET application and use Wine to run it.
1) Generate bindings
Using Visual Studio:
Use BindingGenerator (below) to generate C# bindings to the DLL.
The output folder will contain libfoo.cs
2) Write a wrapper program
Write a program which uses libfoo.cs
If required, instantiate objects using the Foo.cs example below
Compile the program as follows:
Right-click the Project
Publish -> Folder
Show all settings
Configuration: Release
Target framework: net8.0
Deployment mode: Self-contained
Target Runtime: win-x64
Produce single file: Tick
Once published, copy the required DLLs into the output folder
3) Run the program on Linux
apt-get update
apt install wine -y
wine app.exe
using CppSharp;
using CppSharp.AST;
using CppSharp.Generators;
using CppSharp.Parser;
namespace BindingGenerator
{
public class BindingGenerator : ILibrary
{
public static void Main(string[] args)
{
ConsoleDriver.Run(new BindingGenerator());
}
// Setup the driver options and module information.
public void Setup(Driver driver)
{
var options = driver.Options;
options.GeneratorKind = GeneratorKind.CSharp;
var parserOptions = driver.ParserOptions;
parserOptions.LanguageVersion = LanguageVersion.C99;
parserOptions.MicrosoftMode = true;
// Define the module containing your C++ library details
var module = options.AddModule("libfoo");
module.IncludeDirs.Add(@"D:\libfoo\headers");
module.Headers.Add("interface.h");
module.LibraryDirs.Add(@"D:\libfoo");
module.Libraries.Add("libfoo.dll");
options.OutputDir = Path.Combine(Directory.GetCurrentDirectory(), "GeneratedBindings");
// Optional: Configure specific calling conventions if necessary
// options.CallingConvention = CallingConvention.Cdecl;
}
public void SetupPasses(Driver driver)
{
// Passes can be added here to customize the AST (Abstract Syntax Tree)
// before generation (e.g., to rename specific functions or ignore some declarations).
}
public void Preprocess(Driver driver, ASTContext ctx)
{
// Preprocessing step: filter out unwanted declarations.
// For example, you can ignore specific functions or classes here.
}
public void Postprocess(Driver driver, ASTContext ctx)
{
// Postprocessing step: make final adjustments to the generated bindings.
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using libfoo;
using static DemoFoo.Utilities.Utils;
namespace libfoo
{
public unsafe partial class FooInputs
{
public FooInputs()
{
__Instance = Marshal.AllocHGlobal(sizeof(__Internal));
MemoryManagement.RtlZeroMemory(__Instance, sizeof(__Internal));
}
}
public unsafe partial class FooOutputs
{
public FooOutputs()
{
__Instance = Marshal.AllocHGlobal(sizeof(__Internal));
MemoryManagement.RtlZeroMemory(__Instance, sizeof(__Internal));
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace DemoFoo.Utilities
{
public static class Utils
{
public static class MemoryManagement
{
[DllImport("kernel32.dll")]
public static extern void RtlZeroMemory(IntPtr dst, int length);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment