18

I'm writing a software in c# which needs to call many times and by many threads a function in a c++ unmanaged dll.

I have a C++ file like that:

// "variables" which consist in some simple variables (int, double) 
//  and in some complex variables (structs containing arrays of structs)


extern "C"
{
     __declspec(dllexport) int function1()
    {
        // some work depending on random and on the "variables"
    }
}

and a C# class like that

public class class1
{
    //  "variables" <--- the "same" as the C++ file's ones 
    //  Dll import <--- ok

    public void method1()
    {
        int [] result;

        for(int i=0; i<many_times; i++)
        {
            result = new int[number_of_parallel_tasks];                

            Parallel.For(0, number_of_parallel_tasks, delegate(int j)
            { 
                 // I would like to do  result[j] = function1()  
            });

            //  choose best result
            //  then update "variables" 
        }
    }

}

I wrote "I would like to do ..." because the c++ function needs at each round to have the "variables" updated, too.

My question is:

Is it possible to share memory between C++ and C# in order to avoid passing reference every time? Is it just a waste of time?

I read about memory mapped files. Could they help me? However, do you know more appropriate solutions?
Thank you very much.

888
  • 3,246
  • 8
  • 41
  • 60

3 Answers3

25

There is no problem sharing memory between C# and C++ using P/Invoke once you know how it works. I would suggest reading about marshaling in MSDN. You might also want to read about using the unsafe keyword and fixing memory.

Here is a sample that assumes that your variables can be described as a simple struct:

In C++ declare your function as follows:

#pragma pack(1)
typedef struct VARIABLES
{
/*
Use simple variables, avoid pointers
If you need to use arrays use fixed size ones
*/
}variables_t;
#pragma pack()
extern "C"
{
     __declspec(dllexport) int function1(void * variables)
    {
        // some work depending on random and on the "variables"
    }
}

In C# do something like this:

[StructLayout(LayoutKind.Sequential, Pack=1)]
struct variables_t
{
/*
Place the exact same definition as in C++
remember that long long in c++ is long in c#
use MarshalAs for fixed size arrays
*/
};

[DllExport("YourDll.dll", CallingConvention=CallingConvention.Cdecl)]
static extern int function(ref variables_t variables); 

And in your class:

variables_t variables = new variables_t();
//Initialize variables here
for(int i=0; i<many_times; i++)
{
    int[] result = new int[number_of_parallel_tasks];
    Parallel.For(0, number_of_parallel_tasks, delegate(int j)
    { 
          result[j] = function1(ref variables)  
    });

    //  choose best result
    //  then update "variables" 
}

You can use more complex scenarios like allocating and releasing the structure in c++, using other forms of marshaling to get the data back like building your own class to read and write directly to the unmanaged memory. But if you can use a simple struct to hold your variables the method above is the simplest.

EDIT: Pointers on how to handle correctly more complex data

So the sample above is in my opinion the correct way to "share" data between C# and C++ if it is simple data eg. a structure holding primitive types or arrays of fixed size of primitive types.

This being said there are actually very little limitations on the way you can access memory using C#. For more information look into the unsafe keyword, the fixed keyword and the GCHandle struct. And still if you have a very complex data structures that contain arrays of other structures etc. then you have a more complicated job.

In the case above I would recommend moving the logic on how to update the "variables" into C++. Add in C++ a function to look something like this:

extern "C"
{
     __declspec(dllexport) void updateVariables(int bestResult)
    {
        // update the variables
    }
}

I would still suggest not to use global variables so I propose the following scheme. In C++:

typedef struct MYVERYCOMPLEXDATA
{
/*
Some very complex data structure
*/
}variables_t;
extern "C"
{
     __declspec(dllexport) variables_t * AllocVariables()
    {
        // Alloc the variables;
    }
     __declspec(dllexport) void ReleaseVariables(variables_t * variables)
    {
        // Free the variables;
    }
     __declspec(dllexport) int function1(variables_t const * variables)
    {
        // Do some work depending on variables;
    }
    __declspec(dllexport) void updateVariables(variables_t * variables, int bestResult)
    {
       // update the variables
    }
};

In C#:

[DllExport("YourDll.dll", CallingConvention=CallingConvention.Cdecl)]
static extern IntPtr AllocVariables();
[DllExport("YourDll.dll", CallingConvention=CallingConvention.Cdecl)]
static extern void ReleaseVariables(IntPtr variables); 
[DllExport("YourDll.dll", CallingConvention=CallingConvention.Cdecl)]
static extern int function1(IntPtr variables); 
[DllExport("YourDll.dll", CallingConvention=CallingConvention.Cdecl)]
static extern void updateVariables(IntPtr variables, int bestResult); 

If you still want to maintain you logic in C# you will have to do something like the following: Create a class to hold the memory returned from C++ and write your own memory access logic. Expose the data to C# using copy semantics. What I mean is as follows, Say you have in C++ a structure like this:

#pragma pack(1)
typedef struct SUBSTRUCT
{
int subInt;
double subDouble;
}subvar_t;
typedef struct COMPLEXDATA
{
int int0;
double double0;
int subdata_length;
subvar_t * subdata;
}variables_t;
#pragma pack()

in C# you do something like this

