How to marshal an array of structure In C#?

2019-07-15 14:57发布

I have to call a C++ dll in C#. And the header of the dll is as following(simplified):

//Header of C++

struct vector
{
    float x;
    float y;

    vector()
    {}

    vector(float x0, float y0)
    {
        x = x0;
        y = y0;
    }
};

struct unmanaged_struct
{
    int int_var;
    float float_var;
    char* chars_var;
    vector vector_var;

    unmanaged_struct(int i, float f, char* ch, float vec_x, float vec_y) 
    {
        int_var = i;
        float_var = f;
        chars_var = ch;
        vector_var = vector(vec_x, vec_y);
    }
};

// this function is used to output all the variable values of the struct instance

extern "C" __declspec( dllexport )  void unmanagedstruct_summary(unmanaged_struct* us_list, int length);

And I defined following class in C#

//CSharp

[StructLayout(LayoutKind.Sequential)]
public class Vector
{
    public float x;
    public float y;

    public Vector(float f1, float f2)
    {
        x = f1;
        y = f2;
    }
}

[StructLayout(LayoutKind.Sequential)]
public class UnmanagedStruct 
{ 
    public int int_var;
    public float float_var;
    public string char_var;
    public Vector vector_var;

    public UnmanagedStruct(int i, float f, string s, Vector vec)
    {
        this.int_var = i;
        this.float_var = f;
        this.char_var = s;
        this.vector_var = vec;
    }
}

class UnmanagedDllCallTest
{
    [DllImport("unmanageddll.dll", EntryPoint = "unmanagedstruct_summary")]
    public  static extern void unmanagedstruct_summary([Out]UnmanagedStruct[] usList, int length);    

  static void Main(string[] args)
    {

        UnmanagedStruct[] usList = new UnmanagedStruct[1];
        usList[0] = new UnmanagedStruct(1, 1.0f, "aa", new Vector(10, 1));       
        usList[1] = new UnmanagedStruct(2, 2.0f, "ba", new Vector(20, 2));  
        UnmanagedDllCallTest.unmanagedstruct_summary(usList, 2);
}

And the output is as following:

unmanaged_struct summary:

0

1.12104e-044

Unhandled Exception: System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt. at callunmanageddll.UnmanagedDllCallTest.unmanagedstruct_summary(UnmanagedStr uct[] usList, Int32 length) at callunmanageddll.Program.Main(String[] args) in c:\users\dynaturtle\docume nts\visual studio 2010\Projects\callunmanageddll\callunmanageddll\Program.cs:lin e 68

The C++ dll is OK as I have written test in C++ and the function works well. I have read this thread but it seems the solution didn't work in my case. Any suggestions? Thanks in advance!

标签: c# pinvoke
3条回答
\"骚年 ilove
2楼-- · 2019-07-15 15:47

Use Marshal.PtrToStructure. There is a sample here.

So you would have to change the signature of the method from out structure array to out IntPtr. However, you need to know the size of the buffer being passed out.

public struct Vector
{
    public float x;
    public float y;

}

public struct UnmanagedStruct 
{ 
    public int int_var;
    public float float_var;
    public string char_var;
    public Vector vector_var;

}

class UnmanagedDllCallTest
{
    [DllImport("unmanageddll.dll", EntryPoint = "unmanagedstruct_summary")]
    public static extern void unmanagedstruct_summary([Out] IntPtr ptr, int length);    

  static void Main(string[] args)
  {

    for(int i=0; i<length; i++)
    {
        UnmanagedStruc st;
        Marshal.PtrToStructure(ptr, st);
        // increment ptr and move forward
    }

}
查看更多
走好不送
3楼-- · 2019-07-15 15:51

First: Vector and UnmanagedStruct should be structs, not classes.

查看更多
Ridiculous、
4楼-- · 2019-07-15 15:55

JIC I'd share my approach. Perhaps this is not an expected answer by at one time I spend a time to resolve my issue.

I have the following structure to expose some data from DLL.

//C++ code
    struct State
    {
        const wchar_t * name;
        unsigned int state; 
    };

APIENTRY bool get_states(H_PRCSR, MacroState *, const int sz); //pay attention, function accepts already allocated array and size for it

To accept this data From C++ I can do this way

std::vector<State> states(desired_size);
get_states(hparser, &states[0], states.size());

To do the same on C# I used the following way

//C#
[StructLayout(LayoutKind.Sequential)]
public struct Status
{
   public IntPtr name;
   public uint state;

   public string getName()
   {
     if (name == IntPtr.Zero) return "<no-value>";
     return Marshal.PtrToStringUni(name);
   }
}

//And import function...

[DllImport(dll, CallingConvention = CallingConvention.Winapi)]
private static extern bool get_states(IntPtr p, [Out]MacroFlag[] flags, int flags_size);

//And simple decoder
public static Status[] getAll(IntPtr p, int size)
{
   var results = new Status[size];

   get_states(p, results, size);

   return results;
}

As, I saw the are different approaches to do this. This is one of them. And it works for me. Perhaps, this post will not resolve the issue but will be a good point to start

查看更多
登录 后发表回答