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

Posted

技术标签:

【中文标题】如何从 C# 中的非托管内存中读取数据?【英文标题】:How can I read from unmanaged memory in C#? 【发布时间】:2012-08-30 17:30:00 【问题描述】:

我想从 C++ 中创建的 Foo 结构的非托管数组在 C# 中创建 Foo 对象。 这就是我认为它应该工作的方式:

在 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# 下检索 detectedFoos 指针地址。我该怎么做?

【问题讨论】:

这不会去任何地方。您可以获得指向 Foo 的指针,但这对您没有帮助。您不知道 Foo 对象的布局是什么样的。只有 C++ 编译器知道。您需要用 C++/CLI 语言编写一个包装类。 【参考方案1】:

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

【讨论】:

不知道会检测到多少Foos。所以System.Runtime.Interopservices.Marshal.PtrToStructure() 不是一个选项,我猜? 您必须返回Foo 的号码。没有人可以对未知大小数组的指针做任何事情。 detectFoo() 返回检测到的 Foo 的数量。不是吗? @DeadMG:我当然知道检测到了多少 Foo,我返回了那个值。我只是在上面的代码中忘记了它。我不知道:调用方法之前的对象数量。所以我不能事先在 C# 中分配内存。【参考方案2】:

您必须在 C# 中再次定义您的结构,这取决于您的结构。您的结构必须是 blitable(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;

【讨论】:

【参考方案3】:

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

【讨论】:

以上是关于如何从 C# 中的非托管内存中读取数据?的主要内容,如果未能解决你的问题,请参考以下文章

c#从数据库中读取图片出现参数无效

当 CPU 尝试读取由 GPU 初始化的托管内存时,为啥数据会从主机迁移到设备?

c# 中,如何读取XML文件,并将读取到的内容显示到TreeView中

c# - 如何调用分配输出缓冲区以在c#中返回数据的非托管c++函数?

读取内存内存映射文件 C++ 和 C#

如何使用 C# 自动化从访问数据库(.accdb)中读取所有记录