walruz
walruz

Reputation: 1329

How to properly pass struct pointer from C# to C DLL

I need to export function from C DLL. Here is example that I have written

typedef struct tag_struct {
  unsigned char first;
  unsigned char second;
} st_struct;

__declspec(dllexport) unsigned char Func( st_struct *ptr )
{
    return ptr->first + ptr->second;
}

Here is C# code that I'm using to import described above function.

using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace ImportTest
{
    [Serializable]
    [StructLayout(LayoutKind.Sequential)]
    public class st_struct
    {
        public byte first;
        public byte second;
    }

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            st_struct x = new st_struct();

            x.first = 1;
            x.second = 2;

            byte result = Func(ref x);
        }

        [DllImport("MarshalTest.dll")]
        protected static extern byte Func(ref st_struct inputs);
    }
}

My problem is that return value of Func is not 3 as it should be (1 + 2).

I am using debugger to see values inside DLL - they are different (not 1 and 2 that I provided).

Function returns proper value whan I change C# code like that:

public Form1()
{
    InitializeComponent();

    st_struct x = new st_struct();

    x.first = 1;
    x.second = 2;

    byte result = Func(x);
}

[DllImport("MarshalTest.dll")]
protected static extern byte Func(st_struct inputs);

Problem disappears when I remove ref. But I don't understand why.

Could you please explain that?

Upvotes: 2

Views: 4127

Answers (2)

Chris
Chris

Reputation: 5515

As @kennyzx mentions, since your st_struct is a class, it is already a reference type and will be passed as a pointer. I suspect throwing a ref onto that will give you a double pointer, which doesn't make much sense when mixing managed and unmanaged code. The marshaller can possibly handle that and create a new object for you if the pointer changes, but it seems like a sketchy thing to do.

So, when passing the class without ref, it works as expected (the C code gets a pointer). If you change it to a struct, passing it without ref should pass it on the stack and passing it with ref will pass it as a pointer.

Using struct seems like the obvious choice in your case, as it can be passed directly (the CLR just needs to pin it and pass a pointer). Using class I suspect will involve more marshalling.

Upvotes: 2

kennyzx
kennyzx

Reputation: 13003

You might be thinking that the ref keyword is required in order to passing parameter by reference. But since st_struct is defined as a reference type (class is reference type), so the parameter is passed by reference, not by value. You don't need the ref keyword.

If st_struct were defined as struct, you may find it work when you use the ref keyword.

Upvotes: 2

Related Questions