如何在C#中从非托管内存中读取数据?

5
我希望您能够从C++创建的未托管的Foo结构体数组中,翻译出与C#相关的内容。以下是我的想法:
在C++方面:
extern "C" __declspec(dllexport) void* createFooDetector()
{
    return new FooDetector();
}

extern "C" __declspec(dllexport) void releaseFooDetector(void* fooDetector)
{
    FooDetector *fd = (FooDetector*)fooDetector;
    delete fd;
}

extern "C" __declspec(dllexport) int detectFoo(void* fooDetector, Foo **detectedFoos)
{
    FooDetector *fd = (FooDetector*)fooDetector;
    vector<Foo> foos;
    fd->detect(foos);

    int numDetectedFoos = foos.size();
    Foo *fooArr = new Foo[numDetectedFoos];
    for (int i=0; i<numDetectedFoos; ++i)
    {
        fooArr[i] = foos[i];
    }

    detectedFoos = &fooArr;

    return numDetectedFoos;
}

extern "C" __declspec(dllexport) void releaseFooObjects(Foo* fooObjects)
{
    delete [] fooObjects;
}

在C#方面: (我省略了一些花哨的代码,以便更好地从C#中调用C ++函数);
List<Foo> detectFooObjects()
{
    IntPtr fooDetector = createFooDetector();

    IntPtr detectedFoos = IntPtr.Zero;
    detectFoo(fooDetector, ref detectedFoos);

    // How do I get Foo objects from my IntPtr pointing to an unmanaged array of Foo structs?

    releaseFooObjects(detectedFoos);

    releaseFooDetector(fooDetector);
}

但我不知道如何从 IntPtr detectedFoos 中检索对象。应该有一种方法... 有什么提示吗?
更新:假设 Foo 是一个简单的检测矩形。
C++:
struct Foo
{
    int x;
    int y;
    int w;
    int h;
};

C#:
[StructLayout(LayoutKind.Sequential)]
public struct Foo
{
    public int x;
    public int y;
    public int width;
    public int height;
}

能否在释放非托管内存之前从非托管内存中读取并创建新的托管对象?

我不知道有多少个Foo对象会被检测到,因此在调用detectFoo()之前无法确定在C#中分配多少内存。这就是为什么我在C++中分配/释放内存并只传递指针的原因。但是在C#下,我无法检索到detectedFoo指针地址。我该怎么做?


2
这不会有任何进展。你可以获取一个指向Foo的指针,但这并不能帮助你。你不知道Foo对象的布局是什么样子的,只有C++编译器知道。你需要用C++/CLI语言编写一个包装类。 - Hans Passant
3个回答

2
你必须在C#项目中重新声明Foo。假设你知道Foo的数量和sizeof(Foo)的值,那么你应该能够使用System.Runtime.Interopservices.Marshal.PtrToStructure()一次一个地检索你的Foo结构体。

我不知道会被检测到多少个 Foo。那么 System.Runtime.Interopservices.Marshal.PtrToStructure() 应该不是一个选项,我猜? - Ben
1
你必须返回 Foo 的数量。没有人能对一个未知大小的数组指针做任何事情。 - Puppy
detectFoo() 返回检测到的 Foo 的数量。是这样吗? - pdriegen
@DeadMG:当然我知道检测到了多少个Foo,我会返回那个值的...只是我在上面的代码中忘记了。我不知道的是:在调用该方法之前有多少对象。因此,我无法提前在C#中分配内存。 - Ben

0

你需要在C#中重新定义你的结构体,这取决于你的结构体。你的结构体必须是可复制的(C#结构体的内存布局必须与C结构体相同)。

看一下“结构体封送”。

或者发布你真正的“Foo”结构体,我可以向你展示C#版本。

更新:

因为你的结构体似乎是可复制的,所以你可以简单地将指向非托管内存的指针转换为指向在C#中定义的结构体的指针:

如果你的应用程序使用不安全代码没有问题,你可以写:

unsafe List<Foo> detectFooObjects()
{
 List<Foo> res = new List<Foo>()
IntPtr fooDetector = createFooDetector();

IntPtr detectedFoos = IntPtr.Zero;
int nNumFoos = detectFoo(fooDetector, ref detectedFoos );
for(int i=0;i<nNumFoos;i++)
{
   Foo** ppDetectedFoos = detectedFoos.ToPointer();

   Foo* pFoo = *ppDetectedFoos
   res.Add(*pFoo); //copies the struct because is a struct
   ppDetectedFoos++:
}

releaseFooObjects(detectedFoos);

releaseFooDetector(fooDetector);
return res;
}

0

我最终通过使用C++/CLI包装类解决了我的问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接