简体   繁体   中英

C# marshalling large struct to c DLL

I'm trying to access functions exported by some DLL (for accessing some specific hardware). The access to the functions using DLLImport and simple datatypes is working. But to some functions I need to pass some quite large structures. To overcome size limitations of automated marshalling I figured out to use IntPtr.

typedef struct
{
    double value[MemDepth];
    double offset[MemDepth];
    unsigned long start;
    BOOL active;
} RegisterData;

typedef struct 
{
    RegisterData a,b,c,d,e,f,g,h;
    BOOL active;
    BOOL test;
} Register;

And this is how I tried to put them in C#:

[StructLayout(LayoutKind.Sequential)]
public struct RegisterData
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = defines.MemDepth)]
    public double[] value;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = defines.MemDepth)]
    public double[] offset;

    UInt32 start;

    [MarshalAs(UnmanagedType.U1)]
    bool active;
}

[StructLayout(LayoutKind.Sequential)]
public struct Register
{
    RegisterData a,b,c,d,e,f,g,h;
    [MarshalAs(UnmanagedType.U1)]
    bool active;
    [MarshalAs(UnmanagedType.U1)]
    bool test;
}

The function I'm trying to call looks the following in the DLL:

long ResetControl(Register* control);

Which I translated into:

[DllImport("test.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern Int32 Reset(IntPtr control);

I'm then trying to call the function like this:

public Int32 lib_Reset(ref Register control)
{
    int size = System.Runtime.InteropServices.Marshal.SizeOf(control);

    //IntPtr ptr = System.Runtime.InteropServices.Marshal.FreeHGlobal(size);
    IntPtr ptr = System.Runtime.InteropServices.Marshal.AllocCoTaskMem(size);

    System.Console.WriteLine("requested size of: " + size); // shows correct

    System.Runtime.InteropServices.Marshal.StructureToPtr(control, ptr, false);

    System.Console.WriteLine("mem done");

    Int32 ret = Reset(ptr);

    System.Console.WriteLine("called reset");

    System.Runtime.InteropServices.Marshal.PtrToStructure(ptr, control);
    System.Console.WriteLine("transfered back");

    System.Runtime.InteropServices.Marshal.FreeCoTaskMem(ptr);
    //System.Runtime.InteropServices.Marshal.FreeHGlobal(ptr);

    System.Console.WriteLine("freeed mem");
    ptr = IntPtr.Zero;

    return ret;
}

This results in " A heap has been corrupted." when calling the DLLs Reset function. I already tried different ways but non worked.

EDIT:

I now got it working by cleaning and rebuilding the project using the IntPtr approach. It does not work directly using "ref". When using "ref" I always get:

 Cannot marshal 'parameter #1': Internal limitation: structure is too complex or too large.

I now do the following to convert from structure to pointer and back:

public Int32 lib_Reset(ref Register control)
{
    int size = Marshal.SizeOf(control);
    IntPtr ptr = Marshal.FreeHGlobal(size);
    Marshal.StructureToPtr(control, ptr, false);
    Int32 ret = Reset(ptr);
    control = (Register)Marshal.PtrToStructure(ptr, typeof(control));
    Marshal.FreeHGlobal(ptr);
    ptr = IntPtr.Zero;
    return ret;
}

That way I can call Reset but it seems the functions does not do anything.

Register reg = new Register();

reg.active = true;
System.Console.WriteLine(reg.active);   // .active = true
lib_Reset(ref reg);         // should set .active = false
System.Console.WriteLine(reg.active);   // .active = true

The function Reset should initialize the Register structure. Thereby it sets active to false. But in the code shown above the value of active does not change. I tested the same with a integer value. It also did not change.

I hope anyone here can point me to the right direction in accessing the data.

Thank you, Tobias

Now it works. Cleaning and rebuilding the project did the trick. It is now working with the IntPtr approach. But the Reset function does not affect the values in the structure.

From the code of the DLL I know the elements should be initialized to some values. But the values of the elements do not change from before the call to after the call:

Register reg = new Register();

reg.active = true;
System.Console.WriteLine(reg.active);   // .active = true
lib_Reset(ref reg);         // should set .active = false
System.Console.WriteLine(reg.active);   // .active = true

The same fot the other elements. Is there something wrong with the conversion from structure to pointer and then back from pointer to structure?

If I take some value that is of integer type and I set it to 4 before the call it will be 4 after the call. So the values set before are preserved.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM