Search code examples
c#cpinvokestructure

passing structure from c# to C dll


I am trying to learn enough C# so that I can pass a strcture by reference to a C DLL; but it never gets to the "cFunction". As you can see in the cFunction, I am explicitly setting the streamSubset value to 44; but back in the c# portion it does not return "44". Here is the C code:

typedef struct s_mPlot
{
    double      price[100];
    int         streamSubset;
} mPlot;

extern "C" __declspec( dllexport )  
void cFunction(mPlot *Mplot){
    Mplot->streamSubset = 44;}

// and here is the c# code

 using System;
    using Vibe.Function;
    using System.Runtime.InteropServices;
    [StructLayout(LayoutKind.Sequential)]
    public class MPLOT 
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)]
        public double []    price;
        public int          streamSubset;
     } 

namespace Vibe.Indicator{
public class myIdx : IndicatorObject {

    [DllImport("C:\\Users\\joe\\mcDll.dll", CharSet = CharSet.Auto)]
    public static extern void cFunction(
    [In, MarshalAs(UnmanagedType.LPStruct)]  MPLOT  mPlot );
    public myIdx(object _ctx):base(_ctx){}
    private IPlotObject plot1;

    protected override void Create()
    {
        MPLOT mPlot         = new MPLOT();
        mPlot.streamSubset = 2; 
        cFunction(mPlot);

        if (mPlot.streamSubset == 44)
             go();  
    }

}

}


Solution

  • I can see the following:

    1. You almost certainly need to specify the cdecl calling convention in your DllImport attribute. Add CallingConvention=CallingConvention.Cdecl.
    2. I believe that UnmanagedType.LPStruct adds an extra level of indirection. But you are passing a C# class which is a reference type. That means you are passing a pointer to a pointer. That's one level of indirection too many. First of all remove [In, MarshalAs(UnmanagedType.LPStruct)] altogether. Then your code should work. If you switched to a struct rather than a class for MPLOT then you'd need to pass by ref to get the indirection.

    I think I would have the code like this:

    [StructLayout(LayoutKind.Sequential)]
    public struct MPLOT 
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)]
        public double []    price;
        public int          streamSubset;
    } 
    
    [DllImport("dllname.dll", CallingConvention=CallingConvention.Cdecl)]
    public static extern void cFunction(
        ref MPLOT mPlot
    );