C#Marshal typedef T_STRING T_STRINGS [MAX_STRING_SIZE]

时间:2014-06-13 11:07:09

标签: c# pinvoke marshalling

如何元帅:

#define MAX_STRING_SIZE 255
typedef char T_STRING[MAX_STRING_SIZE];

#define MAX_STRING_LIST_SIZE 50
typedef T_STRING T_STRINGS[MAX_STRING_LIST_SIZE];

typedef struct
{
   unsigned long m_ID;
   T_STRING m_input;
   T_STRINGS m_resultTexts;
} ResultList;

在C#中?

我试过了:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct ResultList
{

    public uint m_ID;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 255)]
    public string m_input;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12750)]
    public string[] m_resultTexts;
}

调用时会抛出错误: 无法编组'参数#2':内部限制:结构过于复杂或过大。

参数#2是ResultList结构。

这个问题是"下一步"来自C# Marshal typedef char T_STRING[MAX_STRING_SIZE]

2 个答案:

答案 0 :(得分:2)

marshaller不会处理一系列固定长度的字符串。那太复杂了。

你的结构声明无论如何都是错误的。 SizeConst指定数组长度而不是字节大小。如果您仔细考虑这一点,您将意识到您必须指定两个维度,数组长度和数组元素类型的维度。但这并不重要。没有多少调整这些属性来解决问题。

这意味着您需要手动完成。你可以使用自定义编组器来完成它。或者你可以用Marshal.AllocHGlobal分配一块内存并传递该函数。

我无法分辨数据的流向,但这两种方式都很简单。要将数据传递给非托管代码,您需要写入分配的内存块。使用Marshal.WriteInt32写入整数。使用Marshal.Copy编写字符串。你需要指针算术。并且您需要使用Encoding.GetBytes来获取字符串数据。

在相反方向,您Marshal.ReadInt32使用uint。并且可以将字符串复制到字节数组并使用Encoding.GetString进行解码。

对于结构布局,您需要考虑对齐和填充。碰巧这个结构当前没有内部填充。但由于整数的对齐要求,它必须是4的倍数。扩展它时要小心。

最后,如果您同时控制托管代码和非托管代码,则可以考虑更改非托管端的数据类型,以便更轻松地进行管理。或者您可能会考虑再次使用C ++ / CLI来平滑互操作。

答案 1 :(得分:0)

使用它来通过TCP / IP或PIPESTREAM发送所需的操作如下。

在执行每个MarshalAs之后,在每个变量之前

[MarshalAs(UnmanagedType.U4 or BytValArray or I4 ... ETC ]
DO: [FieldOffset(Previous offset value + Previous Variable Size in byte)]

示例

   [MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)]
        [FieldOffset(4)]
        private char[] zSlotName;
        public char[] _zSlotName
        {
            get { return zSlotName; }
            set {
                zSlotName = new char[9];
                for (int i = 0; i < zSlotName.Length; i++)
                {
                    if (value.Length > i)
                        zSlotName[i] = value[i];
                    else
                        zSlotName[i] = ' ';
                }
            }
        }
        [MarshalAs(UnmanagedType.U4)]
        [FieldOffset(13)]
        private int nThreadId;
        public int _nThreadId
        {
            get { return nThreadId; }
            set { nThreadId = value; }
        }

这将告诉编译器和程序,当您通过TCP / IP或PIPESTREAM发送变量时,或想将其写入二进制文件时,在块中的哪个位置分配与其他变量相关的变量,以及如何从(STREAM)返回时阅读

我知道这听起来好像是一个很严重的错误,但这是由于int的大小以及字节和dword,单词以及我们今天在各种语言中没有的变量的不同而引起的,我认为C#需要知道其变量的具体位置在内存或文件中。

如果执行此操作,那么就可以为游戏或任何您自己想要的通信协议建立任何协议,请注意,将其转换为类以存储在DB中或以二进制存储在文件中非常容易,这很容易您不希望使用SQL。

亲切的问候

崩溃替代;)