2

I have a function in C with this signature:

int addPos(int init_array_size, 
           int *cnt, 
           int *array_size, 
           PosT ***posArray, 
           char *infoMsg);

and here is what PosT looks like:

typedef union pu
{
    struct  dpos   d;
    struct  epo    e;
    struct  bpos   b;
    struct  spos   c;
} PosT ;

What's the best way to call this method in C# via P/Invoke? Do I need to define a class in C# representing PosT? How do I pass PosT ***posArray parameter across from C# to C?

Justin Niessner
  • 242,243
  • 40
  • 408
  • 536
jambodev
  • 351
  • 2
  • 3
  • 9
  • 1
    I know this isn't productive toward answering the question, but why on earth does that C function declaration have so many parameters? That could easily be wrapped by a simple structure. I believe there's some sample code for marshalling a structure in an answer here: http://stackoverflow.com/questions/1985067/marshalling-a-struct-containing-string – Shotgun Ninja Dec 19 '12 at 17:07
  • 1
    What exactly is the `hPtr` parameter. Just knowing its type is not enough information. – David Heffernan Dec 19 '12 at 17:10
  • 2
    Roll up a nice fat news paper. I'd recommend the New York Times Sunday edition. Go find the programmer that wrote that code and give him a good whack over the head with the paper. That ought to keep you inspired for a while as you try to get this monstrosity going. – Hans Passant Dec 19 '12 at 17:14
  • original post eddited now, with less parameters, this is an API I need to call, so I have to accept it as is. – jambodev Dec 19 '12 at 17:40
  • 1
    You are going to need to answer the question I asked, which `quetzalcoatl` elaborated on. Until you do so, nobody can help you. – David Heffernan Dec 19 '12 at 20:28
  • Thanks David, I have now answered the question in reply to quetz question. – jambodev Dec 20 '12 at 11:09
  • Hi David Heffernan I have created a neater and more detailed version of the question here: http://stackoverflow.com/questions/13974790/pinvoke-function-call-with-pointer-to-pointer-to-pointer-parameter I appreciate your help. – jambodev Dec 20 '12 at 15:32

2 Answers2

2

You have described how the PosT looks like, but this is not enough. First, have to know what the function expects to be passed as the ***PosT argument, and only THEN you can think of invoking it from C++ or C# side.

I know that probably does not fit your wishes, but please look:

PosT p;
PosT* ptr = &p;
PosT** ptr2 = &ptr;
PosT*** ptr3 = &ptr2;
func(...., ptr3, ...); // OK!?


PosT* ptr = new PosT[123];
PosT** ptr2 = &ptr;
PosT*** ptr3 = &ptr2;
func(...., ptr3, ...); // OK!?


PosT** ptr2 = new PosT[5];
for(int i=0;i<5;++i) ptr2[i] = new PosT[123];
PosT*** ptr3 = &ptr2;
func(...., ptr3, ...); // OK!??

And so on. Which one in-memory structure that I have quickly built is correct for that function? This is what determines the datatype that you will have to pass from the C# side.

If the function takes a thing you'd call a "mutable reference to jagged aray" (so the LAST example I provided), so the P/Invoke declaration would be:

[..extern..]
void func(....., ref PosT[][] posArray, ...);

invoked similar to:

func(...., new PosT[][] { 
         new PosT[] { new PosT{ d = ... }, new PosT{ d = ... }, ... },
         new PosT[] { new PosT{ d = ... }, new PosT{ d = ... }, ... },
         ... },
    .... );

but, please, first check what this function expects. With * there are really too many posibilities to just guess. You say it is from some API - check in its docs first! Tell us what exactly this function expects and me/someone will tell you how to build such POD in C#. Other way round it will not work! :)

PS. sorry for crappy C++/C# code, I'm in haste and only had a few minutes to write this:/

quetzalcoatl
  • 32,194
  • 8
  • 68
  • 107
  • Thanks quetza I appreciate it, in my C program I call it like this: variable declaration: PosT **posArray = NULL; and for the call: Addpos(..., &posArray,..) and addPos() will allocate memory for it, populate it and will return it. and then I have another function that does the deallocation. – jambodev Dec 20 '12 at 11:08
  • Hi quetzalcoatl I have added a cleaner and more detailed version of the question here: http://stackoverflow.com/questions/13974790/pinvoke-function-call-with-pointer-to-pointer-to-pointer-parameter I appreciate your help. – jambodev Dec 20 '12 at 15:31
1

Question #1 do I need to define a class in CSharp representing PosT?
You should define PosT as a struct in c#. Since this a union struct you will need to apply the StructLayout attribute, and fully define your other structs. It might look something like this:

struct  dpos { };
struct  epo  { };
struct  bpos { };
struct  spos { };
[System.Runtime.InteropServices.StructLayout(
    System.Runtime.InteropServices.LayoutKind.Explicit, Size=99)]
struct PosT {
    [System.Runtime.InteropServices.FieldOffset(0)] dpos   d;
    [System.Runtime.InteropServices.FieldOffset(0)] epo    e;
    [System.Runtime.InteropServices.FieldOffset(0)] bpos   b;
    [System.Runtime.InteropServices.FieldOffset(0)] spos   c;
    };

** Note that the "Size=99" isn't correct. Ideally, you should set this size to the number of bytes used by the largest enclosed struct.

Question #2- how do I pass PosT ***posArray parameter across frm CSharp to C?
You have to be really careful doing this sort of thing, especially if you expect to allocate the memory for posArray under C#. You don't want to pass a buffer to C that the .NET GC is about to move/change. If C is returning the buffer you should be ok (but you will leak memory if there isn't a C function to let you release the memory). You should take a look at Default Marshaling for Arrays and www.pinvoke.net might have some examples. If you are sending the posArray buffer from C# to C you are going to have to resort to an unsafe context (see below). I'm not sure exactly how to handle that level or redirection. Your cnt and array_size can be handled using the 'ref' keyword.

Question #3- How do I specify marshaling for it all?
Take a look at the links above especially the Default Marshaling for Arrays. You might also need to just play about with some test code, break after the call and use the immediate window to investigate the structures.


Remember

You can pretty much do a straight (c-like) call if you compile c# using the "UNSAFE" switch. For example:

unsafe public MainWindow() {
    InitializeComponent();
    int abc = 4;
    int* abcPtr = &abc;
    *abcPtr = 8;

    fixed (PosT* gog = new PosT[30]) {
        PosT* gogPtr = (gog + 1);
        }
    }

You have to be very careful because C# manages it's own memory and can move things on you (see 'fixed' above). I'm not recommending this but it's useful for quick and dirty sorts of things

Also if the API is defined in a tlb (table library) visual studio will usually offer to create the bindings for you via a stub dll (see tlbimp command)


Hopefully, someone can provide a more specific response but I think this info could at least start you down the road.

Dweeberly
  • 4,668
  • 2
  • 22
  • 41