简体   繁体   中英

Marshalling array of structures from c++ to c# in WinCE

I'm trying to Marshal the following structures from c++ to c# on a Windows CE program and compact framework 2.0. I'm having a lot of difficulties with the marshalling of strings.

I have this c++ code:

#define Console_Parameters_MAX 50

struct AllParameters {
  Parameter Parameters[Console_Parameters_MAX];
} ;

struct Parameter { 
  int Index;
  int Id; 
  char Value[20];
};

extern "C" {
   BOOL GetAllConsoleParameters(AllParameters *pItem);
}

and this are the corresponding c# code:

[StructLayout(LayoutKind.Sequential)]
public struct AllParameters {
  [MarshalAs(UnmanagedType.ByValArray, SizeConst = 50)]
  public Parameter[] Parameters
}

[StructLayout(LayoutKind.Sequential)]
public struct Parameter {
  public int Index;
  public int Id;
  [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
  public byte[] Value;
}

[DllImport("exemple.dll", SetLastError = true)]
public static extern bool GetAllConsoleParameters([MarshalAs(UnmanagedType.Struct)] ref AllParameters pItem);

and this is how I invoke it:

AllParameters item = new AllParameters();
if (AppAPI.GetAllConsoleParameters(ref item)) {
   var array = item.Parameters;
}

When I call the GetAllConsoleParameters I get exception NotSupportedException. I've tried many configurations but with no success.

Can anyone advise on how to achieve it?

Thanks in advance

This works for me on a Windows Desktop. You might have to change the calling convention to Cdecl in the C DLL and the C# DllImport attribute, because I read here that Cdecl is standard on Windows CE: https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.callingconvention(v=vs.110).aspx

C code:

extern "C" {
  __declspec(dllexport) BOOL __stdcall GetAllConsoleParameters(AllParameters *pItem)
  {
    pItem->Parameters[0].Index = 0;
    pItem->Parameters[0].Id = 42;
    CopyMemory(&pItem->Parameters[0].Value[0], "Hello World", 12);
    pItem->Parameters[1].Index = 1;
    pItem->Parameters[1].Id = 43;
    CopyMemory(&pItem->Parameters[1].Value[0], "Hello World 43", 15);
    return TRUE;
  }
}

C# code:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
struct Parameter
{
    int Index;
    int Id;
    //char Value[20];
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
    string Value;
};

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
struct AllParameters
{
    //Parameter Parameters[Console_Parameters_MAX];
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 50)]
    Parameter[] Parameters;
};

class Program
{
    [DllImport("MarshalC.dll", CallingConvention = CallingConvention.StdCall)]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool GetAllConsoleParameters(ref AllParameters pItem);

    static void Main(string[] args)
    {
        var size = Marshal.SizeOf<AllParameters>();
        AllParameters all = new AllParameters();
        bool result = GetAllConsoleParameters(ref all);
    }
}

I would do it like this

        [StructLayout(LayoutKind.Sequential)]
        public struct AllParameters {
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 50)]
            public Parameter[] Parameters;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct Parameter {
          public int Index;
          public int Id;
          [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
          public byte[] Value;
        }

        [DllImport("exemple.dll", SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
        public static extern bool GetAllConsoleParameters(ref IntPtr pItem);


        static void Main(string[] args)
        {
            AllParameters allParameters = new AllParameters();
            allParameters.Parameters = new Parameter[50];
            IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(allParameters));

            int z = Marshal.SizeOf(allParameters);

            if (GetAllConsoleParameters(ref ptr))
            {
                Marshal.PtrToStructure(ptr, allParameters);
                Parameter[] parameters = allParameters.Parameters;
            }

        }

following my solution, c++ code:

/* - not used
#define Console_Parameters_MAX 50

struct AllParameters {
  Parameter Parameters[Console_Parameters_MAX];
} ;
*/

struct Parameter { 
  int Index;
  int Id; 
  char Value[20];
};

extern "C" {
   BOOL GetAllConsoleParameters(Parameter pItem[], int size);
}

and corresponding c# code:

/* - not used
[StructLayout(LayoutKind.Sequential)]
public struct AllParameters {
  [MarshalAs(UnmanagedType.ByValArray, SizeConst = 50)]
  public Parameter[] Parameters
}
*/

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct Parameter {
  public int Index;
  public int Id;
  [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
  public byte[] Value;
}

[DllImport("exemple.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool GetAllConsoleParameters([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1), Out] ConsoleParameter[] myStruct, int size);

and code invoke:

ConsoleParameter[] item = new ConsoleParameter[50];
if (AppAPI.GetAllConsoleParameters(item, 50)) {
   var array = item;
}

thanks a lot for help

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM