LibraryImport examples, especially on Linux
Has anyone got examples of usage of the newer LibraryImport infrastructure (?) as opposed to DllImport?
I have seen https://stackoverflow.com/questions/75304403/marshalling-function-pointers-with-net-7-libraryimport which just about melted my face off
and https://learn.microsoft.com/en-us/dotnet/standard/native-interop/pinvoke-source-generation isn't bad but no one seems to be using this in open source code.
Stack Overflow
Marshalling Function Pointers with .NET 7 LibraryImport
I'm trying to implement some P/Invoke code using the new LibraryImport attribute, as opposed to the old DllImport. Specifically, I am trying to marshal a WNDCLASSEXW struct for use in RegisterClass...
P/Invoke source generation - .NET
Learn about compile-time source generation for platform invokes in .NET.
55 Replies
Better, just ask @jkortech to help you
That doesn't seem scaleable.
I agree in general, but it'll probably be fine this time
I'm not sure if this is viewable, but https://git.sr.ht/~voltagex/csharp-udev/tree/main/item/Udev.cs is as far as I got before realising I was using a deprecated API
The main questions I have are around how to get back to the ".NET world" - if everything is marked unsafe, I'm just writing C badly in C#
That’s a flawed/incorrect perception
C# includes unsafe and always has, it is a fundamental part of the language
By calling a p/invoke, you are fundamentally doing something unsafe and realistically it always should have required you to specify it as such
that makes sense
There are many benefits to writing code in c# and having some key unsafe parts, and overall you get more type safety, more portability, and generally better integration with other things that .net provides
I guess it's more a design question of how not to have unsafe leak into all of my code
You use LibraryImport to write your “safe” p/invokes taking things like string, span, T[], out T, etc
The source generator then generates the unsafe code behind the scenes to make it all work
It already doesn’t leak unless you explicitly make the p/invoke take a pointer type
hrm
I need to find an "easier" Linux API to try this out on
It’s really no different than you would’ve written more classic p/invokes. You largely just use LibraryImport instead of DllImport
P/Invokes have always done my head in 😅
There’s a couple additional difference, like the ability to use span, the requirement to enable unsafe blocks, and the need to be explicit about Boolean marshaling
but I think you might have given me a good pointer (hah) there.
But for the most parts it’s a 1-1 swap
My LibraryImports took pointers because that's what the native side expects. I guess i need to understand the CustomMarshaller stuff?
It depends, there’s a number of built in marshallers to handle common scenarios like strings or arrays. If it gets more esoteric you may need a custom one
Writing 1-1 bindings against native can be beneficial though. While it requires the caller to be more verbose, it can help improve performance and the overall readability/maintainability of the code, as you can more precisely see what’s actually happening
That’s not required in many scenarios though, which is why the safe wrappers the generator creates can often be good as well
It really depends on the needs and goals of your application/library and how you want to maintain your code long term
What do you mean by 1-1 bindings here?
Meaning a signature that is as close to what native defines as possible, generally these are fully blittable (require no marshaling), use pointers, etc
I went down the rabbit hole of matching an API exactly and got tangled up implementing all sorts of internal data structures. Using opaque pointers (?) got me a lot further a lot faster
I wonder what I need to go learn for all this to click together in my brain.
Spend a few months in #allow-unsafe-blocks and the
unsafe
mental parasite will take over soon enoughPeople often use tooling to make it simpler, things like ClangSharp will take a c header and produce blittable bindings directly
ClangSharp is also a great learning tool if you want to see how stuff translates
TIL ClangSharp
thank you.
ClangSharpPInvokeGenerator, specifically
rolls right off the tongue
It’s not necessarily the easiest tool to use, as it is essentially a wrapper over clang and therefore requires many options to be passed in, much as actual clang does
But it’s used by a large number of projects, including at Microsoft, to maintain bindings for various languages
would you go from that generated code back to LibraryImport though?
No, ClangSharp and LibraryImport are basically parallel tools. They do similar things, but with overall different intents/approaches
LibraryImport works great when you need a small number of C# friendly bindings that take advantage of marshaling
Yeah if you're using one, you typically won't be using the other for the same imports
But you can definitely use ClangSharpPInvokeGenerator to save time if you have an import you want to do manually/with LibraryImport and don't want to spend a lot of time writing out the interop structures
ClangSharp tends to be used to bind against entire libraries or sdks, providing everything and as close to “#include <header.h>” as you can get in dotnet
It’s often used for high performance scenarios like graphics bindings for games, rather than one off helpers for lob apps or tools
damn, I don't think Debian has a new enough version of clang
stuck trying to get libclangsharp.so
I did not expect to be compiling Clang today.
I thought there were already Linux binaries available for ClangSharpPInvokeGenerator + ClangSharp + the associated Clang version on NuGet
@Zombie I wasn't able to work out how to tell dotnet/nuget to give me the file while not in a csproj, I also couldn't find the native binaries in whatever clangsharp nupkg. I am most definitely missing something here.
dotnet tool install --global ClangSharpPInvokeGenerator
iirc
then just ClangSharpPInvokeGenerator
is usable in the terminalyep, that was throwing errors about not neing able to find various .so files
Weird
but I am not sure what it's asking me to read on https://github.com/dotnet/clangsharp
GitHub
GitHub - dotnet/ClangSharp: Clang bindings for .NET written in C#
Clang bindings for .NET written in C#. Contribute to dotnet/ClangSharp development by creating an account on GitHub.
I assume this part
bah, we're way off topic now and I'm missing simple stuff.
WIthout setting up a csproj somewhere I can't see how to pull down the correct package
Shouldn't need a csproj since ClangSharpPInvokeGenerator is a tool rather than something you import into your project
i.e. it is run externally
yes, but I need libclang/libClangSharp which are nupkg files. I can't see past the meta-package at a glance.
Ah
Might have to wait for Tanner to be around again I suppose
libclang.runtime.linux-x64 18.1.3.2
linux x64 native library for libclang.
right!
thanks @reflectronic
this has libclang.so
46MB, that seems more like it
libClangSharp.runtime.linux-x64 18.1.3.1
linux x64 native library for libClangSharp.
and this one has libclangsharp.so
the metapackage is set up in a strange way so that referencing it does not download the entire world to your hard drive
this maybe should be a bit clearer in the instructions on the repo @Tanner Gooding
Unhandled exception: System.UnauthorizedAccessException: Access to the path '/home/voltagex/src/docker/test-pinvoke' is denied.
(it's not in docker now, but I am getting the error no matter where I run ClangSharpPInvokeGenerator)
ah, that's meant to be a filename, not a directory
Well it was worth a shot, but I am beginning to think slogging through LibraryImport would be easier.
message continues, there is no way in hell I am subscribing to Discord Nitro to be able to communicate.
If you want some parameter examples you can check out Tanner's TerraFX repos or my own https://github.com/NewBloodInteractive/NewBlood.Interop.Steamworks
Look in the .rsp files in the generation folder
The main thing to note is that there is both
--file
and --traverse
. The former means "parse this file", while the latter means "stuff in this file should have bindings generated for it"
I guess the errors you have there are a result of the parameters passed to Clang, which you can control via --additional
in ClangSharpPInvokeGenerator (settings.rsp in the aforementioned repos)this is very much appreciated, although I am definitely at information overload now, and will go play some video games instead.
hahaha, understandable
This feels like going around the long way to learning C.
I'm still musing about what the "easiest" API to generate bindings against would be. I guess there's always SWIG