gpt4 book ai didi

c# - 将结构数组从 C++ 编码到 C#?

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:37:13 28 4
gpt4 key购买 nike

在我的 C# 代码中,我试图从遗留 C++ DLL(我无法更改的代码)中获取结构数组。

在该 C++ 代码中,结构定义如下:

struct MyStruct
{
char* id;
char* description;
};

我正在调用的方法 (get_my_structures) 返回指向 MyStruct 结构数组的指针:

MyStruct* get_my_structures()
{
...
}

还有另一种返回结构数量的方法,所以我知道返回了多少结构。

在我的 C# 代码中,我这样定义了 MyStruct:

[StructLayout(LayoutKind.Sequential)]  
public class MyStruct
{
[MarshalAsAttribute(UnmanagedType.LPStr)] // <-- also tried without this
private string _id;
[MarshalAsAttribute(UnmanagedType.LPStr)]
private string _description;
}

互操作签名如下所示:

[DllImport("legacy.dll", EntryPoint="get_my_structures")]
public static extern IntPtr GetMyStructures();

最后,获取 MyStruct 结构数组的代码如下所示:

int structuresCount = ...;
IntPtr myStructs = GetMyStructures();
int structSize = Marshal.SizeOf(typeof(MyStruct)); // <- returns 8 in my case
for (int i = 0; i < structuresCount; i++)
{
IntPtr data = new IntPtr(myStructs.ToInt64() + structSize * i);
MyStruct ms = (MyStruct) Marshal.PtrToStructure(data, typeof(MyStruct));
...
}

问题是,只有第一个结构(偏移量为零的结构)被正确编码。后续的在 _id 和 _description 成员中有虚假值。这些值并没有完全被丢弃,或者看起来是这样:它们是来自其他内存位置的字符串。代码本身不会崩溃。

我已验证 get_my_structures() 中的 C++ 代码确实返回了正确的数据。数据不会在通话期间或通话后被意外删除或修改。

在调试器中查看,返回数据的 C++ 内存布局如下所示:

0: id (char*)           <---- [MyStruct 1]
4: description (char*)
8: id (char*) <---- [MyStruct 2]
12: description (char*)
16: id (char*) <---- [MyStruct 3]
...

[2009 年 11 月 18 日更新]

这是 C++ 代码准备这些结构的方式(实际代码要丑得多,但这是一个足够接近的近似值):

static char buffer[12345] = {0};
MyStruct* myStructs = (MyStruct*) &buffer;
for (int i = 0; i < structuresCount; i++)
{
MyStruct* ms = <some other permanent address where the struct is>;
myStructs[i].id = (char*) ms->id;
myStructs[i].description = (char*) ms->description;
}
return myStructs;

不可否认,上面的代码进行了一些丑陋的转换并复制了原始指针,但它似乎仍然正确地做到了这一点。至少那是我在调试器中看到的:上面的(静态)缓冲区确实包含所有这些一个接一个存储的裸 char* 指针,它们指向内存中的有效(非本地)位置。

Pavel 的示例表明这确实是唯一可能出错的地方。我将尝试分析字符串真正所在的那些“结束”位置发生了什么,而不是指针存储的位置。

最佳答案

我无法重现您的问题,这使我怀疑它确实是 C++ 方面的问题。这是我尝试的完整源代码。

dll.cpp - 用 cl.exe/LD 编译:

extern "C" {

struct MyStruct
{
char* id;
char* description;
};

__declspec(dllexport)
MyStruct* __stdcall get_my_structures()
{
static MyStruct a[] =
{
{ "id1", "desc1" },
{ "id2", "desc2" },
{ "id3", "desc3" }
};
return a;

}

}

test.cs - 使用 csc.exe/platform:x86 编译:

using System;
using System.Runtime.InteropServices;


[StructLayout(LayoutKind.Sequential)]
public class MyStruct
{
[MarshalAsAttribute(UnmanagedType.LPStr)]
public string _id;
[MarshalAsAttribute(UnmanagedType.LPStr)]
public string _description;
}


class Program
{
[DllImport("dll")]
static extern IntPtr get_my_structures();

static void Main()
{
int structSize = Marshal.SizeOf(typeof(MyStruct));
Console.WriteLine(structSize);

IntPtr myStructs = get_my_structures();
for (int i = 0; i < 3; ++i)
{
IntPtr data = new IntPtr(myStructs.ToInt64() + structSize * i);
MyStruct ms = (MyStruct) Marshal.PtrToStructure(data, typeof(MyStruct));

Console.WriteLine();
Console.WriteLine(ms._id);
Console.WriteLine(ms._description);
}
}
}

这会正确打印出所有 3 个结构。

您能展示填充结构的 C++ 代码吗?您可以直接从 C++ 调用它并获得正确结果的事实并不一定意味着它是正确的。例如,您可能返回一个指向堆栈分配结构的指针。那么,在进行直接调用时,您会得到一个技术上无效的指针,但数据可能会保留下来。在执行 P/Invoke 编码时,当 P/Invoke 数据结构尝试从那里读取值时,堆栈可能会被 P/Invoke 数据结构覆盖。

关于c# - 将结构数组从 C++ 编码到 C#?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1748169/

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