emxArray_real_T to C# struct plus initialisation
Asked Answered
A

2

0

I am trying to create a 'constructor' for this C# struct (initial attempt included):

[StructLayout(LayoutKind.Sequential)]
public struct emxArray_real_T
{
public IntPtr data;
public IntPtr size;
public int allocatedSize;
public int numDimensions;
[MarshalAs(UnmanagedType.U1)]
public bool canFreeData;

public emxArray_real_T(double[] cSharpData)
{
    var arraySize = Marshal.SizeOf(cSharpData[0]) * cSharpData.Length;
    this.data = Marshal.AllocHGlobal(arraySize);
    // ????
    numDimensions = 1;
    canFreeData = false;
}
}

The C corresponding C struct looks like this:

typedef struct emxArray_real_T
{
   real_T *data;
   int32_T *size;
   int32_T allocated;
   int32_T numDimensions;
   boolean_T canFreeData;
} emxArray_real_T;

and is explained here.

Looking forward to any comments/answers. Thanks!

Albuminate answered 22/2, 2013 at 11:14 Comment(0)
N
1

You have a couple of choices as to how to do this. You can allocate unmanaged memory. And then copy the contents of your managed memory across. And then presumably copy it back when your call into native code returns.

Since your example sets canFreeData to false then I guess you want to do it the other way. And that is to pass the managed memory down to the native code. In order to do that you need to pin it to protect it from GC movement.

In order to make either of these approaches work I think you need a wrapper class to managed either the native memory, or the pinning. Here's how I'd tackle the pinning approach:

[StructLayout(LayoutKind.Sequential)]
public struct emxArray_real_T
{
    public IntPtr data;
    public IntPtr size;
    public int allocatedSize;
    public int numDimensions;
    [MarshalAs(UnmanagedType.U1)]
    public bool canFreeData;
}

public class emxArray_real_T_Wrapper : IDisposable
{
    private emxArray_real_T value;
    private GCHandle dataHandle;
    private GCHandle sizeHandle;

    public emxArray_real_T Value {
        get { return value; } 
    }

    public emxArray_real_T_Wrapper(double[] data)
    {
        dataHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
        value.data = dataHandle.AddrOfPinnedObject();
        sizeHandle = GCHandle.Alloc(new int[] { data.Length }, GCHandleType.Pinned);
        value.size = sizeHandle.AddrOfPinnedObject();
        value.allocatedSize = data.Length;
        value.numDimensions = 1;
        value.canFreeData = false;
    }

    public void Dispose()
    {
        dataHandle.Free();
        sizeHandle.Free();
        GC.SuppressFinalize(this);
    }

    ~emxArray_real_T_Wrapper()
    {
        Dispose();
    }
}
Nudge answered 22/2, 2013 at 13:4 Comment(1)
Comments archived in chat.Spearwort
E
1

Code:

[StructLayout(LayoutKind.Sequential)]
public struct emxArray_real_T
{
    public IntPtr data;
    public IntPtr size;
    public int allocatedSize;
    public int numDimensions;
    [MarshalAs(UnmanagedType.U1)]
    public bool canFreeData;
}

public class emxArray_real_T_Wrapper : IDisposable
{
    private emxArray_real_T value;
    private GCHandle dataHandle;
    private GCHandle sizeHandle;

    public ref emxArray_real_T Value
    {
        get { return ref value; }
    }

    public double[] Data
    {
        get
        {
            double[] data = new double[value.allocatedSize];
            Marshal.Copy(value.data, data, 0, value.allocatedSize);
            return data;
        }
    }

    public emxArray_real_T_Wrapper(double[] data)
    {
        dataHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
        value.data = dataHandle.AddrOfPinnedObject();
        sizeHandle = GCHandle.Alloc(new int[] { 1, data.Length }, GCHandleType.Pinned);
        value.size = sizeHandle.AddrOfPinnedObject();
        value.allocatedSize = data.Length;
        value.numDimensions = 1;
        value.canFreeData = false;
    }

    public void Dispose()
    {
        dataHandle.Free();
        sizeHandle.Free();
        GC.SuppressFinalize(this);
    }

    ~emxArray_real_T_Wrapper()
    {
        Dispose();
    }

Declaration:

[DllImport("TestFunc.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void TestFunc(int n, double WnLow, double WnHigh, ref emxArray_real_T kernel);

Using it:

public double[] CalculateBandPassCoefficients(int order, double FreqCutoffNormLow, double FreqCutoffNormHigh)
{
    double[] kernel = new double[order];
    using (emxArray_real_T_Wrapper wb = new emxArray_real_T_Wrapper(kernel))
    {
        TestFunc(order, FreqCutoffNormLow, FreqCutoffNormHigh, ref kernelWrapper.Value);

        kernel = kernelWrapper.Data;
    }
    return kernel;
}
Extend answered 18/4, 2019 at 12:38 Comment(1)
In this code sizeHandle is int[2], which indicates a 2d array, but that does not align with numDimensions = 1. Further, it should be clarified how this answer provides improvements over the accepted answer.Pleader

© 2022 - 2024 — McMap. All rights reserved.