【问题标题】:Marshaling pointer to an array of strings封送指向字符串数组的指针
【发布时间】:2009-08-24 17:55:04
【问题描述】:

我在编组指向字符串数组的指针时遇到了一些麻烦。它看起来像这样无害:

typedef struct
{
    char* listOfStrings[100];
} UnmanagedStruct;

这实际上嵌入在另一个结构中,如下所示:

typedef struct
{
    UnmanagedStruct umgdStruct;
} Outerstruct;

非托管代码回调托管代码,并将 Outerstruct 作为 IntPtr 返回,其中分配了内存并填充了值。

托管世界:

[StructLayout(LayoutKind.Sequential)]
public struct UnmanagedStruct
{
    [MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPStr, SizeConst=100)]
    public string[] listOfStrings;
}

[StructLayout(LayoutKind.Sequential)]
public struct Outerstruct
{
    public UnmanagedStruct ums;
}

public void CallbackFromUnmanagedLayer(IntPtr outerStruct)
{
    Outerstruct os = Marshal.PtrToStructure(outerStruct, typeof(Outerstruct));
    // The above line FAILS! it throws an exception complaining it cannot marshal listOfStrings field in the inner struct and that its managed representation is incorrect!
}

如果我将 listOfStrings 更改为简单的 IntPtr,那么 Marshal.PtrToStructure 可以工作,但现在我无法翻入 listOfStrings 并一一提取字符串。

【问题讨论】:

    标签: c# pinvoke unmanaged marshalling


    【解决方案1】:

    编组除非常基本的字符串之外的任何内容都很复杂,并且充满了难以发现的副案例。通常最好在结构定义中使用安全/简单的路线,并添加一些包装器属性来整理一下。

    在这种情况下,我将使用 IntPtr 数组,然后添加一个将它们转换为字符串的包装器属性

    [StructLayout(LayoutKind.Sequential)]
    public struct UnmanagedStruct
    {
        [MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPStr, SizeConst=100)]
        public IntPtr[] listOfStrings;
    
        public IEnumerable<string> Strings { get { 
          return listOfStrings.Select(x =>Marshal.PtrToStringAnsi(x));
        }
    }
    

    【讨论】:

    • Jared 感谢您的验证!在看到你的问题之前,我刚刚发布了我自己问题的答案。一个问题——如何在帖子中格式化我的代码?他们看起来都一团糟,总得有人编辑和纠正。
    • @Dilip,选择您的代码 sn-p 并按 CTRL+K。这将通过缩进所有 4 spcaes 来修复格式
    • @Jared:只是快速跟进。如果我使用 UnmanagedType.LPArray,代码会继续爆炸。只有 UnmanagedType.ByValArray 有效。我现在明白 KeeperOfTheSoul 在他/她的 cmets 中暗示了什么。
    【解决方案2】:

    好的..我似乎已经开始工作了。它应该被编组为 IntPtr[]

    这似乎有效:

    [StructLayout(LayoutKind.Sequential)] 
    public struct UnmanagedStruct 
    { 
        [MarshalAs(UnmanagedType.ByValArray, SizeConst=100)] 
        public IntPtr[] listOfStrings; 
    }
    
    for (int i = 0; i < 100; ++i)
    {
        if (listOfstrings[i] != IntPtr.Zero)
            Console.WriteLine(Marshal.PtrToStringAnsi(listOfStrings[i]));
    }    
    

    【讨论】:

    • ByValArray == 就地数组,LPArray == 指向数组的指针。虽然 SizeConst 仍然应该与 LPArray 一起使用,但编组时的错误有点奇怪。
    猜你喜欢
    • 2014-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-28
    • 1970-01-01
    • 1970-01-01
    • 2012-02-08
    • 2013-09-27
    相关资源
    最近更新 更多