29

我想从 IntPtr 指针获取数据到字节数组中。我可以使用以下代码来做到这一点:

IntPtr intPtr = GetBuff();
byte[] b = new byte[length];
Marshal.Copy(intPtr, b, 0, length);

但是上面的代码强制从 IntPtr 到字节数组的复制操作。当有问题的数据很大时,这不是一个好的解决方案。

有没有办法将 IntPtr 转换为字节数组?例如,以下工作是否有效:

byte[] b = (byte[])intPtr

这将消除对复制操作的需要。

另外:我们如何确定 IntPtr 指向的数据长度?

4

4 回答 4

23

正如其他人所提到的,您无法在不复制的情况下将数据存储在托管 byte[]中(使用您提供的当前结构*)。但是,如果您实际上并不需要它位于托管缓冲区中,则可以使用unsafe操作直接使用非托管内存。这真的取决于你需要用它做什么。

所有byte[]和其他引用类型都由 CLR 垃圾收集器管理,它负责在不再使用时分配内存和解除分配。返回指向的内存GetBuffer是由 C++ 代码分配的一块非托管内存,并且(内存布局/实现细节除外)本质上与您的 GC 托管内存完全分离。因此,如果您想使用 GC 托管 CLR 类型 ( byte[]) 来包含当前保存在您的 指向的非托管内存中的所有数据IntPtr,则需要将其移动(复制)到 GC 知道的内存中。这可以通过Marshal.Copy使用unsafe代码或 pinvoke 或您拥有的自定义方法或通过自定义方法来完成。

但是,这取决于您想用它做什么。你提到它是视频数据。如果您想对数据应用一些转换或过滤器,您可以直接在非托管缓冲区上进行。如果要将缓冲区保存到磁盘,则可以直接在非托管缓冲区上进行。

关于长度,没有办法知道非托管内存缓冲区的长度,除非分配缓冲区的函数也告诉你长度是多少。正如评论者所提到的,这可以通过多种方式完成(结构的第一个字段,方法的输出参数)。

*最后,如果您可以控制 C++ 代码,则可以对其进行修改,使其不负责分配写入数据的缓冲区,而是提供一个指向预分配缓冲区的指针。然后,您可以在 C# 中创建一个托管 byte[]的,预先分配到 C++ 代码所需的大小,并使用该GCHandle类型来固定它并提供指向您的 C++ 代码的指针。

于 2012-03-16T06:46:47.457 回答
9

试试这个:

byte* b = (byte*)intPtr;

需要不安全(在函数签名、块或编译器标志中/unsafe)。

于 2012-03-16T06:04:28.943 回答
3

您不能让托管数组占用非托管内存。您可以一次复制一个非托管数据块,然后处理每个块,或者创建一个UnmanagedArray接受IntPtr并提供索引器的类,该索引器仍将使用 Marshal.Copy 访问数据。

正如@Vinod 所指出的,您可以使用unsafe代码执行此操作。这将允许您使用类似 C 的指针直接访问内存。但是,在调用任何不安全的 .NET 方法之前,您需要将数据编组到托管内存中,因此您几乎只能使用自己的 C 类代码。我认为您根本不应该为此烦恼,只需用 C++ 编写代码即可。

于 2012-03-16T06:04:10.483 回答
2

查看此代码项目页面,了解使用非托管数组的解决方案。

于 2012-03-16T06:15:39.860 回答