【问题标题】:PInvoke struct with array of unspecified length具有未指定长度的数组的 PInvoke 结构
【发布时间】:2013-03-24 23:38:30
【问题描述】:

C 定义

typedef struct {
    const uint8_t* buf;
    int bufLen;
} Info;


int Foo(Info* info);

C# 定义

[StructLayout(LayoutKind.Sequential)]
public struct Info
{
     // [MarshalAs( ??? )]
     public byte[] buf;
     int bufLen
}    

[DllImport(...)]
public static extern int Foo(ref Info info);

我无法确定在 C# 结构定义中为 byte[] buf 上的 MarshalAs 属性指定什么内容。缓冲区是在 .NET 端分配的,它的长度在调用时是已知的。

在一个简单的小测试中:

var info = new Info {
    buf = new byte[] {0x40, 0x50, 0x60, 0x70},
    bufLen = 4,
};

Foo(ref info);

似乎一切正常,但实际上我缓冲区中的数据不正确。从我看到的 DLL 中打印出来 01 00 80 00 - 不确定那是什么。

我试过了:

  • 没有MarshalAs
  • [MarshalAs(UnmanagedType.SafeArray)]

没有任何效果。

总的来说,我也真的不知道调试这类问题的最佳方法。

【问题讨论】:

  • 这样的结构很难自动编组,你必须自己做。将 buf 声明为 IntPtr 并使用 Marshal.AllocHGlobal 为其分配内存。 Marshal.Copy 来填充它。注意在非托管端发生了什么,如果它存储指针或结构的副本,你会遇到很多麻烦。
  • @HansPassant 谢谢,那行得通。为什么 CLR 无法自动处理这个问题?我的第一个猜测是它不会查看 Info 来查看如何编组其任何元素 - 而只是查看 Info 的大小并复制那么多字节。
  • 可以,但是太危险了。当数组不再固定时,本机代码可能会在以后取消引用指针。
  • @HansPassant 可以,但这与将byte[] 编组为参数的情况有何不同?
  • @HansPassant 在这些情况下,我希望 MarshalAs 上的 struct 声明支持 SizeParamIndex 参数的变体...

标签: c# pinvoke


【解决方案1】:

根据 Hans Passant 的建议,我实施了以下操作:

[StructLayout(LayoutKind.Sequential)]
public struct Info : IDisposable
{
    private IntPtr buf;
    private int bufLen;

    public Info(byte[] buf) : this() {
        this.buf = Marshal.AllocHGlobal(buf.Length);
        Marshal.Copy(buf, 0, this.buf, buf.Length);
        this.bufLen = buf.Length;
    }

    public void Dispose() {
        if (buf != IntPtr.Zero) {
            Marshal.FreeHGlobal(buf);
            buf= IntPtr.Zero;
        }
    }
}

【讨论】:

    【解决方案2】:

    我知道为时已晚.. 但以防万一有人来这里寻求答案。

    我终于想出了类似练习的下一个解决方案。

    [StructLayout(LayoutKind.Sequential)]
    public struct Info
    {
         [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
         public byte[] buf;
         int bufLen
    }
    

    来自 MSDN

    数组可以被编组为 UnmanagedType.ByValArray,它 要求您设置 MarshalAsAttribute.SizeConst 字段。规模 只能设置为常量。

    在我的例子中,缓冲区大小是 8 个字节。

    【讨论】:

    • 我不确定这个答案有什么相关性? OP 明确表示他事先不知道缓冲区的大小。
    • @9ee1 你是对的。我错过了主题中的 “并且它的长度在调用时已知” 并且被 “在一个简单的小测试中:” 代码示例弄糊涂了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-01-14
    • 2015-08-16
    • 2020-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多