Let me start by saying I've looked and found descriptions of the use of fixed{}, Marshal.AllocHGlobal()and GCHandle.Alloc() throughout this forum and in many links on the web. However, I have yet to find a concise explanation of when to use the Marshal class vs. the GCHandle class (with and without using fixed{}).
I'm using a third-party .NET library which has a method called Readline() in a "Buffer" class. The manual shows the following function prototype:
bool ReadLine(int x1, int y1, int x2, int y2, System.IntPtr bufData, out int numRead);
with a description of bufData that says: ...The memory area must have a number of bytes larger than or equal to the line length times the value returned by the BytesPerPixel property.
Now later in the user manual they do give an example of accessing the buffer (which I've tweaked a little bit for my specific example):
// 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();
and I could follow the above "example" code with:
// Read one line of buffer data
success = buffer.ReadLine(0, 0, 639, 0, dataLineAddress, out numRead);
// Unpin the array
dataLineHandle.Free()
That could be the end of the story (and I have yet to test the above code), but I ended up googling the GCHandle class which drove me down the path of .NET interoperability, pInvoke, etc.
So my questions... 1) Why can't I use:
IntPtr dataLineAddress = Marshal.AllocHGlobal( size * 2 );
and pass that into ReadLine()?
2) Can I also use the following snippet of code (extracted and tweaked from examples on the 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);
}
I'd be interested in anyone can shed light on the above techniques and point out my errors in implementation as well as point out when the above methods are appropriate. Finally, even if the above methods are all valid, is there a general push in the past few years toward one approach or the other?
Thanks in advance!! Hyped
Well, the alternative will likely work too. But the Marshal.AllocHGlobal sample isn't complete, you've now got the data in unmanaged memory. You still need to do work to get it into a managed object (array) so you can easily access it, you must call Marshal.Copy(). Inefficient since that copies the data twice. And don't forget to call Marshal.FreeHGlobal().
The fixed sample does the same thing as the vendor sample, it implicitly pins the memory. Awkwardness here is that the API takes an IntPtr, not a byte*. And you must change compile settings to allow the unsafe keyword. It isn't otherwise more efficient.
You're not ahead by doing this differently.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With