C++ Interop - writing a shim to use an existing c++ library [Answered]
So I'm currently trying to make a shim for a C++ library that I can then call from C#.
The original library uses CMake, not MSBuild, so I've set up Cmake, let it generate the .vcxproj files, made a 2nd C++ project for my shim classes and made it depend on the generated projects.
Then I've added that + the generated projects into my C# msbuild solution.
Now I am trying to call a hello world function that I made in my shim library form C#, but that immediately fails with no indicator as to what went wrong. all I get is
System.Runtime.InteropServices.SEHException: 'External component has thrown an exception.'
.
How I declared it on the C++ side:
How I declared it on the C# side:
any help appreciated!71 Replies
dumb question, have you implemented it in the c++ file or only declared?
The part I posted is my header file, I've implemented it in a corresponding source file. All it does is print hello world tho, so I omitted that part from the post
have you tried another calling convention such as __cdecl?
yes, same result.
What's actually in your
CLIP_Hello
function? I know you think it's unimportant, but it does look like it's throwing an exception...a single
printf("Hello world");
, but I've tried without that (empty method) and that didn't change anythingWhat happens if you remove the C++ library altogether, and just call an empty method in the shim library (although it isn't shimming anything any more)
this is an empty method in the shim library
"Then I've added that + the generated projects into my C# msbuild solution." -- what do you mean by "added" exactly?
Right, but is the shim library linked against the original library at all?
solution --> add --> existing project
yes
Right, so I'm saying to try it without that linking
Wait, are you trying to do p/invoke or C++/cli? If you're doing p/invoke, the native dll doesn't get added to your .NET project at all (well, you can add it as a 'None' file type, and set it to copy to the output directory, to make things easier). The DLL just has to be in the bin/ directory, next to the compiled .NET assembly
p/invoke, and I have made visual studio add it to the .net project
ok so.. one thing at a time:
You added it as a file to the .NET project, or as a project to the .NET solution? You said one, and then the other
what do you mean "added"
clip
is the native library I am trying to use. problem: it uses CMake. so setting that up it generated vcxproj files for clip
, ZERO_CHECK
, ALL_BUILD
and a bunch of test stuff that I have ignored and not imported.
ClipShim
is my shim library, that depends on clip
.added a project to the solution, made my C# project depend on it
"made my C# project depend on it" -- how, exactly?
.NET assemblies can't depend directly on native assemblies
build dependencies --> project dependencies --> checked the ClipShim project
Oh right, just as a build dependency
now when I build my C# project, it also compiles the shim library
Does the shim library DLL actually end up in your .NET project's bin folder?
yes, let me get to that xD
next up, in the shim project's properties (Configuration Properties --> Advanced), I set it to copy references + debug symbols to the out dir.
now when I build (also verified it works with publishing to folder), I get a ClipShim.dll (and pdb) file next to my C# exe file
And ClipShip dynamically links Clip? Or statically links?
where do I check that again? (sorry not too well versed with vcxprojects)
Is Clip a .dll or .so?
source, gets compiled when building ClipShim
What does it get compiled to?
dll afaik
OK, so dynamically linked. Is the Clip dll present in the bin folder of your .NET application?
no
Well, that's one problem
(that's related to why I asked you to try removing the dependency from ClipShim to Clip, and see whether that changed anything)
if I enable the same copy behavior as I did above for the shim project, I get the following issue:
1) it produces a .lib file just fine
2) it fails at trying to copy
<solution dir>\x64\Debug\ZERO_CHECK
because that doesn't existForget about the build dependency stuff for now, and just manually copy the dlls into the right place
Once that's working, you can work backwards and fix each subsequent problem as you come to it. You'll be starting from a working base, which makes it a lot easier
removed the
clip
dependency alltogether, but still get the same exception as I did initially, with it unble to call into the shim dllGreat, it's good to narrow things down
Can you check the full list of compiler flags for Shim, and see whether /clr is in there anywhere?
you're probably asking for this?
Right, so you are building a C++/CLI library. I did ask...
sorry.. this is all very unfamiliar territory
You're trying to combine two worlds. C++/CLI projects are C++ but compiled into IL (or at least, a mix of native code and IL), and they run on the .NET runtime. You can reference a C++/CLI project from a C# project in the normal way -- as far as C# is concerned, it's a normal .NET assembly which it can just call into
Native C++ projects don't run on the .NET runtime and don't have any IL in them, and C# interops with those by using p/invoke
ok so..
given that the
clip
library itself is NOT a .net library, what should I do from here?So, pick one. Either compile the project as a native C++ library and p/invoke, or compile it as C++/CLI and reference it in the way you'd reference any other .NET assembly (without using pinvoke at all)
C++/CLI projects can reference normal C++ projects -- it's what makes them really useful for interop
ohhh I see
so what changes would I make to my declarations? (if I go the C++/CLI route)
I didn't think .NET Core had good support for C++/CLI, but looking at that setting, that might have changed. C++/CLI is more powerful -- you get to use objects, the C++ code can use .NET types, etc, and you don't have to flatten everything down into pointers and C function calls
If you're going the proper C++/CLI route, your shim library declares .NET classes but with using C++ syntax. It's slightly odd until you get used to it. It would define .NET classes using
ref class SomeType
etc, and reference other .NET types using managed pointers (^
pointers) and gcnew
you see this all started out because apparently nobody ever bothered to support more than text in clipboards on .net 😅
and while I could just p/invoke 2 function calls for windows, that expands into 1k+ lines on linux very quickly...
But those types compile to IL, and can be used directly from your C# library
If this needs to work on linux, just check what the C++/CLI story is there. I'm not sure what support is like
linux support was the main reason I'm doing this, yeah. (again, on windows it'd be 2 simple p/invoke calls)
If you need linux support, you probably don't want to be building the C++ shim library with VS to begin with 😛
well, msbuild
that does work on linux as far as I know
Well, MSVC is the relevant bit
Which is a windows C++ compiler
heh..
Yeah, if this all needs to run on linux you're looking at p/invoke, and probably looking to build your shim library with gcc/clang
well msvc works on linux just fine (once you manage to install it)
but yeah I got both gcc and clang set up on here so might as well use one of those
I'm surprised by that. You're not running it in wine or something are you?
no, I'm currently on windows
As in, when you say "well msvc works on linux just fine"
I've had to use it for a project before and made it work
in a docker container on linux host, that is
but yeah it may have used wine under the hood, don't remember exactly, been over a year
Did you make it work in wine, or natively? If it was using wine, then the compiled dll will probably only have worked under wine...
https://docs.microsoft.com/en-us/cpp/linux/download-install-and-setup-the-linux-development-workload?view=msvc-170 suggests good support for linux, but that says it's using gcc/clang, and it's a fairly recent article
So I still don't think MSVC can build native linux binaries
the pipeline was set up to use that linux container to produce both windows + linux binaries, I used the windows ones, other dev used linux. idk whether that was with or without wine
Either way, I'm pretty sure you want to be building with gcc/clang here. And that'll definitely give you a native binary, not a C++/CLI one
then p/invoke should work fine
yeah uh.. I'm gonna recreate the shim project from scratch, I somehow managed to kill VS just now 😅
what template would be best for this?
(for the shim)
I haven't tried to do a GCC/clang project in VS before. The link I posted above looks decent, otherwise for something quick and dirty I'd just avoid VS altogether
wouldn't have any issues with that, I prefer rider.
thing is, I still want it to be set up cleanly so that building the application automatically builds the shim, and building the shim automatically builds the cpp lib
problem is all the "linux" workflow stuff expects a remote environment to compile on, and while I could set that up, I don't want to, given that I already have all the necessary toolchains right on my windows system, don't need them on WSL
Tbh I'd reach for another build system, and make that call
dotnet build
aaand here we go again.
made a new DLL project (gonna see about the toolchain later as it right now only lets me choose a nonexistent clang-cl or visual studio msvc).
removed the autogenerated precompiled header stuff because I don't want that right now.
now building the solution succeeds, however the shim library builds to a different folder and I can't get it to copy to the proper build dir.
and launching my c# project is no longer possible because it now tries to execute the shim dll, which is of course not possible (urgh it had automatically selected the shim as startup project, fixed..)
ok ended up hardcoding the output path, but eyyy seems to work now :D
so I can call into my shim library just fine now;
however as soon as I try to depend on
clip
, I'm getting linker errors for not being able to resolve external functions.
with MSVC:
with clang-cl:
Looks like you're not linking whatever library provides those functions?
possibly, but that seems to be some windows builtin stuff?
yeah.. it's in
Shlwapi.h
, which is part of the windows SDKYep, which you'll need to link
intellisense literally finds it and thinks it's valid, but the compiler doesn't
It'll be getting that from the header: that's the only place which holds the signature info anyway. It's not until the linker kicks in that it realises it can't actually find a symbol with that name anywhere
This is fairly fundamental c/c++ stuff - you'll get better help on the c/c++ discord I think
✅ This post has been marked as answered!