传递非空结尾的字符串给非托管代码

4
考虑以下结构体将被发送到未托管的dll上,通过TCP协议进行传输。
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct FooMessage
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 42)]
    public string foo;

    //More fields...
}

Using the following function (credit to Cheeso):

public byte[] RawSerialize( T item )
{
    int rawSize = Marshal.SizeOf( typeof(T) );
    IntPtr buffer = Marshal.AllocHGlobal( rawSize );
    Marshal.StructureToPtr( item, buffer, false );
    byte[] rawData = new byte[ rawSize ];
    Marshal.Copy( buffer, rawData, 0, rawSize );
    Marshal.FreeHGlobal( buffer );
    return rawData;
}

问题: 码化程序假定foo是以空字符结尾的字符串,然而非托管dll并不是如此 - 实际上它使用最后一个字符(这总是从码化程序中输出的null值)。
有什么想法吗?
澄清:我不能只将SizeConst更改为43,因为我需要保持消息的总大小以及结构体中下一个字段的位置(根据现有的ICD)。

1
使用char[]确实是这样做的方法。 - Mattias S
3个回答

2

鉴于没有其他答案发布,这里是我发现的解决方法

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct FooMessage
{
    // use this for non-null-terminated strings
    // use default encoder to convert to and from string
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=42)]
    public char[] foo;

    //More fields...
}

TCP专家Stephen Cleary也提供了类似的解决方案。


我觉得很奇怪,居然没有一个可以帮助序列化非空结尾字符串的 Marshalling 属性... - pauloya
我们最终将 char[] 字段隐藏(私有化),并在结构体上定义了一个公共属性,其中包含 getter 和 setter,用于将 char[] 转换为字符串和反之。它还根据需要应用 Trim 和 Pad。 - pauloya
请添加以下类似的属性,以使事情变得更加轻松。public string Foo { get => new string(this.foo); set => this.foo = value.ToCharArray(); } - user169771

2
您可以使用 StructLayout(LayoutKind.Explicit ...) 并在每个字段上打上 [FieldOffset(n)] 的标签。这将允许您将 SizeConst 值增加到 43,同时将下一个字段标记为从偏移量 42 开始。编组器将编组 42 个字符的字符串并忽略第 43 个字节,而是附加一个空终止符。

0

你只有两个选择:

  1. 让dll理解NUL结尾的字符串;或者
  2. 在消息中发送字符计数,并让dll理解该计数。

选其一,随你喜欢。

-- b


这个dll不在我的控制范围内。我已经找到了一个解决方法,请参见原帖。只是想知道是否有更好的方法,虽然我怀疑这点。 - Ohad Schneider

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接