如何从C#中的byte []获取IntPtr


127

我想将一个传递byte[]给采用IntPtrC#中的Parameter 的方法,这可能吗?如何?


您能否提供更多详细信息?你为什么要这么做?
Grzenio

2
例如,如果您使用DirectShow API,则需要此方法...要从VideoRenderer获取数据,则必须使用此方法。GCHandlefixed方法的工作原理也很吸引人。:P :))
Cipi

对于要传输terrabyte数据的任何事物,都需要使用此文件,并且要避免多余的副本。动用你的想象力。
Brain2000

Answers:


93

不确定是否要将IntPtr放入数组,但是可以使用Mashal.Copy复制数据以供非托管代码使用:

IntPtr unmanagedPointer = Marshal.AllocHGlobal(bytes.Length);
Marshal.Copy(bytes, 0, unmanagedPointer, bytes.Length);
// Call unmanaged code
Marshal.FreeHGlobal(unmanagedPointer);

或者,您可以声明具有一个属性的结构,然后使用Marshal.PtrToStructure,但这仍然需要分配非托管内存。

编辑:另外,正如Tyalis所指出的,如果不安全的代码是您的选择,您也可以使用固定的


需要澄清的是,Marshal.Copy这种过载需要一个开始索引。电话应该是Marshal.Copy(bytes, 0, unmanagedPointer, bytes.Length);
mkenyon

最好在不创建新内存的情况下获取IntPtr,例如@ user65157的答案。

208

其他方式,

GCHandle pinnedArray = GCHandle.Alloc(byteArray, GCHandleType.Pinned);
IntPtr pointer = pinnedArray.AddrOfPinnedObject();
// Do your stuff...
pinnedArray.Free();

1
@Cipi每一位Eric Lipperts的帖子,它都应该使用Fixed关键字而不是使用GC
goodguys_activate 2012年

非常感谢。工作很好,很简单。我对GCHandles和Marshal不太熟练。有人可以告诉我,元帅有什么优点,GCHandle有什么优点,在哪里使用了哪一个?谢谢
小猪

1
有人可以提供对Eric Lippert帖子的引用吗?
卡梅隆

3
@piggy:我认为元帅的缺点是您必须复制数据(这可能需要很长时间并且浪费您可能需要的内存)
Riki

6
@ makerofthings7我不相信Lippert在说使用“固定”而不是在[固定对象]的GC,我相信他是在说不要无限期地使用GC固定对象,还说不要使用固定来固定对象。一样。
卡梅隆

129

这应该可行,但必须在不安全的上下文中使用:

byte[] buffer = new byte[255];
fixed (byte* p = buffer)
{
    IntPtr ptr = (IntPtr)p;
    // do you stuff here
}

当心,您必须在固定块中使用指针!一旦您不再位于固定块中,gc便可以移动对象。


16
我喜欢这个答案,因为它不涉及分配额外的内存只是为了访问数据
Xcalibur

3
如果使用大字节[],这是最佳答案。副本的开销太大
goodguys_activate 2012年


13

这是@ user65157答案的一种说法(顺便说一句,顺便说一句):

我为固定对象创建了一个IDisposable包装器:

class AutoPinner : IDisposable
{
   GCHandle _pinnedArray;
   public AutoPinner(Object obj)
   {
      _pinnedArray = GCHandle.Alloc(obj, GCHandleType.Pinned);
   }
   public static implicit operator IntPtr(AutoPinner ap)
   {
      return ap._pinnedArray.AddrOfPinnedObject(); 
   }
   public void Dispose()
   {
      _pinnedArray.Free();
   }
}

然后像这样使用它:

using (AutoPinner ap = new AutoPinner(MyManagedObject))
{
   UnmanagedIntPtr = ap;  // Use the operator to retrieve the IntPtr
   //do your stuff
}

我发现这是不忘记调用Free()的好方法:


4
您可能需要研究从SafeHandle派生AutoPinner,因为该类考虑了并发性和安全性“陷阱”,以及鼓励/使用推荐的IDisposable模式。
kkahl '16

0

元帅。复制有效,但速度很慢。更快的是在for循环中复制字节。更快的方法是将字节数组转换为ulong数组,复制尽可能多的ulong到字节数组中,然后复制可能剩余的7个字节(未对齐8个字节的路径)。最快的方法是将字节数组固定在上面Tyalis的答案中提出的固定语句中。


-1
IntPtr GetIntPtr(Byte[] byteBuf)
{
    IntPtr ptr = Marshal.AllocHGlobal(byteBuf.Length);
    for (int i = 0; i < byteBuf.Length; i++)
    {
       Marshal.WriteByte(ptr, i, byteBuf[i]);
    }
    return ptr;
}

这是所接受答案的效率较低的副本。为什么要逐字节而不是一次复制?
BDL

我在代码中接受的答案中发现了错误。我在c#中调用了C ++ dll函数。char *参数用于c ++,因此我使用了可接受的方法。[DllImport(“ MyDll.dll”,EntryPoint =“ functionName”,CharSet = CharSet.Ansi,CallingConvention = CallingConvention.Cdecl)]但很奇怪,当使用接受的方法时,无法获得正确的IntPtr,我得到了一些缓冲区已损坏的消息,某些缓冲区显示为Unicode。所以我用了这种方法
nexdev

-6

在某些情况下,对于IntPtr,可以使用Int32类型(或Int64)。如果可以的话,另一个有用的类是BitConverter。对于您想要的内容,您可以使用例如BitConverter.ToInt32。


14
绝对不要使用Int32或Int64代替指针。如果必须将代码移植到其他平台(32位-> 64位),则会遇到各种麻烦。
xxbbcc 2012年

5
不,没有有效的情况下可以正确安全地使用a Int32作为指针。这是几年前做的不好的做法,它导致了各种移植问题。即使这样Int64也不安全,因为已经有128位架构,并且指针大小会增加。指针仅应表示为指针。
xxbbcc 2012年

1
我在.NET CF项目中使用了它,没有任何问题,如果尝试将其移植到其他系统上,当然会遇到问题,但是这里有代码永远都不会移植。
亚历杭德罗·梅斯夸

确实没有计划移植某些代码,但是事情很快就会改变。即使您在CF上的用法是合理的(我不知道),对于一个通用问题仍然是不好的建议。对于指针使用int/ 的唯一有效方案是所使用long的语言没有指针的概念时(例如VB6)。C#支持指针,并且具有IntPtr-根本不需要使用int指针代替指针。如果您在回答中添加清晰的警告和对潜在问题的解释,我将删除-1。
xxbbcc 2012年

3
好吧,您正在将非常特定的编组代码的用法与此有关在C#中使用指针的一般性问题进行比较。在普通的C#中,没有任何一种情况是有效的。我知道这是一个老问题,但我投了反对票,因为我通过寻找为IntPtr分配内存的方式碰到了它-其他人也会看到这一点。我认为您的建议非常危险,因为人们会以为他们会以为只要将来遇到的麻烦就轻松解决问题就可以了。
xxbbcc 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.