【问题标题】:Marshalling array of structures from c++ to c# in WinCE在 WinCE 中将结构数组从 c++ 编组到 c#
【发布时间】:2017-01-23 17:14:45
【问题描述】:

我正在尝试在 Windows CE 程序和紧凑框架 2.0 上将以下结构从 c++ 编组到 c#。我在编组字符串时遇到了很多困难。

我有这个 c++ 代码:

#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);
}

这是对应的c#代码:

[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);

这就是我调用它的方式:

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

当我调用 GetAllConsoleParameters 时,我得到了 NotSupportedException 异常。我尝试了很多配置,但都没有成功。

谁能建议如何实现它?

提前致谢

【问题讨论】:

  • 异常的信息是什么?
  • 字符串是一个类,不等同于字符数组。 IN c++ 字符数组以 '\0' 结束。所以在 c# 中使用 byte[] 而不是 char 数组。
  • 我已经有一段时间没有使用 C++了,但我很确定 char []string[] 是不等价的。我认为您可能只希望在您的 c# 代码中使用 string
  • @jdweng 字符串仍然被编组
  • 对不起字节非字符串...复制错误...

标签: c# c++ arraylist compact-framework marshalling


【解决方案1】:

这适用于我在 Windows 桌面上。您可能必须在 C DLL 和 C# DllImport 属性中将调用约定更改为 Cdecl,因为我在这里读到 Cdecl 在 Windows CE 上是标准的:https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.callingconvention(v=vs.110).aspx

C 代码:

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#代码:

[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);
    }
}

【讨论】:

  • 使用紧凑型框架 2.0。 Charset.Ansi 未实现
  • 我尝试使用“C:\Program Files (x86)\Microsoft.NET\SDK\CompactFramework\v2.0\WindowsCE\mscorlib.dll”并进入这个库,不包含 Cdecl。也许我的库已经过时了?
【解决方案2】:

我会这样做

        [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;
            }

        }

【讨论】:

  • 我试试你的解决方案,但在 Marshal.SizeOf(allParameters) 中抛出异常,异常:NotsupportedException。枚举 CallingConvention 仅包含一个元素 WinApi。 (我使用框架:Compact Framework 2.0 和 VS 2008)。不明白为什么....
  • 确保使用最新的代码和结构。我在原始发布的代码中遇到了类似的错误并进行了更改。发布的代码不再给出此错误。 'z' 的值是 1400 = 50 * 28,这是正确的。因此,代码将一个 LPTR ptr 传递给非托管内存空间中预先分配的 1400 字节内存块。
  • 你可以试试这个:int z = Marshal.SizeOf(allParameters.GetType());
  • CE好像不支持Cdecl所以去掉CallingConvention选项。
【解决方案3】:

按照我的解决方案,c++ 代码:

/* - 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);
}

及对应的c#代码:

/* - 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);

和代码调用:

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

非常感谢您的帮助

【讨论】:

    猜你喜欢
    • 2010-12-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多