传递结构时可以将指针编组到数组吗?

时间:2018-12-25 05:00:47

标签: c# c++ marshalling

在下面的示例中,如果将C ++中定义的dataArray定义为数组而不是指针,则可以正常工作(仅产生垃圾数据)。是否还有另一种方法来封送C#数组,以便它以数组形式读取指针?

C#

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct CSharpFoo{
    int alpha;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5]
    public int[] dataArray;
    int beta;
}

C ++

struct CPPFoo{
    int alpha;
    //int* dataArray; //Doesn't work, even though initialized to an array elsewhere
    int dataArray[5];
    int beta;
}

通过这样的函数
C#

[DllImport("MyDll.dll", CallingConvention = CallingConvention.Cdecl, BestFitMapping = false, ThrowOnUnmappableChar = true)]
public extern static bool InitializeDLL([MarshalAs(UnmanagedType.FunctionPtr)] ResultCallback callbackPointer);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void ResultCallback(CSharpFoo value);

C ++

//Callback
typedef void(__stdcall * ResultCallback)(CPPFoo);
__declspec(dllexport) bool InitializeDLL(ResultCallback callback);

谢谢!

编辑:: 因为尚不清楚“初始化为其他位置的数组”:

CPPFoo(int dummy){ //Constructor
    alpha = 32;
    dataArray = new int[5];
    for (int i = 0; i < 5; i++){
        dataArray[i] = i;
    }
    beta = 13;
}
//dataArray C++ {0,1,2,3,4}
//alpha C# 32
//dataArray C# {Total random garbage} (dataArray[3] is 13!)
//beta C# 0

PS,CPPFoo结构是来自DLL的复杂结构,因此我无法更改它。现在,为了使工作正常,我将其复制到更合适的数组中,如NonCreature0714的答案,但这导致所有数据被复制两次。我想避免的就是这个双重副本。

另一个编辑: 对于包含单个数组的结构,似乎可以正确传递值,而对于复杂的结构,则可能会抛出垃圾。 我已经更新了代码以反映更复杂的结构!

3 个答案:

答案 0 :(得分:1)

据我了解您的代码,我在本地计算机上进行了一些测试。我的以下代码没有问题

c#侧

struct Foo
{
    public int alpha;

    public IntPtr Data;

    public int beta;

    public void GetData(ref int[] buffer,int length)
    {
        Marshal.Copy(Data,buffer,0,length);
    }

}

班计划     {

    [DllImport("MyPtr.dll",EntryPoint = "InitializeDLL", CallingConvention = CallingConvention.Cdecl)]
    public static extern bool InitializeDLL([MarshalAs(UnmanagedType.FunctionPtr)]ResultCallback callbackPointer);

    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    public delegate void ResultCallback(ref Foo value);

    static void CallBackMe(ref Foo value)
    {
        var buffer = new int[5];
        value.GetData(ref buffer,buffer.Length);
    }
    static void Main(string[] args)
    {
        InitializeDLL(CallBackMe);
    }
}

C ++方面

struct CPPFoo {
    int* dataArrayPtr;
    CPPFoo()
    {
        dataArrayPtr = new int[5];
        for (int i = 0; i < 5; i++) {
            dataArrayPtr[i] = i;
        }
    }
};

typedef void(__stdcall * ResultCallback)(CPPFoo);
extern "C" __declspec(dllexport) bool InitializeDLL(ResultCallback callback)
{
    CPPFoo f;
    callback(f);
    return true;
}

有效代码 enter image description here

答案 1 :(得分:0)

我在C#中的经验非常有限,但似乎可以与 int dataArray [5]; 一起使用,因为代码与CSharpFoo和CPPFoo的类型一致。两者的大小均为5个整数。当您将类型更改为指针时,一个是五个整数的大小,另一个是单个指针,该大小小于五个整数。

最有可能传递一个动态大小的数组将需要一个大小,并且要获取一个指针,您可能需要使用不安全的代码来做一些事情,但是我不是用C#编写的,所以这只是一个猜测。

struct CPPFoo {
    int* dataArray = nullptr;
    int size = 0;
}

答案 2 :(得分:0)

使用向量。

#include <vector>

struct CPPFoo {
    std::vector<int> dataArray;
};

向量更易读且更安全。它只是轻微会增加代码的大小,并且只比使用原始数组慢一点。您也不必担心保持变量的大小,并在每次更改时对其进行更新。