vla*_*mir 5 .net c# c++ interop marshalling
在我的C#代码中,我正在尝试从遗留C++ DLL中获取结构数组(代码我无法更改).
在该C++代码中,结构定义如下:
struct MyStruct
{
char* id;
char* description;
};
Run Code Online (Sandbox Code Playgroud)
我正在调用的方法(get_my_structures)返回一个指向MyStruct结构数组的指针:
MyStruct* get_my_structures()
{
...
}
Run Code Online (Sandbox Code Playgroud)
还有另一种方法可以返回结构的数量,因此我知道返回了多少结构.
在我的C#代码中,我已经定义了MyStruct:
[StructLayout(LayoutKind.Sequential)]
public class MyStruct
{
[MarshalAsAttribute(UnmanagedType.LPStr)] // <-- also tried without this
private string _id;
[MarshalAsAttribute(UnmanagedType.LPStr)]
private string _description;
}
Run Code Online (Sandbox Code Playgroud)
互操作签名如下所示:
[DllImport("legacy.dll", EntryPoint="get_my_structures")]
public static extern IntPtr GetMyStructures();
Run Code Online (Sandbox Code Playgroud)
最后,获取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));
...
}
Run Code Online (Sandbox Code Playgroud)
麻烦的是,只有第一个结构(偏移零点处的一个)才能正确编组.后续的在_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]
...
Run Code Online (Sandbox Code Playgroud)
[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;
Run Code Online (Sandbox Code Playgroud)
不可否认,上面的代码执行了一些丑陋的转换和复制原始指针,但它似乎仍然可以正确地做到这一点.至少这是我在调试器中看到的:上面的(静态)缓冲区确实包含一个接一个地存储的所有这些裸char*指针,并且它们指向内存中的有效(非本地)位置.
帕维尔的例子表明,这确实是唯一可能出错的地方.我将尝试分析字符串确实存在的那些"结束"位置会发生什么,而不是存储指针的位置.
您必须使用UnmanagedType.LPTStrchar*。还
以及 CharSet 规范:StringBuilder建议使用非常量 char*:
[StructLayout(LayoutKind.Sequential, Charset = CharSet.Auto)]
public class MyStruct
{
[MarshalAsAttribute(UnmanagedType.LPTStr)]
private StringBuilder _id;
[MarshalAsAttribute(UnmanagedType.LPTStr)]
private StringBuilder _description;
}
Run Code Online (Sandbox Code Playgroud)
至于DllImport声明,你尝试过吗
[DllImport("legacy.dll", EntryPoint="get_my_structures")]
public static extern MarshalAs(UnmanagedType.LPArray) MyStruct[] GetMyStructures();
Run Code Online (Sandbox Code Playgroud)
?
另外,如果前面的方法不起作用,请将其保留在 IntPtr 中并尝试对返回的结构进行 Mashal,如下所示:
for (int i = 0; i < structuresCount; i++)
{
MyStruct ms = (MyStruct) Marshal.PtrToStructure(myStructs, typeof(MyStruct));
...
myStructs += Marshal.SizeOf(ms);
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
7550 次 |
| 最近记录: |