views:

3077

answers:

8

I have an unmanaged C++ library. I would like to expose the functionality for .NET applications. There's one partucular function I am not sure how to handle:

typedef void (free_fn*) (void*); void put (void *data, free_fn deallocation_function);

The idea is that you pass dynamically allocated buffer to the function and supply a deallocation function. The library will process the data asynchronously and will release the buffer later on when data is no longer needed:

void *p = malloc (100); ... fill in the buffer... put (p, free);

How can I expose this kind of thing to .NET applications?

A: 

You'd have to have managed wrappers for the functions themselves (or unmanaged wrappers if you want to pass in managed functions). Or else, treat the unmanaged function pointers as opaque handles in the managed world.

Eclipse
+2  A: 

In general, .NET consumers of your library won't be passing dynamically created arrays to your functions. As far as I know, all containers in .NET are garbage collected.

Regardless, you will need to make a managed wrapper for your unmanaged code. There are many tutorials and articles on this, here is one to start with.

When writing .NET wrappers for unamanged code, I've found that you want to concentrate more on preserving functionality than on making every function accessible in .NET. In your example, it may be better to just have the managed wrapper copy the array into unmanaged memory and perform whatever operations you need to inside the library. This way you don't have to do any pinning of managed memory or Marshalling of managed to unmanaged memory in order to circumvent the .NET runtime's garbage collection. However, how you implement the managed wrapper really depends on what the purpose of that function is.

If you really want to implement this function for function in .NET, you will need to look at the Marshal class in .NET for taking control of managed memory in unmanaged code.

For your callback function, you will first need to create .NET delegates that can be assigned in managed code. You will then need to make an unmanaged free function internal to your library that is called by the unmanaged version of the put function. This unmanaged free function will then be responsible for calling the managed delegate, if the user assigned one.

bsruth
+3  A: 

Be very careful when you do this. .NET really, really wants to have its objects be pinned on the way into an unmanaged routine and unpinned on the way out. If your unmanaged code holds onto a pointer value, that had been pinned on the way in then there is very real chance that the memory will be moved or garbage collected or both.

This is especially the case with delegates marshalled to function pointers (trust me on this - I found that marshaled delegates were being garbage collected on me - I had people at Microsoft verify that for me). The ultimate solution to this problem is to stash away copies of your delegates in a static table paired with a unique transaction id, then create an unmanaged function that when called looks up the delegate in the table via transaction id then executes it. It's ugly and if I had another choice, I would've used it.

Here's the best way to do this in your case - since your unmanaged code uses a set it and forget it model, then you should make your API chunkier. Create an wrapper in managed C++ that allocates memory via an unmanaged routine, copies your data into it and then passes it on along with a pointer to an unmanaged deallocator.

plinth
A: 

Take a look at swig It handles a lot of this stuff automatically but not sure if it's advanced enough to help you.

Jared
+1  A: 

You definitely don't want to pin the managed buffer, as trying to deallocate it in unmanaged code seems like the shortest route to madness. If you can't rewrite this portion in fully managed code, your best bet is either going to be making a copy of the data in the wrapper, or completely hiding the buffer management from the managed world.

If you had the guts (and the masochistic stamina) you could pin the buffer in the wrapper, then pass in the marshaled delegate of a managed function that unpins the buffer. However, I wouldn't suggest it. Having had to do a couple of managed wrappers has taught me the value of exposing the absolute minimum unmanaged functionality, even if it means you have to rewrite some things in managed code. Crossing that boundary is about as easy as going from East Germany to West Germany used to be, to say nothing of the performance hits.

Lee Crabtree
A: 

Since you mentioned it was asyncronous, I'd do it this way. The .Net exposed function only takes the data but doesn't take a delegate. Your code passes the pinned data and a function pointer to a function that will simply unpin the data. This leaves the memory cleanup to the GC, but makes sure the it won't clean it up till the asyncronous part is done.

Joel Lucsy
+1  A: 

Most replies suggest that the data should be copied from managed buffer to unmanaged buffer. How exactly would you do that? Is following implementation OK?

void managed_put (byte data_ __gc[], size_t size_)
{
    //  Pin the data
    byte __pin *tmp_data = &data_[0];

    //  Copy data to the unmanaged buffer.
    void *data = malloc (size_);
    memcpy (data, (byte*) tmp_data, size_);

    //  Forward the call
    put (data, size_, free);
}
In general, you wouldn't pass byte data_ __gc[] from a .NET application. It would be a System.Array. See: http://msdn.microsoft.com/en-us/magazine/cc301755.aspx for more information on arrays in .NET.
bsruth
Ok, good, but what about the code itself? Is it OK? Is the pinning needed at all? Will malloc allocate unmanaged memory even though used in managed context? Etc.
Without testing it, the code looks OK.
bsruth
A: 

Some of the previous poster's have been using MC++, which is deprecated. C++/CLI is far more elegant of a solution.

The BEST, technique for interop, is implicit interop, not explicit. I dont believe anybody has commented on this yet. However, it gives you the ability to marshal your types from managed<->native where if you make a change to your type definition or structure layout, it will not result in a breaking change (which explicit interop does).

This wikiepedia article documents some of the differences and is a good starting point for further information.

P/Invoke (explicit and implicit)

Also, the site marshal-as.net has some examples and information as to this newer method (again, more ideal as it will not break your code if the a native struct is re-defined).

RandomNickName42