传递结构时可以将指针编组到数组吗?
Posted
技术标签:
【中文标题】传递结构时可以将指针编组到数组吗?【英文标题】:Can a pointer be marshalled to an array when passing a struct? 【发布时间】:2018-12-25 05:00:47 【问题描述】:在下面的示例中,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 的答案,但这会导致所有数据都被复制 - 两次。这是我试图避免的双重副本。
另一个编辑: 虽然看起来对于包含单个数组的结构,值可以正确传递,但对于复杂的结构,垃圾会被扔进去。 我更新了代码以反映更复杂的结构!
【问题讨论】:
请定义“不起作用”。 垃圾数据。现在将编辑int* dataArray;
-- 对我们来说,这是垃圾数据。我们不知道这个指针指向什么,因为它是未初始化的。也许您认为您将其设置为一个局部变量(因此不再存在)。
在别处初始化为数组?我从一个单独的 DLL 接收它,文档将它称为指向数组的指针。
@NonCreature0714 因为它不仅仅是 1 个数组,它是一个复杂的结构,包含从 DLL 接收的数据(所以我无法更改它)
【参考方案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;
代码在行动
【讨论】:
这很有趣!但它没有用。等我更新问题 @Mars,你能告诉我什么不工作吗?我有这段代码没有任何问题 我更新了代码。请使用更复杂的结构重试 :) @Mars,是的,数组变成了垃圾值 @Mars,我刚刚点击了你的个人资料,希望能得到一些关于你住在哪里的信息,因为你说你在我离开时要离开办公室?原来我们都在日本【参考方案2】:我在 C# 方面的经验非常有限,但似乎它可以与 int dataArray[5]; 一起使用,因为代码符合 CSharpFoo 和 CPPFoo 的类型。两者的大小都是五个整数。当您将类型更改为指针时,一个是五个整数的大小,另一个是一个小于五个整数的单个指针。
很可能传递一个动态大小的数组需要一个大小,并且要获得一个指针,您可能需要使用不安全的代码做一些事情,但我不是用 C# 编写的,所以这只是一个猜测。
struct CPPFoo
int* dataArray = nullptr;
int size = 0;
【讨论】:
谢谢。将其作为固定大小的数组传递并非不可能,但这意味着将数据复制两次,这不太理想 我在想你可以传递原始数组的地址,这就是我想到 C# 不安全块的原因。能否分配地址和数组的大小。然后 C++ 代码可以将其推入 vectorunsafe
没有启用,我不知道客户端会允许它
会像 int[] = new int[size];
抱歉,我还没准备好就按了最后一条评论的输入。我认为 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5] 可能是需要更改的。可以在 C# 中使用类似以下的内容吗?没有元帅会发生什么...? public struct CSharpFoo public int[] dataArray;公共整数数据大小;【参考方案3】:
使用向量。
#include <vector>
struct CPPFoo
std::vector<int> dataArray;
;
Vector 更具可读性和安全性。它只是略微增加了代码的大小,并且只比使用原始数组稍微慢一点。您也不必担心保留一个大小变量,并在每次更改时对其进行更新。
【讨论】:
以上是关于传递结构时可以将指针编组到数组吗?的主要内容,如果未能解决你的问题,请参考以下文章
将结构数组从 C#(.NET Core) 传递到 C++(未管理)
C:为啥你可以通过值传递(给函数)一个结构,而不是一个数组?