30

I want to code a little program that takes in head tracking data and moves a 3D object accordingly on the screen. To achieve this I found a software called opentrack that has a C++ API. The problem is that any game dev environments I know / have a way to access use C# as the language.

I'm very confortable with C# and used to be comfortable with C++ and C a while back, and could easily get back into it if a solution required it.

This is a silly little personal project, but one I'm passionate about and would love to solve, so any help in resolving this would be appreciated. Thanks!

UPDATE:

Wow, that's an amazing amount and quality of responses, I would like to deeply thank everybody who contributed!

  • 15
    C++/CLI, formerly known as Managed C++ – whatsisname Mar 9 at 3:10
  • 1
    @whatsisname It's more like a favorite sickly cousin was called managed C++, as C++/CLI was created to fill the hole Managed C++ didn't quite succeed in filling. – Deduplicator Mar 9 at 12:57
44

There are various ways to call native code from c#

  1. P/Invoke - allows c-style method calls directly from c# code. If the API does not expose c-style (i.e. extern C) functions you will need a wrapper that does this. If the API uses any kind of objects this will probably be an painful approach.
  2. C++/CLI - This allows you to use .Net types in a c++ project. So you would create a wrapper c++ project that interfaces with the opentrack API, and is called from your regular c# code. This looks like a nice guide on how to do this.. An advantage of this is that it allows you to write wrappers around objects to provide a object oriented API.
| improve this answer | |
  • 8
    P/Invoke can be used IFF you create an 'extern C' wrapper around the C++, either way you need a wrapper somewhere – jk. Mar 9 at 8:09
  • 11
    In my experience, using P/Invoke is a real pain when it comes to memory handling, and is not well suited at all to an object-oriented style. C++/CLI has a slightly weird syntax, but works fairly well once you're used to it. – Richard Metzler Mar 9 at 12:30
  • 5
    You can PInvoke non-extern methods, but name mangling makes it a somewhat miserable experience. – VisualMelon Mar 9 at 22:30
  • 1
    Just as a note - C++/CLI support is not quite there yet for Linux, though Microsoft is working on the problem for .NET Core 3.1: devblogs.microsoft.com/cppblog/… – Tyzoid Mar 10 at 18:24
12

For object-oriented APIs, you should consider using a tool which generates a .NET wrapper automatically. Check out CppSharp over on GitHub. Not everything will work automatically but if you are going to wrap 100 classes, it makes sense to generate the bulk of the code needed for interoperation.

Whether you are just wrapping a few classes or en entire SDK and whether you write your own C++/CLI interoperability code, it is always good practice to have the C++/CLI code in a separate assembly since you will deal with unmanaged code and unmanaged code debugging which you WANT to centralize in a single component.

| improve this answer | |
4

You sure can, and I've done so on both Windows and Mac using .NET Core 3.1, but it will most likely work with .NET Framework as well with minimal modification.

The method I used is called DllImport and is describe here: https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.dllimportattribute?view=netframework-4.8#examples

Below is an example from my own C# code.

    class Program
    {
        // This is where you declare the functions from the C file that you want to use. The filename is a relative path to the file containing the `Program`
        [DllImport("libHello.dll")]
        static extern string say_hello(byte[] buffer);

        static void Main()
        {
             var buffer = new byte[1024]; // create byte buffer
             say_hello(buffer) // call the C function
             Console.WriteLine(Encoding.ASCII.GetString(buffer)) // 'hello'
        }

The most important part is to ensure that the C library that you're using is compiled for the platform you're running on.

If you'll be passing large data back to the C# program, you might think about using a library called cJSON and passing a C struct as JSON to the the buffer, that you can then deserialize into C# class instances. That's how I did it, and it really keeps the C complexity down since you don't really need to worry about freeing heap space from the C# code.

Please note that I accomplished the above using non-Microsoft tooling for C development (of course I did use .NET Core and CLI for the C# stuff). I don't particularly like Visual Studio and Visual C++ and that is my own bias.

| improve this answer | |
0

SWIG - Simplified Wrapper and Generator is the only sane way to do stuff like this. It looks like CppSharp mentioned by @helb is built on top of it. So go for CppSharp, there is no need to reinvent the wheel.

I am pointing SWIG out, not just for completeness but because in some cases it may be simpler. I have no experience with CppSharp though.

As for C++/CLI, I won't start a new project with it. Not even a "Hello World" project! I am saying this as someone who wasted a lifetime on Microsoft Technologies like WCF, WPF, WinForms, OData, and more. I may be wrong but I just won't bet a stone on it. Yes, I have read this and I am still not convinced it will make it to the final wrap up of .Net.

| improve this answer | |
-4

Having been down the road of C++/CLI and the pitfalls of having managed code marry up well with unmanaged code I can't recommend enough avoiding the approach of having C++ and C# binaries directly linked. Principally because of the garbage collection, it's hard to ensure a C# object is destroyed, in the sense that it is really destroyed, and will not interact with C++ objects that you can guarantee have been destructed, that being the virtue of an unmanaged language.

What you may find less problematic, albeit with its own overheads, is to have a process for each language runtime and then to setup a websocket protocol between them. For example, you could use Websocket4Net for C# and Beast for C++.

| improve this answer | |
  • 2
    I am curious about the problems you have encountered. If the .Net wrapper owns the c++ object and has proper dispose/finalize handling, there should not be any risk of accessing disposed c++ objects, right? – JonasH Mar 9 at 15:04
  • 1
    Why websocket as opposed to just regular TCP? Seems needlessly roundabout to have HTTP on top if all you need is a stream anyway. – Cubic Mar 9 at 15:21
  • 1
    Ideally, the C++/CLI part should do little more than solder the bits between the two different environments. If you're using a solid API that is well tested and stable, I don't see how any of this could be a problem. – T. Sar Mar 9 at 15:53
  • 1
    At my last company, we had a C++ MFC application that was over a million lines of code. I was able to replace the MFC UI with C#/WPF and successfully re-use over 90% of the C++ code. In order to mix the deterministic nature of C++ destructors with the non-deterministic nature of garbage collection, you just need to make extensive use of IDisposable. – 17 of 26 Mar 9 at 19:14
  • 6
    @Benedict If you're relying on the GC for cleanup in this case, that's exactly your problem. The correct approach is to use IDisposable. The challenge as always with C++ code is to make sure you get the ownership right (generally you want a C# object to own the native code/objects and encapsulate all its functionality). – Voo Mar 10 at 8:49

Not the answer you're looking for? Browse other questions tagged or ask your own question.