从编组的C ++结构到C#数组?数组、结构

2023-09-03 21:08:26 作者:默泪-

在我的C#code我试图从一个传统的C ++ DLL(在code我不能改变)取结构的数组。

In my C# code I'm trying to fetch an array of structures from a legacy C++ DLL (the code I cannot change).

在C ++中code,结构的定义是这样的:

In that C++ code, the structure is defined like this:

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

这是我打电话(get_my_structures)该方法返回一个指向MYSTRUCT结构的数组:

The method that I'm calling (get_my_structures) returns a pointer to an array of MyStruct structures:

MyStruct* get_my_structures()
{
    ...
}

有返回原状,数量,所以我不知道有多少的结构得到恢复的另一种方法。

There is another method that returns the number of stuctures so I do know how many structures get returned.

在我的C#code,我已经定义MYSTRUCT是这样的:

In my C# code, I have defined MyStruct like this:

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

互操作的签名看起来是这样的:

The interop signature looks like this:

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

最后,code表示取MYSTRUCT结构数组是这样的:

Finally, the code that fetches the array of MyStruct structures looks like this:

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成员伪造值。该值没有完全丢弃,或者看起来是这样:他们是从一些其他的存储位置的字符串。 C本身的$ C $不会崩溃。

The trouble is, only the very first structure (one at the offset zero) gets marshaled correctly. Subsequent ones have bogus values in _id and _description members. The values are not completely trashed, or so it seems: they are strings from some other memory locations. The code itself does not crash.

我已经验证,在C ++中get_my_structures code()不会返回正确的数据。该数据不会被意外删除或过程中或通话之后修改。

I have verified that the C++ code in get_my_structures() does return correct data. The data is not accidentally deleted or modified during or after the call.

在调试器中观察,返回的数据的C ++内存布局是这样的:

Viewed in a debugger, C++ memory layout of the returned data looks like this:

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

[更新18/11/2009]

下面是如何在C ++ code prepares这些结构(实际code更恶心,但这是一个足够接近近似值):

Here is how the C++ code prepares these structures (the actual code is much uglier, but this is a close enough approximation):

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;

不可否认,code以上做了一些丑陋的铸造和拷贝原始指针周围,但它仍然看起来正确地做到这一点。至少这是我在调试器中看到:上述(静态)缓存确实包含存储此起彼伏所有这些赤裸裸的char *指针,它们指向有效的(非本地)的内存位置

Admittedly, the code above does some ugly casting and copies raw pointers around, but it still does seem to do that correctly. At least that's what I see in the debugger: the above (static) buffer does contain all these naked char* pointers stored one after another, and they point to valid (non-local) locations in memory.

帕维尔的例子表明,这是真的,事情可能出错的地方。我将试图分析这些结束的位置,其中串真的是,不要在那里指针值存储的位置会发生什么。

Pavel's example shows that this is really the only place where things can go wrong. I will try to analyze what happens with those 'end' locations where the strings really are, not the locations where the pointers get stored.

推荐答案

我无法重现你的问题,这使我怀疑它是真正的东西C ++的一面。下面是完整的源$ C ​​$ C我尝试。

I cannot reproduce your problem, which leads me to suspect that it's really on C++ side of things. Here's the complete source code for my attempt.

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的/平台:86

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个结构。

This correctly prints out all 3 structs.

你能证明你的C ++ code,填补了结构?您可以从C ++直接调用它,并得到正确的结果,但这并不一定意味着它是正确的。例如,你可能会返回一个指针堆栈分配结构。当进行直接调用,那么,你会得到一个技术上无效的指针,但数据可能会保持preserved。在做的P / Invoke编组,堆栈可以用P覆盖/调用数据结构由它试图从那里读取值的点。

Can you show your C++ code that fills the structs? The fact that you can call it from C++ directly and get correct results does not necessarily mean it's correct. For example, you could be returning a pointer to a stack-allocated struct. When doing a direct call, then, you'd get a technically invalid pointer, but the data would likely remain preserved. When doing P/Invoke marshalling, the stack could be overwritten by P/Invoke data structures by the point it tries to read values from there.

 
精彩推荐
图片推荐