P/Invoke or C++/CLI for wrapping a C library

11,655

Solution 1

In the case where I am working with an existing C library, I prefer PInvoke. PInvoke, while a bit tedious and troublesome at times, is a fairly well understood technology that has an ever growing set of tools and internet documentation available. Generally speaking, whatever problem you run into, there is already a sample available on the web, or a quick check on stack overflow will provide a solution.

C++/CLI is a great technology, but IMHO its documentation is limited as compared to PInvoke for interop specific scenarios. It also doesn't have the tooling infrastructure for interop solutions that PInvoke has. Adding a C++/CLI assembly for a scenario that can be solved with PInvoke just seems too costly to me.

On the other hand, if I'm working with a large C++ library, I consider C++/CLI a bit more. PInvoke does not work with C++, and I must end up adding some kind of intermediate layer. Either a small C layer to wrap all of the C++ function calls or a C++/CLI library to bridge the gap. C++/CLI feels a bit more natural to me in this case.

Solution 2

It depends in large part how memory ownership is handled. P/invoke can marshal pointers only when memory management is one of a couple particular ways, usually caller-allocated buffers. If your API returns pointers (via return value or out parameter, doesn't matter) and expects them to be handed back to a destruction function later... p/invoke will either do the automatic marshaling or give you direct access to the pointer value you need to send back later, never both. So C++/CLI becomes a very desirable approach in that case.

Solution 3

Think of P/Invoke as platform invocation, are you calling into something like the Win32 API which is very P/Invoke friendly or do you need to provide .NET bindings for an unmanaged library?

Since the wrapper typically is very thin a C++/CLI wrapper doesn't necessitate that you know C++/CLI specifically. What you need to know can be found in the language specification, it's an extensive documentation with lots of examples. P/Invoke is more of an nice to have feature for smaller well established existing libraries, but if the interface for calling into that library changes you'll run into a problem. With C++/CLI you can still have a public managed interface in your C++/CLI project that's exposed for managed code and handle changes to the C API more easily that way.

If you wanna get rid of the extra DLL you can always try ILMerge, but I'm not sure if it's capable of handling mixed assemblies, (apparently not), but it looks like it's possible to link both managed and unmanaged *.obj files with the PlatformSDK linker like this:

cl.exe /MD /c /clr Unmanaged.cpp
csc.exe /target:module /addmodule:*.obj Managed.cs
link.exe /DLL /LTCG /NOENTRY /CLRIMAGETYPE:IJW *.obj Managed.netmodule

Solution 4

C++/CLI will be easier to debug if you have personnel who know how to debug C++. If you don't, it could potentially be much harder.

I would suggest that the tradeoff here is between ease of use for the consumers of your interop assembly versus ease of maintainability for the assembly itself. If you have a solid core of senior engineers who are familiar with C++ and who can reliably maintain the assembly, it will be much easier on the rest of your team who are unfamiliar with native code to give them a fully managed interface that takes care of everything for them.

On the other hand, if you're the only person in the shop with C++ experience, I would be very leery of embedding a C++ module into the project, in case someone else has to maintain it later.

Solution 5

For an API of this size (~40 total entry points), I would draw the dividing line between C++/CLI and P/Invoke based on how much "header file gunk" you must duplicate in C#. If it's a small (to modest) amount, P/Invoke is fine. Once you start duplicating a lot of .H files in C#—especially for things that aren't exposed in your .NET API—you might be better off using C++/CLI.

Share:
11,655
Ian G
Author by

Ian G

Running to catch up with all the cool stuff out there, interspersed pauses for good food and nice wine.

Updated on June 03, 2022

Comments

  • Ian G
    Ian G almost 2 years

    We have a moderate size (40-odd function) C API that needs to be called from a C# project. The functions logically break up to form a few classes that will comprise the API presented to the rest of the project.

    Are there any objective reasons to prefer P/Invoke or C++/CLI for the interoperability underneath that API, in terms of robustness, maintainability, deployment, ...?

    The issues I could think of that might be, but aren't problematic are:

    • C++/CLI will require a separate assembly; the P/Invoke classes can be in the main assembly. (We've already got multiple assemblies, and there'll be the C DLLs anyway, so not a major issue.)
    • Performance doesn't seem to differ noticeably between the two methods.

    Issues that I'm not sure about are:

    • My feeling is C++/CLI will be easier to debug if there's an interop problem; is this true?
    • Language familiarity - enough people know C# and C++, but knowledge of details of C++/CLI are rarer here.

    Anything else?

  • Ian G
    Ian G about 14 years
    Interesting, I didn't know you could link C# and C++/CLI obj files into the same assembly. Though in this case adding one assembly to the 15+ we already have won't be a problem. Thanks.
  • Ian G
    Ian G about 14 years
    The majority of the current developers are familiar with C++ though only two or three have written any quantity of C++/CLI.
  • Ian G
    Ian G about 14 years
    I'd have to double check but I think in this case the C library uses caller-allocated buffers everywhere.
  • Ian G
    Ian G about 14 years
    The C API is indeed used elsewhere and so will be staying around regardless of the choice made for this project.