views:

718

answers:

6

I have a function in unmanaged C/C++ code (dll) that returns a structure containing a char array. I created C# struct to receive this return value uppon calling the function. And uppon calling this function i get 'System.Runtime.InteropServices.MarshalDirectiveException'

This is C declaration:

typedef struct T_SAMPLE_STRUCT {
int num;
char text[20];
} SAMPLE_STRUCT;

SAMPLE_STRUCT sampleFunction( SAMPLE_STRUCT ss );

This is C# declaration:

struct SAMPLE_STRUCT
{
    public int num;
    public string text;
}

class Dllwrapper
{
    [DllImport("samplecdll.dll")]
    public static extern SAMPLE_STRUCT sampleFunction(SAMPLE_STRUCT ss);

}

I am using 1-byte ASCII.

Does anyone has a hint or a solution on how to do this?

+2  A: 

This is not an easy structure for P/Invoke to marshal: it's easier to marshal structures that contain char* instead of char[] (although you're then left with the problem of allocating the char* from unmanaged code and later freeing it from managed code).

Assuming you're sticking with the current design, one option is to declare the string array as:

public fixed char text[20];

Unfortunately you must then add the unsafe keyword to any code that accesses this array.

Tim Robinson
Thanks Tim, but i havent managed to make it run this way.
Mita
A: 

First you have to put StructLayout[Sequential] attribute on your struct and I think it will work

[ StructLayout( LayoutKind.Sequential, CharSet=CharSet.Ansi )]
struct SAMPLE_STRUCT
{    
    public int num;
    [ MarshalAs( UnmanagedType.ByValArray, SizeConst=20 )] 
    public char[] text;
}
Ahmed Said
+5  A: 

The trick to converting a C array member is to use the MarshalAs(UnmanagedType.ByValTStr). This can be used to tell the CLR to marshal the array as an inlined member vs. a normal non-inlined array. Try the following signature.

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, CharSet=System.Runtime.InteropServices.CharSet.Ansi)]
public struct T_SAMPLE_STRUCT {

    /// int
    public int num;

    /// char[20]
    [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst=20)]
    public string text;
}

public partial class NativeMethods {

    /// Return Type: SAMPLE_STRUCT->T_SAMPLE_STRUCT
    ///ss: SAMPLE_STRUCT->T_SAMPLE_STRUCT
    [System.Runtime.InteropServices.DllImportAttribute("<Unknown>", EntryPoint="sampleFunction")]
public static extern  T_SAMPLE_STRUCT sampleFunction(T_SAMPLE_STRUCT ss) ;

}

This signature is brought to you by the PInovke Interop Assistant (link) available on CodePlex. It can automatically translate most PInvoke signatures from native code to C# or VB.Net.

JaredPar
A: 

I have managed to do this by separating function to:

void receiveStruct( SAMPLE_STRUCT ss )
void returnStruct(SAMPLE_STRUCT &ss)

I have changed struct definition as JaredPar told me:

[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct T_SAMPLE_STRUCT
{
    /// int
    public int num;

    /// char[20]
    [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 20)]
    public string text;
}

And now it works.

Thanks!

Mita
A: 

As an interesting aside, since you already have the answer, the basic issue here is that the struct just needs to be the right size. Since managed types don't have inline arrays, you just have to make up the space that it would otherwise need. When you write in C++/CLI, you'll often see the StructLayoutAttribute with an explicit Size parameter. This makes the runtime allocate the right amount of memory for the type, which allows it to be blittable to the native side. It follows that these should work, as well:

[StructLayout(LayoutKind.Sequential, Size=24)]
public struct T_SAMPLE_STRUCT
{    
    public int num;
    // to get the string here, you'd need to get a pointer
    public char firstChar;     
}

// or

[StructLayout(LayoutKind.Sequential)]
public struct T_SAMPLE_STRUCT
{    
    public int num;
    public byte c0;
    public byte c1;
    public byte c2;
    public byte c3;
    public byte c4;
    public byte c5;
    public byte c6;
    public byte c7;
    public byte c8;
    public byte c9;
    public byte c10;
    public byte c11;
    public byte c12;
    public byte c13;
    public byte c14;
    public byte c15;
    public byte c16;
    public byte c17;
    public byte c18;
    public byte c19;
}

Of course, these are much harder to use from managed code (you'd need to copy memory or use pointers), but they illustrate the concept of a blittable type, which is primarily how types are passed between native and managed code.

codekaizen
+2  A: 

Struct definition in C:

#pragma pack(push, 1)
typedef struct T_SAMPLE_STRUCT {
  int num;
  char text[20];
};
#pragma pack(pop)

Definition in C#:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct T_SAMPLE_STRUCT
{
  [MarshalAs(UnmanagedType.I4)]
  public int num;

  [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
  public string text;
}
abatishchev