7
votes

I've an unmanaged dll that exports the folowing function:

SomeData* test();

Let's assume SomeData as:

typedef struct _Data Data;  
struct _Data{  
    int a;  
    int b;  
}

Now i want to call this function from C# code. I start to define the C# Struture needed to custom marshaling like this:

[StructLayout(LayoutKind.Sequential)]  
public class SomeData  
{  
    public Int32 a;  
    public Int32 b;  
}  

And now, i declare the managed function:

[DllImport("DynamicLibrary.dll", CharSet=CharSet.Auto)]  
[return: MarshalAs(UnmanagedType.LPStruct)]  
public static extern SomeData test();  

And in the main function i have:

IntPtr ptr = test();  

Doing this, i get the MarchalDirectiveException: "Cannot marshal 'return value': Invalid managed/unmanaged type combination (Int/UInt must be paired with SysInt or SysUInt)."

I did not allocate the memory for SomeData in C# since i expect this memory is allocated in the C function and them i would use the Marshal.Copy to pass it to the managed memory.

Any ideas? Thanks

------------------------ EDITED AFTER JaredPar ANSWER --------------------

In fact, i committed a mistake when coping the code to my question. The real managed signature i was using was:

[DllImport("DynamicLibrary.dll", CharSet=CharSet.Auto)]
[return: MarshalAs(UnmanagedType.LPStruct)]
public static extern IntPtr test();

The JaredPar's answer is still relevant. To get the right behaviour, i have 2 choices:

1) Use the 'public static extern IntPtr test();' (without MarshalAs attribute) signature and then access the returned pointer like JaredPar suggested.

2) Use the 'public static extern SomeData test();' (with MarshalAs attribute) and then simply use SomeData sd = test();

1

1 Answers

10
votes

When declaring the managed function you need to match pointer types with reference values or IntPtr values. In this case the LPStruct modifier won't help. The easiest solution is to convert the return value of test to be an IntPtr rather than SomeData since the native method is returning a pointer value. You can then write the following wrapper

[DllImport("DynamicLibrary.dll", CharSet=CharSet.Auto)]
public static extern IntPtr test();

public static SomeData testWrapper() {
  var ptr = test();
  try {
    return (SomeData)Marshal.PtrToStructure(ptr, typeof(SomeData));
  } finally {
    // Free the pointer here if it's allocated memory
  }
}