[DllImport("kernel32.dll")]
static extern void CopyMemory(IntPtr dst, IntPtr src, uint size);

[StructLayout((LayoutKind.Sequential, Pack=1)]
struct variable_t
{    
    public int int0;
    public double double0;
    public int subdata_length;
    private IntPtr subdata;
    public SubData[] subdata
    {
        get
        {
             SubData[] ret = new SubData[subdata_length];
             GCHandle gcH = GCHandle.Alloc(ret, GCHandleType.Pinned);
             CopyMemory(gcH.AddrOfPinnedObject(), subdata, (uint)Marshal.SizeOf(typeof(SubData))*subdata_length);
             gcH.Free();
             return ret;
        }
        set
        {
             if(value == null || value.Length == 0)
             {
                 subdata_length = 0;
                 subdata = IntPtr.Zero;
             }else
             {
                 GCHandle gcH = GCHandle.Alloc(value, GCHandleType.Pinned);
                 subdata_length = value.Length;
                 if(subdata != IntPtr.Zero)
                     Marshal.FreeHGlobal(subdata);
                 subdata = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SubData))*subdata_length);
                 CopyMemory(subdata, gcH.AddrOfPinnedObject(),(uint)Marshal.SizeOf(typeof(SubData))*subdata_length);
                 gcH.Free();
             }
        }
    }
};
[StructLayout((LayoutKind.Sequential, Pack=1)]
sturct SubData
{
    public int subInt;
    public double subDouble;
};

In the above sample the structure can still be passed as in the first sample. This of course is just an outline on how to handle complex data with araays of structures and arrays of structures within arrays of structures. As you can see you will need a lot of copying to guard yourself from memory corruption. Also if the memory is allocated via C++ it will be very bad if you use FreeHGlobal to free it. If you want to avoid copying memory and still maintain the logic within C# the you can write a native memory wrapper with accessors for what ever you want For instance you will have a method to directly set or get the subInt of Nth array member - This way you will keep your copies to exactly what you access.

Another option would be to write specific C++ functions to do the difficult data handling for you and call them from C# according to your logic.

And last but not least you can always use C++ with CLI interface. However I myself do it only if I must - I don't like the lingo but for very complex data you certainly have to consider it.

EDIT

I added the correct calling convention to the DllImport for completeness. Please note that the default calling convention used by the DllImport attribute is Winapi (which on windows translates to __stdcall) while the default calling convention in C/C++ (unless you change the compiler options) is __cdecl.

W1M0R
  • 3,367
  • 3
  • 30
  • 32
Sebastian Cabot
  • 1,812
  • 14
  • 18
  • Thank you for this clear answer. I upvoted it , but I'm not satified at all, yet. Maybe in my question I missed to specify that I already tried with P/invoke, but in this way (if I'm not wrong) I have to pass all the variables (ok, the reference) at each iteration. I was wondering if there is a way to avoid this. – 888 Nov 09 '12 at 10:10
  • 1
    @mcdg There is no problem at all passing the reference to the structure. There is no significant overhead by doing so. In many ways it is much better practice to use some kind of structure or class to hold state. This way you could go multi-threaded and you make your function re-entrant while when using global variables you limit your possibilities. however since you have updated your question with a specification of "variables" I will add to the answer some pointers on how to handle more complex cases – Sebastian Cabot Nov 09 '12 at 14:55
4

The best thing you can do is define your C++ code (which will be unmanaged I assume). Then write a wrapper for it in C++/CLI. The wrapper will provide you with an interface to C# and is the place where you can take care for marchalling (moving data from unmanagerd to managed area's)

PapaAtHome
  • 575
  • 8
  • 25
  • 1
    Why would they use C++/CLI to create the wrapper? You could write the wrapper in C# easily enough. – Security Hound Nov 07 '12 at 18:47
  • 1
    C++/CLI is a bit more flexible in handling all the details, specialy if you want to switch between managed and unmanaged code. C++/CLI, combining .NET and C++, is not 'easy going' if you're at least not used to C++ (but that is only my opinion). Then sticking to C# might be a wiser choice. – PapaAtHome Nov 07 '12 at 21:07
  • I changed a little bit the question to explain myself better. – 888 Nov 08 '12 at 17:32
  • 1
    Your C code will use 'unmanaged' code and memory (native) while your C# code wil use 'managed' code and memory (.NET CLR system) The two cannot be interchanged or referenced in a mixed manor. You need conversion code for it. – PapaAtHome Nov 08 '12 at 20:53
  • 1
    If it is possible to recompile your C code as C++ code as 'managed' then you do not need conversion code. That is one other thing that you can try to do. – PapaAtHome Nov 08 '12 at 20:55
  • Thank you for your hints, but in this case I'm going to keep the dll unmanaged for the performances' sake. – 888 Nov 14 '12 at 14:14
1

One way to avoid having to pass a reference to the variables / data that both the C# and C++ code require is to export two functions from the native DLL. In addition to your function that does the work, provide another function that allows the reference to be passed in and stored to a file scope static pointer that is defined in the same .cpp file as both functions so that is accessible to both.

As you mentioned you could also use a memory mapped file (in this case it could be non-persisted since it never needs to be written to disk).

Josh Heitzman
  • 1,816
  • 1
  • 14
  • 26