Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Exposing a C++ API to C#

So what I have is a C++ API contained within a *.dll and I want to use a C# application to call methods within the API.

So far I have created a C++ / CLR project that includes the native C++ API and managed to create a "bridge" class that looks a bit like the following:

// ManagedBridge.h
#include <CoreAPI.h>
using namespace __CORE_API;

namespace ManagedAPIWrapper
{
    public ref class Bridge
    {
        public:
            int             bridge_test(void);
            int             bridge_test2(api_struct* temp);
    }
}

.

// ManagedBridge.cpp
#include <ManagedBridge.h>

int Bridge::bridge_test(void)
{
    return test();
}

int Bridge::bridge_test2(api_struct* temp)
{
    return test2(temp);
}

I also have a C# application that has a reference to the C++/CLR "Bridge.dll" and then uses the methods contained within. I have a number of problems with this:

  1. I can't figure out how to call bridge_test2 within the C# program, as it has no knowledge of what a api_struct actually is. I know that I need to marshal the object somewhere, but do I do it in the C# program or the C++/CLR bridge?
  2. This seems like a very long-winded way of exposing all of the methods in the API, is there not an easier way that I'm missing out? (That doesn't use P/Invoke!)

EDIT: Ok, so I've got the basics working now thanks to responses below, however my struct (call it "api_struct2" for this example) has both a native enum and union in the C++ native code, like the following:

typedef struct
{
    enum_type1  eEnumExample;
    union
    {
            long        lData;
            int     iData;
            unsigned char   ucArray[128];
            char        *cString;
            void        *pvoid;
    } uData;
} api_struct2;

I think I have figured out how to get the enum working; I've re-declared it in managed code and am performing a "native_enum test = static_cast(eEnumExample)" to switch the managed version to native.

However the union has got me stumped, I'm not really sure how to attack it.. Ideas anyone?

like image 602
Siyfion Avatar asked Apr 09 '10 09:04

Siyfion


Video Answer


1 Answers

Yes, you are passing an unmanaged structure by reference. That's a problem for a C# program, pointers are quite incompatible with garbage collection. Not counting the fact that it probably doesn't have the declaration for the structure either.

You can solve it by declaring a managed version of the structure:

public value struct managed_api_struct {
  // Members...
};

Now you can declare the method as

int bridge_test2(managed_api_struct temp);   // pass by value

or

int bridge_test2(managed_api_struct% temp);  // pass by reference

Pick the latter if the structure has more than 4 fields (~16 bytes). The method needs to copy the structure members, one-by-one, into an unmanaged api_struct and call the unmanaged class method. This is unfortunately necessary because the memory layout of a managed structure is not predictable.

This is all pretty mechanical, you might get help from SWIG. Haven't used it myself, not sure if it is smart enough to deal with a passed structure.

A completely different approach is to make the wrapper class cleaner by giving it a constructor and/or properties that lets you build the content of an api_struct. Or you could declare a wrapper ref class for the structure, much like you would in managed code.

like image 155
Hans Passant Avatar answered Sep 30 '22 17:09

Hans Passant