views:

36

answers:

1

Hi,

I am using API written in C++ in my code (writting in C#). API requires a parameter as Pointer to Structure. The Structure cosists of "Int"s and Char Arrays: for example

 unsafe public struct ToBePassed 
    { 
        Int32 Num1;
        Int32 Num2; 
        Char[] Data; // or fixed Char Data[255];
    }

I can not directly pass the structure structure pointer to API becuase in that case, I am getting error as "Pointers cannot refrence Marshaled structures". Code get compiled successfully but this Error comes when I execute (Debug) the code.

Now I have two options: 1st:- Passing Structure by Ref: I want to ask does an API requiring A Structure Pointer can receive the address when I pass the structure by ref. Note that API will return Data in "Char[] Data".

2nd:- Using Marshal.StructureToPtr: This will convert Structure Pointer to IntPtr. Again the Doubt is same, Will that API receive it correctly?

Thanks for Your Time!

Regards, Swanand

+1  A: 

If it only requires pointer, you can allocate some unmanaged memory, marshal the structure to the memory, and pass that pointer to your function. Then afterwards you could marshal back to the structure (if you wish) and free the memory. Before you marshal anything, you need to properly define the structure. Something like this:

[StructLayout(
    LayoutKind.Sequential,      //must specify a layout
    CharSet = CharSet.Ansi)]    //if you intend to use char
public struct ToBePassed
{
    public Int32 Num1;
    public Int32 Num2;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 255)]
    public Char[] Data;    //specify the size using MarshalAs
}

[DllImport("...")]
public static extern void APICall(IntPtr argPtr);


public static void CallFunction(ToBePassed managedObj)
{
    IntPtr unmanagedAddr = Marshal.AllocHGlobal(Marshal.SizeOf(managedObj));

    Marshal.StructureToPtr(managedObj, unmanagedAddr, true);

    APICall(unmanagedAddr);

    Marshal.PtrToStructure(unmanagedAddr, managedObj);

    Marshal.FreeHGlobal(unmanagedAddr);
    unmanagedAddr = IntPtr.Zero;
}

[edit]
To simulate variable length arrays, allocate unmanaged memory within the structure and initialize as usual.

[StructLayout(LayoutKind.Sequential)]
public struct SomeStruct
{
    public Int32 X;
    public Int32 Y;
}

[StructLayout(LayoutKind.Sequential)]
public struct VLA
{
    public Int32 intArrayLength;
    public Int32 SomeStructArrayLength;
    public IntPtr intArray;
    public IntPtr SomeStructArray;
}

public static VLA CreateVLA(int[] intArray, SomeStruct[] SomeStructArray)
{
    var vla = new VLA()
    {
        intArrayLength = intArray.Length,
        SomeStructArrayLength = SomeStructArray.Length,
        intArray = Marshal.AllocHGlobal(intArray.Length * Marshal.SizeOf(typeof(int))),
        SomeStructArray = Marshal.AllocHGlobal(SomeStructArray.Length * Marshal.SizeOf(typeof(SomeStruct))),
    };
    Marshal.Copy(intArray, 0, vla.intArray, intArray.Length);
    //there's no overload to copy arbitrary arrays, do it manually
    for (int i = 0; i < SomeStructArray.Length; i++)
    {
        Marshal.StructureToPtr(
            SomeStructArray[i],
            vla.SomeStructArray + i * Marshal.SizeOf(typeof(SomeStruct)),
            true);
    }
    return vla;
}
Jeff M
Thank You very very Much! That was Great!! One Question, Can API return data in the Char Array?
Swanand Purankar
AFAIK, yes. The unmanaged API may use the pointed to memory like any other memory since it is unmanaged. In that case, you would want to marshal back to the structure to get the result back in the managed code.
Jeff M
That's Working fine.... But I am facing one more problem, Now I want to Pass data to API (say WriteToAPI version of above mentioned API) I will pass data to API now. But as this Char data is not of size 255 (I want to pass variable size data) it is giving error as "Type Can not be Marshaled because length of an Embedded Array instance does not match the declared length in Layout"
Swanand Purankar
Unfortunately, you cannot marshal variable length arrays. They must be fixed size. You can however achieve the functionality by allocating unmanaged memory for that purpose. I'll update my answer with an example.
Jeff M
Have you updated the code example? I can't spot the differecne.
Swanand Purankar
Was working on it. ;) I tried to be as thorough as possible. Things get a bit complicated with arrays of structures since there isn't an appropriate `Marshal.Copy()` overload.
Jeff M
I am sorry but I can't understand your solution. I don't want to pass Array of Structures. I want to pass a structure which (also) contains An array whoes length is variable.
Swanand Purankar
The thing is, you can't. All arrays must have a fixed size to be marshaled correctly. In my example, the arrays were dynamically allocated (`intArray` and `SomeStructArray`) within the `VLA` struct. I believe this is the only way to achieve such functionality.
Jeff M
Okey! Thanks a lot for your help and Time! It was great talking with You!! Thanks again!
Swanand Purankar