C# 了解固定{}、Marshal.AllocHGlobal()和GCHandle.Alloc()使用之间的差异

C# 了解固定{}、Marshal.AllocHGlobal()和GCHandle.Alloc()使用之间的差异,c#,pinvoke,marshalling,C#,Pinvoke,Marshalling,首先,我要说的是,我已经在本论坛和web上的许多链接中找到了关于fixed{}、Marshal.AllocHGlobal()和GCHandle.Alloc()使用的描述。然而,我还没有找到一个关于何时使用Marshal类和GCHandle类(使用和不使用fixed{})的简明解释 我使用的是第三方.NET库,它在“Buffer”类中有一个名为Readline()的方法。本手册显示了以下功能原型: bool ReadLine(intx1、inty1、intx2、inty2、System.IntPt

首先,我要说的是,我已经在本论坛和web上的许多链接中找到了关于fixed{}、Marshal.AllocHGlobal()和GCHandle.Alloc()使用的描述。然而,我还没有找到一个关于何时使用Marshal类和GCHandle类(使用和不使用fixed{})的简明解释

我使用的是第三方.NET库,它在“Buffer”类中有一个名为Readline()的方法。本手册显示了以下功能原型:

bool ReadLine(intx1、inty1、intx2、inty2、System.IntPtr bufData、out int numRead)

对bufData的描述是:…内存区域的字节数必须大于或等于行长度乘以 字节/像素属性

现在,在用户手册的后面部分,他们给出了一个访问缓冲区的示例(在我的具体示例中,我对其进行了一些调整):

我可以遵循上面的“示例”代码:

// Read one line of buffer data 
success = buffer.ReadLine(0, 0, 639, 0, dataLineAddress, out numRead); 

// Unpin the array 
dataLineHandle.Free() 
这可能是故事的结尾(我还没有测试上面的代码),但我最终在谷歌上搜索了GCHandle类,这让我走上了.NET互操作性、pInvoke等的道路

所以我的问题。。。 1) 为什么我不能使用:

IntPtr dataLineAddress = Marshal.AllocHGlobal( size * 2 );
并将其传递到ReadLine()

2) 我还可以使用以下代码片段(从web上的示例中提取和调整):

我希望任何人都能了解上述技术,指出我在实现中的错误,并指出上述方法何时适用。最后,即使上述方法都是有效的,在过去的几年里,是否有一种方法被普遍采用

提前谢谢!!
大肆宣传

好吧,替代方案可能也会奏效。但是Marshal.AllocHGlobal示例尚未完成,您现在已将数据保存在非托管内存中。您仍然需要将其放入托管对象(数组)中,以便轻松访问它,您必须调用Marshal.Copy()。效率低下,因为这样会将数据复制两次。别忘了调用Marshal.FreeHGlobal()

固定样本与供应商样本做相同的事情,它隐式地固定内存。这里的尴尬之处在于API采用的是IntPtr,而不是字节*。您必须更改编译设置以允许使用不安全的关键字。否则它的效率不会更高


用不同的方式做这件事并没有让你领先。

这听起来像是一个可怕的API。@SLaks我很高兴你对此发表了评论。:-)Hans-如果我调用的是一个非托管DLL函数,它需要一个字节指针指向一个它将“填充”的内存块,那么在使用“封送”和“GCHandle”时是否有偏好?谢谢你上面的回答。@Hyped-你的首选应该是让P/Invoke marshaller来做。这几乎总是可能的。在示例中,您应该将参数声明为数组。类似于byte[]或PixelData[],其中PixelData是具有两个字节成员的结构。
IntPtr dataLineAddress = Marshal.AllocHGlobal( size * 2 );
int size = 640;
byte[] dataLine= new byte[size * 2];  // 2 bytes per pixel

// prevent garbage collector from moving buffer around in memory
fixed (byte* fixedDataLine = dataLine)
{
  // get IntPtr representing address of first buffer element
  IntPtr dataLineAddress= Marshal.UnsafeAddrOfPinnedArrayElement(fixedDataLine , 0);
  success = buffer.ReadLine(0, 0, 639, 0, dataLineAddress, out numRead);
}