Introduction
Sometimes you want to write a .NET application that talks to an unmanaged library. Your first step? To google bindings for that library. But what if there are no bindings for such a library? Well, you can generate them yourself!
TLDR: Final bindings are in the NetObsBindings repository
Table of Contents
ClangSharp
ClangSharp is a generator library/tool that takes a C or C++ header files as input and spits out C# interop code. It has various configuration options to tweak the output. It's easier to show on an example. Let's take a practical scenario.
Generating bindings
Imaging I am building an OBS plugin.
OBS - Open Broadcaster Software that provides an open source video streaming app.
How do I generate .NET bindings for OBS?
OBS has a concept of a video/audio source that can add video/audio to your video stream.
I would like to add my own video source.
To enable that capability, OBS provides a function obs_register_source_s
in obs-source.h
header file.
EXPORT void obs_register_source_s(const struct obs_source_info *info, size_t size);
We established what we want, so let's do it. Firstly, we would want to clone the OBS repository and create a dummy project to test if our bindings compile.
mkdir obs-interop cd obs-interop git clone https://github.com/obsproject/obs-studio # clone obs repository dotnet new console -o TestObsBindings -n TestObsBindings # create the dummy project
Next, we need to use the bindings generator. Let's use it as a global dotnet tool. For it to succeed, we also would need the clang compiler of the same version.
dotnet tool install --global ClangSharpPInvokeGenerator --version 13.0.0-beta1 # install the global dotnet tool winget search clang # find the clang compiler winget install LLVM.LLVM --version 13.0.0 # we found it, the package is called LLVM.LLVM
Let's try to use it.
ClangSharpPInvokeGenerator ` --file .\obs-studio\libobs\obs-source.h <# file we want to generate bindings for #> ` -n ObsInterop <# namespace of the bindings #> ` --methodClassName ObsSource <# class name where to put methods #> ` --libraryPath obs <# name of the DLL #> ` -o .\TestObsBindings\ObsInterop.cs <# output #>
Oh-oh, we got a bunch of errors.
Diagnostics for '.\obs-studio\libobs\obs-source.h':
.\obs-studio\libobs/obs-source.h:33:6: error: redefinition of 'obs_source_type'
.\obs-studio\libobs/obs-source.h:40:6: error: redefinition of 'obs_balance_type'
.\obs-studio\libobs/obs-source.h:46:6: error: redefinition of 'obs_icon_type'
.\obs-studio\libobs/obs-source.h:63:6: error: redefinition of 'obs_media_state'
.\obs-studio\libobs/obs-source.h:210:8: error: redefinition of 'obs_source_audio_mix'
.\obs-studio\libobs/obs-source.h:217:8: error: redefinition of 'obs_source_info'
Skipping '.\obs-studio\libobs\obs-source.h' due to one or more errors listed above.
Hmm, it seems that the same file obs-source
is imported twice.
The header file has #pragma once
but it is not respected when it is the root file.
Well, let's try another way - by using the --traverse
(-t
) parameter to say which module we are interested in.
But what to pick as the -f
parameter?
All OBS modules include obs-module.h
and it transitively references all the needed headers, let's try that.
ClangSharpPInvokeGenerator ` --file .\obs-studio\libobs\obs-source.h <# file we want to generate bindings for #> ` --traverse .\obs-studio\libobs\obs-source.h ` -n ObsInterop <# namespace of the bindings #> ` --methodClassName ObsSource <# class name where to put methods #> ` --libraryPath obs <# name of the DLL #> ` -o .\TestObsBindings\ObsInterop.cs <# output folder #>
Success!
I would like to configure the generation a little bit. Let's make it prettier by splitting things into multiple files and using file scoped namespaces.
ClangSharpPInvokeGenerator ` -c multi-file generate-file-scoped-namespaces <# configuration for the generator#> ` --file .\obs-studio\libobs\obs-source.h <# file we want to generate bindings for #> ` --traverse .\obs-studio\libobs\obs-source.h ` -n ObsInterop <# namespace of the bindings #> ` --methodClassName ObsSource <# class name where to put methods #> ` --libraryPath obs <# name of the DLL #> ` -o .\TestObsBindings\ObsInterop <# output folder #>
Nice!
Getting bindings to compile
dotnet build .\TestObsBindings\TestObsBindings.csproj
When trying to compile, I get a lot of errors in the console. I will tackle them one by one.
- The type or namespace name 'NativeTypeNameAttribute' could not be found
Hmm, there are attributes sprinkled everywhere in the form of
[NativeTypeName("const struct obs_source_info *")]
It seems that I didn't specify a configuration switch generate-helper-types
for those helper types.
Adding it fixes these types of errors.
ClangSharpPInvokeGenerator ` -c multi-file generate-file-scoped-namespaces generate-helper-types <# configuration for the generator#> ` --file .\obs-studio\libobs\obs-module.h <# file we want to generate bindings for #> ` --traverse .\obs-studio\libobs\obs-source.h ` -n ObsInterop <# namespace of the bindings #> ` --methodClassName ObsSource <# class name where to put methods #> ` --libraryPath obs <# name of the DLL #> ` -o .\TestObsBindings\ObsInterop <# output folder #>
- The type or namespace name 'audio_output_data' could not be found
Ok, this is interesting.
The ClangSharp generator didn't include the type audio_output_data
in the final bindings.
Why?
Seems like it is included in another file.
Oh well, let's add it.
But let's add the bindings in a separate ObsAudio
method class name to not pollute our ObsSource one.
# ... omitting script for ObsSource ... ClangSharpPInvokeGenerator ` -c multi-file generate-file-scoped-namespaces generate-helper-types <# configuration for the generator#> ` --file .\obs-studio\libobs\obs-module.h <# file we want to generate bindings for #> ` --traverse .\obs-studio\libobs\media-io\audio-io.h ` -n ObsInterop <# namespace of the bindings #> ` --methodClassName ObsAudio <# class name where to put methods #> ` --libraryPath obs <# name of the DLL #> ` -o .\TestObsBindings\ObsInterop <# output folder #>
- Unsafe code may only appear if compiling with /unsafe
To fix that, we need to enable unsafe blocks in the project file (.csproj).
<PropertyGroup> <AllowUnsafeBlocks>true</AllowUnsafeBlocks> </PropertyGroup>
- The type or namespace name 'obs_data' could not be found
Oh, the struct definition is in a .c file. Why is it included in other header files?
Aha!
It seems that the actual structure of the obs_data is an implementation detail, and the library works with a type alias obs_data_t
.
It is also sometimes called an 'opaque' type.
For that, we can leverage the 'remapping' mechanism of ClangSharp (-r
parameter) that allows us to redefine any type in the source headers.
Let's remap all pointers to obs_data as void pointers (since we don't now and don't care about the internal structure).
ClangSharpPInvokeGenerator ` -c multi-file generate-file-scoped-namespaces generate-helper-types <# configuration for the generator#> ` --file .\obs-studio\libobs\obs-module.h <# file we want to generate bindings for #> ` --traverse .\obs-studio\libobs\obs-source.h ` -n ObsInterop <# namespace of the bindings #> ` --methodClassName ObsSource <# class name where to put methods #> ` --libraryPath obs <# name of the DLL #> ` -o .\TestObsBindings\ObsInterop <# output folder #> ` -r obs_data*=@void* <# remappings #> # ... omitting script for ObsAudio ...
- The type or namespace name 'obs_source' could not be found
- The type or namespace name 'obs_properties' could not be found
- The type or namespace name 'gs_effect' could not be found
- The type or namespace name 'obs_missing_files' could not be found
These all seem to be cases of 'opaque' types, let's add them to the remappings
ClangSharpPInvokeGenerator ` -c multi-file generate-file-scoped-namespaces generate-helper-types <# configuration for the generator#> ` --file .\obs-studio\libobs\obs-module.h <# file we want to generate bindings for #> ` --traverse .\obs-studio\libobs\obs-source.h ` -n ObsInterop <# namespace of the bindings #> ` --methodClassName ObsSource <# class name where to put methods #> ` --libraryPath obs <# name of the DLL #> ` -o .\TestObsBindings\ObsInterop <# output folder #> ` -r obs_data*=@void* gs_effect*=@void* obs_source*=@void* obs_properties*=@void* obs_missing_files*=@void* # ... omitting script for ObsAudio ...
- The type or namespace name 'obs_source_frame' could not be found
This struct seems to be in the obs.h
header. Let's generate bindings for that too.
ClangSharpPInvokeGenerator ` -c multi-file generate-file-scoped-namespaces generate-helper-types <# configuration for the generator#> ` --file .\obs-studio\libobs\obs-module.h <# file we want to generate bindings for #> ` --traverse .\obs-studio\libobs\obs.h ` -n ObsInterop <# namespace of the bindings #> ` --methodClassName Obs <# class name where to put methods #> ` --libraryPath obs <# name of the DLL #> ` -o .\TestObsBindings\ObsInterop <# output folder #> ` -r obs_data*=@void* gs_effect*=@void* obs_source*=@void* obs_properties*=@void* obs_missing_files*=@void* # ... omitting script for ObsAudio ... # ... omitting script for ObsSource ...
Oh wow, there are a lot more errors to handle. But we won't get discouraged!
- The type or namespace name 'video_format' could not be found
This type is located in video-io.h
. We can add it to our generation routine.
ClangSharpPInvokeGenerator ` -c multi-file generate-file-scoped-namespaces generate-helper-types <# configuration for the generator#> ` --file .\obs-studio\libobs\obs-module.h <# file we want to generate bindings for #> ` --traverse .\obs-studio\libobs\media-io\video-io.h ` -n ObsInterop <# namespace of the bindings #> ` --methodClassName ObsVideo <# class name where to put methods #> ` --libraryPath obs <# name of the DLL #> ` -o .\TestObsBindings\ObsInterop <# output folder #> ` -r obs_data*=@void* gs_effect*=@void* obs_source*=@void* obs_properties*=@void* obs_missing_files*=@void* # ... omitting script for ObsAudio ... # ... omitting script for ObsSource ... # ... omitting script for Obs ...
- The type or namespace name 'vec2' could not be found
- The type or namespace name 'vec3' could not be found
- The type or namespace name 'vec4' could not be found
These are vector data types. We can reuse System.Numerics.Vector2/3/4 for these by adding them to the remappings.
-r obs_data*=@void* gs_effect*=@void* obs_source*=@void* obs_properties*=@void* obs_missing_files*=@void* vec2=@System.Numerics.Vector2 vec3=@System.Numerics.Vector3 vec4=@System.Numerics.Vector4
- The type or namespace name 'profiler_name_store' could not be found
- The type or namespace name 'text_lookup' could not be found
- The type or namespace name 'signal_handler' could not be found
- The type or namespace name 'proc_handler' could not be found
- The type or namespace name 'obs_data_array' could not be found
- The type or namespace name 'gs_texture' could not be found
These ones look like opaque types or we don't care about their types yet. Let's add them to our remappings.
-r obs_data*=@void* gs_effect*=@void* obs_source*=@void* obs_properties*=@void* obs_missing_files*=@void* vec2=@System.Numerics.Vector2 vec3=@System.Numerics.Vector3 vec4=@System.Numerics.Vector4 profiler_name_store*=@void* text_lookup*=@void* signal_handler*=@void* proc_handler*=@void* obs_data_array*=@void* gs_texture*=@void*
- The type or namespace name 'gs_color_format' could not be found
This type is in the graphics.h
header file, let's add it to our generation procedure.
ClangSharpPInvokeGenerator ` -c multi-file generate-file-scoped-namespaces generate-helper-types <# configuration for the generator#> ` --file .\obs-studio\libobs\obs-module.h <# file we want to generate bindings for #> ` --traverse .\obs-studio\libobs\graphics\graphics.h ` -n ObsInterop <# namespace of the bindings #> ` --methodClassName ObsGraphics <# class name where to put methods #> ` --libraryPath obs <# name of the DLL #> ` -o .\TestObsBindings\ObsInterop <# output folder #> ` -r obs_data*=@void* gs_effect*=@void* obs_source*=@void* obs_properties*=@void* obs_missing_files*=@void* vec2=@System.Numerics.Vector2 vec3=@System.Numerics.Vector3 vec4=@System.Numerics.Vector4 profiler_name_store*=@void* text_lookup*=@void* signal_handler*=@void* proc_handler*=@void* obs_data_array*=@void* gs_texture*=@void*
- The type or namespace name 'obs_mouse_event' could not be found
This type is in the obs-interacton.h
header file, let's add it to our generation procedure.
ClangSharpPInvokeGenerator ` -c multi-file generate-file-scoped-namespaces generate-helper-types <# configuration for the generator#> ` --file .\obs-studio\libobs\obs-module.h <# file we want to generate bindings for #> ` --traverse .\obs-studio\libobs\obs-interaction.h ` -n ObsInterop <# namespace of the bindings #> ` --methodClassName ObsInteraction <# class name where to put methods #> ` --libraryPath obs <# name of the DLL #> ` -o .\TestObsBindings\ObsInterop <# output folder #> ` -r obs_data*=@void* gs_effect*=@void* obs_source*=@void* obs_properties*=@void* obs_missing_files*=@void* vec2=@System.Numerics.Vector2 vec3=@System.Numerics.Vector3 vec4=@System.Numerics.Vector4 profiler_name_store*=@void* text_lookup*=@void* signal_handler*=@void* proc_handler*=@void* obs_data_array*=@void* gs_texture*=@void*
- The type or namespace name 'input_subsystem' could not be found
This looks like an 'opaque type', let's add it to the remappings.
-r obs_data*=@void* gs_effect*=@void* obs_source*=@void* obs_properties*=@void* obs_missing_files*=@void* vec2=@System.Numerics.Vector2 vec3=@System.Numerics.Vector3 vec4=@System.Numerics.Vector4 profiler_name_store*=@void* text_lookup*=@void* signal_handler*=@void* proc_handler*=@void* obs_data_array*=@void* gs_texture*=@void* input_subsystem*=@void*
- The type or namespace name 'obs_encoder_type' could not be found
This type is defined in the obs-encoder.h
header, let's add it to the generation routine.
ClangSharpPInvokeGenerator ` -c multi-file generate-file-scoped-namespaces generate-helper-types <# configuration for the generator#> ` --file .\obs-studio\libobs\obs-module.h <# file we want to generate bindings for #> ` --traverse .\obs-studio\libobs\obs-encoder.h ` -n ObsInterop <# namespace of the bindings #> ` --methodClassName ObsEncoder <# class name where to put methods #> ` --libraryPath obs <# name of the DLL #> ` -o .\TestObsBindings\ObsInterop <# output folder #> ` -r obs_data*=@void* gs_effect*=@void* obs_source*=@void* obs_properties*=@void* obs_missing_files*=@void* vec2=@System.Numerics.Vector2 vec3=@System.Numerics.Vector3 vec4=@System.Numerics.Vector4 profiler_name_store*=@void* text_lookup*=@void* signal_handler*=@void* proc_handler*=@void* obs_data_array*=@void* gs_texture*=@void* input_subsystem*=@void*
- The type or namespace name 'obs_service_resolution' could not be found
This type is defined in the obs-service.h
header, let's add it to the generation routine.
ClangSharpPInvokeGenerator ` -c multi-file generate-file-scoped-namespaces generate-helper-types <# configuration for the generator#> ` --file .\obs-studio\libobs\obs-module.h <# file we want to generate bindings for #> ` --traverse .\obs-studio\libobs\obs-service.h ` -n ObsInterop <# namespace of the bindings #> ` --methodClassName ObsService <# class name where to put methods #> ` --libraryPath obs <# name of the DLL #> ` -o .\TestObsBindings\ObsInterop <# output folder #> ` -r obs_data*=@void* gs_effect*=@void* obs_source*=@void* obs_properties*=@void* obs_missing_files*=@void* vec2=@System.Numerics.Vector2 vec3=@System.Numerics.Vector3 vec4=@System.Numerics.Vector4 profiler_name_store*=@void* text_lookup*=@void* signal_handler*=@void* proc_handler*=@void* obs_data_array*=@void* gs_texture*=@void* input_subsystem*=@void*
- The name 'bzalloc' does not exist in the current context
What the hell? I would like to look closer.
OK, looks like the generator generated some redundant functions. Let's add exclude-funcs-with-body
config switch in order to get rid of it.
ClangSharpPInvokeGenerator ` -c multi-file generate-file-scoped-namespaces generate-helper-types exclude-funcs-with-body <# configuration for the generator#> ` --file .\obs-studio\libobs\obs-module.h <# file we want to generate bindings for #> ` --traverse .\obs-studio\libobs\graphics\graphics.h ` -n ObsInterop <# namespace of the bindings #> ` --methodClassName ObsGraphics <# class name where to put methods #> ` --libraryPath obs <# name of the DLL #> ` -o .\TestObsBindings\ObsInterop <# output folder #> ` -r obs_data*=@void* gs_effect*=@void* obs_source*=@void* obs_properties*=@void* obs_missing_files*=@void* vec2=@System.Numerics.Vector2 vec3=@System.Numerics.Vector3 vec4=@System.Numerics.Vector4 profiler_name_store*=@void* text_lookup*=@void* signal_handler*=@void* proc_handler*=@void* obs_data_array*=@void* gs_texture*=@void* input_subsystem*=@void* #... excluded other header files for brevity
We finally finished. The bindings are ready.
Build succeeded.
0 Warning(s)
0 Error(s)
Final result
We got 139 files generated. Even though the process was meticulous, the time savings are overwhelming. We could not possibly have created 139 files of valid interop code in such a short time.
> (ls .\TestObsBindings\ObsInterop\).Count 139 # 139 files
Final generation script
ClangSharpPInvokeGenerator ` -c multi-file generate-file-scoped-namespaces generate-helper-types exclude-funcs-with-body <# configuration for the generator#> ` --file .\obs-studio\libobs\obs-module.h <# file we want to generate bindings for #> ` --traverse .\obs-studio\libobs\obs-source.h ` -n ObsInterop <# namespace of the bindings #> ` --methodClassName ObsSource <# class name where to put methods #> ` --libraryPath obs <# name of the DLL #> ` -o .\TestObsBindings\ObsInterop <# output folder #> ` -r obs_data*=@void* gs_effect*=@void* obs_source*=@void* obs_properties*=@void* obs_missing_files*=@void* vec2=@System.Numerics.Vector2 vec3=@System.Numerics.Vector3 vec4=@System.Numerics.Vector4 profiler_name_store*=@void* text_lookup*=@void* signal_handler*=@void* proc_handler*=@void* obs_data_array*=@void* gs_texture*=@void* input_subsystem*=@void* ClangSharpPInvokeGenerator ` -c multi-file generate-file-scoped-namespaces generate-helper-types exclude-funcs-with-body <# configuration for the generator#> ` --file .\obs-studio\libobs\obs-module.h <# file we want to generate bindings for #> ` --traverse .\obs-studio\libobs\media-io\audio-io.h ` -n ObsInterop <# namespace of the bindings #> ` --methodClassName ObsAudio <# class name where to put methods #> ` --libraryPath obs <# name of the DLL #> ` -o .\TestObsBindings\ObsInterop <# output folder #> ` -r obs_data*=@void* gs_effect*=@void* obs_source*=@void* obs_properties*=@void* obs_missing_files*=@void* vec2=@System.Numerics.Vector2 vec3=@System.Numerics.Vector3 vec4=@System.Numerics.Vector4 profiler_name_store*=@void* text_lookup*=@void* signal_handler*=@void* proc_handler*=@void* obs_data_array*=@void* gs_texture*=@void* input_subsystem*=@void* ClangSharpPInvokeGenerator ` -c multi-file generate-file-scoped-namespaces generate-helper-types exclude-funcs-with-body <# configuration for the generator#> ` --file .\obs-studio\libobs\obs-module.h <# file we want to generate bindings for #> ` --traverse .\obs-studio\libobs\obs.h ` -n ObsInterop <# namespace of the bindings #> ` --methodClassName Obs <# class name where to put methods #> ` --libraryPath obs <# name of the DLL #> ` -o .\TestObsBindings\ObsInterop <# output folder #> ` -r obs_data*=@void* gs_effect*=@void* obs_source*=@void* obs_properties*=@void* obs_missing_files*=@void* vec2=@System.Numerics.Vector2 vec3=@System.Numerics.Vector3 vec4=@System.Numerics.Vector4 profiler_name_store*=@void* text_lookup*=@void* signal_handler*=@void* proc_handler*=@void* obs_data_array*=@void* gs_texture*=@void* input_subsystem*=@void* ClangSharpPInvokeGenerator ` -c multi-file generate-file-scoped-namespaces generate-helper-types exclude-funcs-with-body <# configuration for the generator#> ` --file .\obs-studio\libobs\obs-module.h <# file we want to generate bindings for #> ` --traverse .\obs-studio\libobs\media-io\video-io.h ` -n ObsInterop <# namespace of the bindings #> ` --methodClassName ObsVideo <# class name where to put methods #> ` --libraryPath obs <# name of the DLL #> ` -o .\TestObsBindings\ObsInterop <# output folder #> ` -r obs_data*=@void* gs_effect*=@void* obs_source*=@void* obs_properties*=@void* obs_missing_files*=@void* vec2=@System.Numerics.Vector2 vec3=@System.Numerics.Vector3 vec4=@System.Numerics.Vector4 profiler_name_store*=@void* text_lookup*=@void* signal_handler*=@void* proc_handler*=@void* obs_data_array*=@void* gs_texture*=@void* input_subsystem*=@void* ClangSharpPInvokeGenerator ` -c multi-file generate-file-scoped-namespaces generate-helper-types exclude-funcs-with-body <# configuration for the generator#> ` --file .\obs-studio\libobs\obs-module.h <# file we want to generate bindings for #> ` --traverse .\obs-studio\libobs\graphics\graphics.h ` -n ObsInterop <# namespace of the bindings #> ` --methodClassName ObsGraphics <# class name where to put methods #> ` --libraryPath obs <# name of the DLL #> ` -o .\TestObsBindings\ObsInterop <# output folder #> ` -r obs_data*=@void* gs_effect*=@void* obs_source*=@void* obs_properties*=@void* obs_missing_files*=@void* vec2=@System.Numerics.Vector2 vec3=@System.Numerics.Vector3 vec4=@System.Numerics.Vector4 profiler_name_store*=@void* text_lookup*=@void* signal_handler*=@void* proc_handler*=@void* obs_data_array*=@void* gs_texture*=@void* input_subsystem*=@void* ClangSharpPInvokeGenerator ` -c multi-file generate-file-scoped-namespaces generate-helper-types exclude-funcs-with-body <# configuration for the generator#> ` --file .\obs-studio\libobs\obs-module.h <# file we want to generate bindings for #> ` --traverse .\obs-studio\libobs\obs-interaction.h ` -n ObsInterop <# namespace of the bindings #> ` --methodClassName ObsInteraction <# class name where to put methods #> ` --libraryPath obs <# name of the DLL #> ` -o .\TestObsBindings\ObsInterop <# output folder #> ` -r obs_data*=@void* gs_effect*=@void* obs_source*=@void* obs_properties*=@void* obs_missing_files*=@void* vec2=@System.Numerics.Vector2 vec3=@System.Numerics.Vector3 vec4=@System.Numerics.Vector4 profiler_name_store*=@void* text_lookup*=@void* signal_handler*=@void* proc_handler*=@void* obs_data_array*=@void* gs_texture*=@void* input_subsystem*=@void* ClangSharpPInvokeGenerator ` -c multi-file generate-file-scoped-namespaces generate-helper-types exclude-funcs-with-body <# configuration for the generator#> ` --file .\obs-studio\libobs\obs-module.h <# file we want to generate bindings for #> ` --traverse .\obs-studio\libobs\obs-encoder.h ` -n ObsInterop <# namespace of the bindings #> ` --methodClassName ObsEncoder <# class name where to put methods #> ` --libraryPath obs <# name of the DLL #> ` -o .\TestObsBindings\ObsInterop <# output folder #> ` -r obs_data*=@void* gs_effect*=@void* obs_source*=@void* obs_properties*=@void* obs_missing_files*=@void* vec2=@System.Numerics.Vector2 vec3=@System.Numerics.Vector3 vec4=@System.Numerics.Vector4 profiler_name_store*=@void* text_lookup*=@void* signal_handler*=@void* proc_handler*=@void* obs_data_array*=@void* gs_texture*=@void* input_subsystem*=@void* ClangSharpPInvokeGenerator ` -c multi-file generate-file-scoped-namespaces generate-helper-types exclude-funcs-with-body <# configuration for the generator#> ` --file .\obs-studio\libobs\obs-module.h <# file we want to generate bindings for #> ` --traverse .\obs-studio\libobs\obs-service.h ` -n ObsInterop <# namespace of the bindings #> ` --methodClassName ObsService <# class name where to put methods #> ` --libraryPath obs <# name of the DLL #> ` -o .\TestObsBindings\ObsInterop <# output folder #> ` -r obs_data*=@void* gs_effect*=@void* obs_source*=@void* obs_properties*=@void* obs_missing_files*=@void* vec2=@System.Numerics.Vector2 vec3=@System.Numerics.Vector3 vec4=@System.Numerics.Vector4 profiler_name_store*=@void* text_lookup*=@void* signal_handler*=@void* proc_handler*=@void* obs_data_array*=@void* gs_texture*=@void* input_subsystem*=@void*
Conclusion
ClangSharp is a useful tool to generate .NET interop bindings. It saves a bunch of time and effort. However, there are lots of caveats where you need to understand interop and C code in order to work with them. The ones I would point out are:
- 'opaque' types (e.g. typedef struct type type_t)
- knowing which headers to generate
- knowing what to use as a 'root' header (-f param) and what to use as the actual generation target (-t param).
- knowing which .NET types we can reuse (that have the same structure and layout).
- figuring out the configuration for the generation tool (e.g. invalid C# function bodies).