views:

130

answers:

3

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?

+2  A: 
Richard
What will adding the #pragma help me with? And is there any way that I can put all of the marshalling in the C++/CLI project, so that every project referencing it doesn't have to re-declare the equivalent .NET structs/classes?
Siyfion
@Siyfion: `#pragma managed` allows switching in and out of managed code. See http://msdn.microsoft.com/en-us/library/0adb9zxe(VS.100).aspx
Richard
I understand that, but it works currently, so I can't see what adding that will enable me to do!?
Siyfion
+2  A: 

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.

Hans Passant
So would this mean that I create the managed_api_struct in the C++/CLI *.dll or in the C# code itself? Also, I thought that you could pass a managed struct to the native code so long as you utilized the StructLayout attribute?
Siyfion
It doesn't matter, but C++/CLI makes sense to avoid circular dependencies. Yes, [StructLayout] works but you *must* use a Marshal::StructureToPtr() call. The layout of a managed structure is not predictable.
Hans Passant
Ok thanks, I'll give that a go now, see where I get too.
Siyfion
I've added an additional "EDIT:..." in the question, which I'd appreciate if you could give me your thoughts? Thanks!
Siyfion
You cannot in general use unions in managed code, it is a fundamentally unsafe construct. There is no need to, the managed struct does not have to resemble the unmanaged one.
Hans Passant
+2  A: 

Yuo are trying to do this way more complicated that it really is. What you want is two different structs. One managed and one unmanaged. You expose the managed version externally (to your C# app). It will be all ".Net-ish" with no concepts of unions or so.

In your bridge you receive the managed version of the struct, manually create the unmanaged struct and write code to move your data, field by field over to the unmanaged struct. Then call your unmanaged code and finally move the data back to the managed struct.

The beautiful thing about C++/CLI is that the managed code also can work with unmanaged code and data (and include the unmanaged .h files).

danbystrom