了解使用fixed {},Marshal.AllocHGlobal()和GCHandle.Alloc()之间的区别

Hyp*_*ped 15 c# pinvoke marshalling

首先让我说看看我在整个论坛和网络上的许多链接中找到了固定{},Marshal.AllocHGlobal()和GCHandle.Alloc()的使用说明.但是,我还没有找到关于何时使用Marshal类与GCHandle类(使用和不使用fixed {})的简明解释.

我正在使用第三方.NET库,它在"Buffer"类中有一个名为Readline()的方法.该手册显示了以下函数原型:

bool ReadLine(int x1,int y1,int x2,int y2,System.IntPtr bufData,out int numRead);

描述bufData,说明:...内存区域的字节数必须大于或等于行长度乘以BytesPerPixel属性返回的值.

现在,后来在他们的用户手册,给访问该缓冲区(我已经调整了我的具体的例子一点点)的例子:

// Create an array large enough to hold one line from buffer
int size = 640; 
byte[] dataLine = new byte[size * 2];   // 2 bytes per pixel

// Pin the array to avoid Garbage collector moving it  
GCHandle dataLineHandle = GCHandle.Alloc(dataLine, GCHandleType.Pinned); 
IntPtr dataLineAddress = dataLineHandle.AddrOfPinnedObject(); 
Run Code Online (Sandbox Code Playgroud)

我可以按照上面的"示例"代码:

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

// Unpin the array 
dataLineHandle.Free() 
Run Code Online (Sandbox Code Playgroud)

这可能是故事的结尾(我还没有测试上面的代码),但我最终搜索了GCHandle类,这使我顺利进入.NET互操作性,pInvoke等.

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

IntPtr dataLineAddress = Marshal.AllocHGlobal( size * 2 );
Run Code Online (Sandbox Code Playgroud)

并将其传递给ReadLine()?

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

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);
}
Run Code Online (Sandbox Code Playgroud)

我感兴趣的是任何人都可以阐明上述技术,并指出我在实施中的错误,并指出上述方法是否合适.最后,即使上述方法都是有效的,过去几年是否会对一种方法或另一种方法进行普遍推动?

提前致谢!!炒作

Han*_*ant 4

好吧,替代方案也可能有效。但 Marshal.AllocHGlobal 示例并不完整,您现在已经在非托管内存中获得了数据。您仍然需要将其放入托管对象(数组)中,以便可以轻松访问它,您必须调用 Marshal.Copy()。效率低下,因为复制数据两次。并且不要忘记调用 Marshal.FreeHGlobal()。

固定示例与供应商示例执行相同的操作,它隐式固定内存。这里的尴尬之处在于 API 采用 IntPtr,而不是字节*。并且您必须更改编译设置以允许使用unsafe关键字。否则它并没有效率更高。

如果你以不同的方式做这件事,你并不会领先。