【问题标题】:Passing a Structure to C++ API using Marshal.StructureToPtr in C#在 C# 中使用 Marshal.StructureToPtr 将结构传递给 C++ API
【发布时间】:2010-10-15 06:07:31
【问题描述】:

我在我的代码中使用用 C++ 编写的 API(用 C# 编写)。 API 需要一个参数作为指向结构的指针。 该结构由“Int”和 Char 数组组成: 例如

 unsafe public struct ToBePassed 
    { 
        Int32 Num1;
        Int32 Num2; 
        Char[] Data; // or fixed Char Data[255];
    }

我不能直接将结构指针传递给 API,因为在这种情况下,我会收到错误消息,因为“指针无法引用编组结构”。代码编译成功,但是当我执行(调试)代码时出现此错误。

现在我有两个选择: 第一个:- 通过 Ref 传递结构:我想问当我通过 ref 传递结构时,需要结构指针的 API 是否可以接收地址。请注意,API 将返回“Char[] Data”中的数据。

第二个:- 使用 Marshal.StructureToPtr:这会将结构指针转换为 IntPtr。再次怀疑是一样的,该API会正确接收它吗?

感谢您的宝贵时间!

问候, 斯旺德

【问题讨论】:

    标签: c# struct marshalling


    【解决方案1】:

    如果它只需要指针,您可以分配一些非托管内存,将结构编组到内存,然后将该指针传递给您的函数。然后,您可以编组回结构(如果您愿意)并释放内存。在编组任何内容之前,您需要正确定义结构。像这样的:

    [StructLayout(
        LayoutKind.Sequential,      //must specify a layout
        CharSet = CharSet.Ansi)]    //if you intend to use char
    public struct ToBePassed
    {
        public Int32 Num1;
        public Int32 Num2;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 255)]
        public Char[] Data;    //specify the size using MarshalAs
    }
    
    [DllImport("...")]
    public static extern void APICall(IntPtr argPtr);
    
    
    public static void CallFunction(ToBePassed managedObj)
    {
        IntPtr unmanagedAddr = Marshal.AllocHGlobal(Marshal.SizeOf(managedObj));
    
        Marshal.StructureToPtr(managedObj, unmanagedAddr, true);
    
        APICall(unmanagedAddr);
    
        Marshal.PtrToStructure(unmanagedAddr, managedObj);
    
        Marshal.FreeHGlobal(unmanagedAddr);
        unmanagedAddr = IntPtr.Zero;
    }
    

    [编辑]
    要模拟变长数组,请在结构中分配非托管内存并照常进行初始化。

    [StructLayout(LayoutKind.Sequential)]
    public struct SomeStruct
    {
        public Int32 X;
        public Int32 Y;
    }
    
    [StructLayout(LayoutKind.Sequential)]
    public struct VLA
    {
        public Int32 intArrayLength;
        public Int32 SomeStructArrayLength;
        public IntPtr intArray;
        public IntPtr SomeStructArray;
    }
    
    public static VLA CreateVLA(int[] intArray, SomeStruct[] SomeStructArray)
    {
        var vla = new VLA()
        {
            intArrayLength = intArray.Length,
            SomeStructArrayLength = SomeStructArray.Length,
            intArray = Marshal.AllocHGlobal(intArray.Length * Marshal.SizeOf(typeof(int))),
            SomeStructArray = Marshal.AllocHGlobal(SomeStructArray.Length * Marshal.SizeOf(typeof(SomeStruct))),
        };
        Marshal.Copy(intArray, 0, vla.intArray, intArray.Length);
        //there's no overload to copy arbitrary arrays, do it manually
        for (int i = 0; i < SomeStructArray.Length; i++)
        {
            Marshal.StructureToPtr(
                SomeStructArray[i],
                vla.SomeStructArray + i * Marshal.SizeOf(typeof(SomeStruct)),
                true);
        }
        return vla;
    }
    

    【讨论】:

    • 非常感谢!那很棒!!一个问题,API可以返回Char数组中的数据吗?
    • AFAIK,是的。非托管 API 可以像使用任何其他内存一样使用指向的内存,因为它是非托管的。在这种情况下,您可能希望编组回结构以将结果返回到托管代码中。
    • 这很好....但是我面临另一个问题,现在我想将数据传递给 API(比如上述 API 的 WriteToAPI 版本)我现在将数据传递给 API。但由于这个 Char 数据的大小不是 255(我想传递可变大小的数据),它会给出错误“类型无法编组,因为嵌入式数组实例的长度与布局中声明的长度不匹配”
    • 不幸的是,您无法编组可变长度数组。它们必须是固定大小的。但是,您可以通过为此目的分配非托管内存来实现该功能。我会用一个例子来更新我的答案。
    • 你更新了代码示例吗?我看不出区别。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-09-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多