Search code examples
c#.netcvisual-c++pinvoke

P/Invoke - simple addition of struct parameters


I have this C code:

real_T addition(const struct_T parameters)
{
    return parameters.a + parameters.b;
}

typedef struct
{
    real_T a;
    real_T b;
} struct_T;

typedef double real_T;

I invoke it from C# like this:

using System.Runtime.InteropServices;

    namespace AdditionConsoleApplication
    {
        class Program
        {
            [DllImport(@"X:\Bla\Addition.dll", CallingConvention = CallingConvention.Cdecl)]
            public static extern double addition(struct_T parameters);

            static void Main(string[] args)
            {
                struct_T parameters = new struct_T();
                parameters.a = 1;
                parameters.b = 3;

                Console.WriteLine(addition(parameters));
            }
        }
    }

Here struct_T:

[StructLayout(LayoutKind.Sequential)]
class struct_T
{
    public double a;
    public double b;
}

Unfortunately, the math is not correct:

2.72645911468311E-284

Can anyone see something wrong?


Solution

  • You have this definition for the structure:

    [StructLayout(LayoutKind.Sequential)]
    class struct_T
    {
        public double a;
        public double b;
    }
    

    The p/invoke marshaller will marshal classes by reference. In other words, a pointer to the object is passed to the native code. This mismatch explains why the program fails.

    The solution is to make sure that both sides of the interface match. One way is to pass by value on both sides. A simple way to achieve that is to declare the structure to be a value type in C#. Do that by making it a struct:

    [StructLayout(LayoutKind.Sequential)]
    struct struct_T
    {
        public double a;
        public double b;
    }
    

    The other way would be to pass by reference at both sides. You would achieve that by using class on the C# side and changing the C code.

    real_T addition(const struct_T* parameters)
    {
        return parameters->a + parameters->b;
    }