gpt4 book ai didi

c# - 如何将在 C# 列表中创建的对象的地址传递给 C++ dll?

转载 作者:太空狗 更新时间:2023-10-29 20:17:37 25 4
gpt4 key购买 nike

我一直在谷歌上寻找我的问题的一些答案,但不太明白我找到了什么。在使用 System.IO 读取一些文本文件后,我有一些对象被创建并存储在 C# List 中。之后,我想将对这些对象中的每一个的引用(使用 const 指针)发送到 C++ dll 中的内部类,以便它可以使用它们来计算某些算法。

这是我正在做的一些简单示例(不是实际代码):

C#类:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class SimpleClass
{
[MarshalAs(UnmanagedType.LPStr)]
public string Name;
public float Length;
}

与相应的 C 结构:

struct SimpleClass
{
const char* Name;
float Length;
};

存储在

List<SimpleClass> ItemList;

在解析一些文本文件之后。

然后调用下面的dll函数:

C#:

[DllImport("SimpleDLL")]
public static extern void AddSimpleReference(SimpleClass inSimple);

C:

void AddSimpleReference(const SimpleClass* inSimple)
{
g_Vector.push_back(inSimple); // g_Vector is a std::vector<const SimpleClass* > type
}

我试过的是:

for(int i=0; i<ItemList.Count;++i)
{
SimpleClass theSimpleItem = ItemList[i];
AddSimpleReference(theSimpleItem);
}

最初,我认为只需使用赋值运算符就可以很容易地获得实际的引用/地址,因为 C# 中的类是按引用传递的,但事实证明,C++ 类总是推送相同的地址值 (临时引用的地址值)放入容器而不是实际地址。如何获取实际对象地址并将其发送到 C++ DLL,以便它可以对 C# 对象进行只读访问?

更新:对于使用不安全代码发布答案的人深表歉意。我忘了提到 C# 代码实际上用作 Unity 游戏引擎中的脚本,不允许使用不安全的代码。

最佳答案

首先,您需要更改互操作签名以采用指针(因此使其不安全)。

[DllImport("SimpleDLL")]
public unsafe static extern void AddSimpleReference(SimpleClass* inSimple);

然后,因为 GC 可以随意在内存中移动对象,所以您需要在整个时间内将对象固定在内存中,您将需要它在非托管端的地址。为此,您需要 fixed 语句:

SimpleClass theSimpleItem = ItemList[i];
unsafe
{
fixed(SimpleClass* ptr = &theSimpleItem)
{
AddSimpleReference(ptr);
}
}

如果 AddSimpleReference 使用指针然后丢弃它,这将起作用。但是您将指针存储在 std::vector 中以备后用。这是行不通的,因为一旦执行离开 fixed block ,由于 GC 将原始项目移动到其他地方,指针可能会变得无效。

要解决此问题,您需要固定这些项目,直到您完成它们为止。为此,您可能需要求助于 GCHandle类型。

// Change the interop signature, use IntPtr instead (no "unsafe" modifier)
[DllImport("SimpleDLL")]
public static extern void AddSimpleReference(IntPtr inSimple);

// ----
for(int i=0; i<ItemList.Count;++i)
{
SimpleClass theSimpleItem = ItemList[i];
// take a pinned handle before passing the item down.
GCHandle handle = GCHandle.Alloc(theSimpleItem, GCHandleType.Pinned);
AddSimpleReference(GCHandle.ToIntPtr(handle));
// probably a good idea save this handle somewhere for later release
}

// ----
// when you're done, don't forget to ensure the handle is freed
// probably in a Dispose method, or a finally block somewhere appropriate
GCHandle.Free(handle);

当做这样的事情时,请记住将对象长时间固定在内存中是一个坏主意,因为它会阻止垃圾收集器有效地完成其工作。

关于c# - 如何将在 C# 列表中创建的对象的地址传递给 C++ dll?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6341023/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com