views:

333

answers:

3

Hi, My application consist of C# code with unmanaged C dll calls. In my C# code I have an object/class where its properties are both system types such as string and int and other objects I have defined.

I would like to pass this complex (Graph.cs) object to my C (dll) code, what implementation would you suggest here?

I have tried moving structs but I fail to do so with anything other then string and int.

Thanks.

Code:

public Class Grpah {

    TupleCollection m_TupleCollection;
    int m_nGeneralIndex;       
    bool m_bPrintWRF;
    string m_sLink;  
}

public Class TupleCollection {

    IList<Tuple> _collection;

}     

public Class Tuple {

    Globals.TupleType m_ToppleType;        
    ArrayList m_Parameters;

}

public class TupleArgs {

    public string Value { get; set; }       
    public Globals.PAS PAS;
    public RefCollection RefCollection { get; set; }
    public int Time{ get; set; }      

}

public class RefCollection {

    public List<int> SynSet{ get; set; } 
    public Globals.PAS PAS;

}
+5  A: 

Try:

How to: Marshal Structures Using PInvoke

I think the easiest way for you to make progress is to modify the native code, giving it the ability to work with CLR types.

Now, you're almost certainly using Visual Studio, and hopefully it's VS2005 or later. This means that although your existing native code is in C, you have the option to delve into a little C++. And not only that - you also have C++/CLI.

So I would make a new C++/CLI DLL, and link your C library to it, so that it can call into the C code. Then write a thin translation layer in the C++/CLI library: it will expose true CLR classes (written with ref class) and will call onto the native C code.

e.g. in a C header:

void do_something_with_foo_data(int a, int b, int c);

In C++/CLI:

public ref class FooWrapper
{
    static void DoSomethingWithFoo(Foo ^foo)
    {
        // pick apart the Foo class and pass the pieces on to the C function

        do_something_with_foo_data(foo->A, foo->B, foo->C);
    }
};

In C#:

public class Foo
{
    public int A { get; set; }
    public int B { get; set; }
    public int C { get; set; }
}

...

var foo = new Foo { A = 1, B = 2, C = 3 };
FooWrapper.DoSomethingWithFoo(foo);
Daniel Earwicker
Thanks, in this example they use only simple data type such as int which I can handle, how about C# List data type? or ListArray.
You can't handle managed structures from unmanaged code. You need to convert those structures into unmanaged structures and pass those. This answer is the most valid one that I've seen here.
Dave Van den Eynde
+1 I like this approach!
Iain Collins
Yes, this will work, but remember that you still have to convert the objects into plain old C structs somewhere. You example is simple enough, but in a non-trivial case, I can really say if its easier to do in C++ than it is directly in C#. It seems to be about the same amount of fiddling in both cases though, but you dont have to involve yet another language in the C#->C approach.
Martin Wickman
In the C#->C you actually do have to write in another language: interop attributes. The major advantage I've found with a C++/CLI layer is that you can step through it in the debugger, so you can literally see the conversion taking place and immediately find out what's going wrong. With interop, *if you know what to write* it's short and simple, but the problem is working out what to write.
Daniel Earwicker
A: 

Your model looks pretty complicated, and incomplete: Tuple.m_parameters is an ArrayList, so it can contain just about anything, and the Globals.PAS type is not defined here.

Perhaps you should think of a different strategy: in your DLL, make a small model that contains whatever you need in your C code, and make it as simple as possible (but not simpler!).

Then, learn whatever you need to marshal that C model from your managed code, and fill in the model from your Graph.cs class. Preferably, Graph.cs shouldn't be responsible to do this, your marshalling code should be.

Dave Van den Eynde
+1  A: 

When I did this I used Marshal exclusively. This was with standard C for the native code. I did not want to convert my C code to "managed C++" or whatever they call it :-)

The hard and tedious part is that you have to manually marshal your data structure into something that maps directly to the receiving C function. In my case, I had to create separate structs for each data bearing C#-class I wanted to send. In essence, you should convert your nice C# object hierarchy into a more basic form consisting of structs which you then marshal into a memory chunk which you then use as an argument in your native call.

You should use the method Marshal.AllocHGlobal for allocing memory and Marshal.StructureToPtr for copying your C# struct to that memory and then Marshal.FreeHGlobal to free it.

Once you have your IntPtr (from StructureToPtr), you should be able to simply call your C-dll with that pointer as argument. Note that the struct you are sending to your C function must have the same layout as the native C struct, or you will get very odd results.

Returning data is pretty much the same thing, but you are using the opposite functions (PtrToStructure etc) instead.

Thats the basic of it anyway.

Martin Wickman
+1 Not sure I actually like the sound of this in practice, but it's interesting none the less.
Iain Collins
Heh, thanks. But unless you want to port your existing native code or write that C++ wrapper thing, this is the only way forward as far as *I* know.
Martin Wickman