Just What Is an Intptr Exactly

Just what is an IntPtr exactly?

It's a "native (platform-specific) size integer." It's internally represented as void* but exposed as an integer. You can use it whenever you need to store an unmanaged pointer and don't want to use unsafe code. IntPtr.Zero is effectively NULL (a null pointer).

what is intptr?

It is the managed counterpart of void*.

You can cast to and from void* for usage in managed code without having to resort to unsafe code in managed layers, eg C#.

How do I check if a value type has a value of IntPtr.Zero

Since you are working on a custom renderer I assume you are using Xamarin.Forms.
You can't check for IntPtr.Zero here. The IntPtr is set internally by Xamarin.Forms. You have usually no access to the wrong pointer. So you can't see it's there but you receive an error when you access the Width property because it works internally with that pointer.

There are two possible causes for this error:

  1. You didn't initialize the control properly. So it contains null pointers.
  2. There is a bug in Xamarin.Forms. Test your code against other versions of Xamarin.Forms. If the error doesn't occur in a different version or you believe it's a bug then file it here.

dynamic way to free intPtr

No, the memory you allocate is non-GC handled memory, so it won't be freed (technically it will be freed when your program ends... But I don't think it's what you want).

What you can do is incapsulate the IntPtr in a disposable object.

public sealed class ArrayToIntPtr : IDisposable
{
public IntPtr Ptr { get; protected set; }

public ArrayToIntPtr(Array input)
{
if (input != null)
{
int s = Marshal.SizeOf(input.GetValue(0).GetType()) * input.Length;
Ptr = Marshal.AllocHGlobal(s);
}
else
{
Ptr = IntPtr.Zero;
}
}

~ArrayToIntPtr()
{
Dispose(false);
}

public void Dispose()
{
Dispose(true);
}

protected void Dispose(bool disposing)
{
if (Ptr != IntPtr.Zero)
{
Marshal.FreeHGlobal(Ptr);
Ptr = IntPtr.Zero;
}

if (disposing)
{
GC.SuppressFinalize(this);
}
}
}

(note that the class is sealed, so the Dispose(bool disposing) isn't virtual)

Proper IntPtr use in C#

You can use IntPtr objects this way:

int test = 55;

// Allocating memory for int
IntPtr intPointer = Marshal.AllocHGlobal(sizeof(int));

Marshal.WriteInt32(intPointer,test);

// sending intPointer to unmanaged code here

//Test reading of IntPtr object
int test2 = Marshal.ReadInt32(intPointer); // test2 would be equal 55

// Free memory
Marshal.FreeHGlobal(intPointer);

You can explore other Marshal method to understand how to write string, doubles and etc to IntPtr.

So words about your sample code - its not a good idea to dispose external allocated unmanaged object. You should dispose only object which you have allocated in class constructor. This is not strict rule but some kind of good practice.

How is IntPtr Marshalled?

It's a straight blit, no different conceptually from int or long. Obviously it's 4 bytes under x86, and 8 bytes under x64. There's no magic here.

Endianness is never an issue with p/invoke marshalling. The endianness is a property of the underlying machine. The unmanaged DLL that you call using p/invoke using the same endianness as your managed code, because they run on the same machine.

looping through IntPtr?

This is how to loop through an image pointed to using an IntPtr (pointer to native memory)

//assume this actually points to something (not zero!!)
IntPtr pNative = IntPtr.Zero;

//assume these are you image dimensions
int w=640; //width
int h=480; //height
int ch =3; //channels

//image loop
//use unsafe
//this is very fast!!
unsafe
{
for (int r = 0; r < h; r++)
{
byte* pI = (byte*)pNative.ToPointer() + r*w*ch; //pointer to start of row
for (int c = 0; c < w; c++)
{
pI[c * ch] = 0; //red
pI[c * ch+1] = 0; //green
pI[c * ch+2] = 0; //blue

//also equivalent to *(pI + c*ch) = 0 - i.e. using pointer arythmetic;
}
}
}

IntPtr vs. As Any in COM

The solution I adopted was the following:

1) Copy .idl to another folder

2) Replace the __int3264 with void* in the variables you want to be IntPtr

3) Build the .tlb file from the .idl one

4) Build a primary interop based on the .tlb file.

Voilá!

Regards,
Mauro.

Convert double[,] to IntPtr C#

There are various things wrong with that.

First, rows * columns is not the size of the data, it's only the total number of elements. The elements are not one byte each, but eight, or sizeof(double) if you prefer.

Second, p is updated by p = (IntPtr)(p.ToInt64() + IntPtr.Size); (ie advancing it by 4 or 8 bytes depending on how big pointers are in the current mode), but you've written columns * 8 (or, columns * sizeof(double)) bytes of data. Advancing p by less than columns * 8 makes the writes overwrite each other, so not all data ends up in the result. By the way, the complicated conversions here are actually not necessary, you can add directly to an IntPtr, since .NET 4.

Third, p is changed in the loop, which is not bad on its own, but it's done in a way that loses track of the original pointer. toReturn.cells = p; and Marshal.FreeHGlobal(p); use a p which does not refer to the area that you allocated, they use a p which now points just past the end of the data (well it would point there if p was updated by the right amount). The original p must be remembered.

Fourth, freeing the data before returning means that it now no longer exists, so whatever code this data is passed to, still doesn't have it: it has a pointer to nothing which will be invalid to use (it may accidentally work, but it's dangerous and wrong).

The first three points are easy to fix, but the last one needs a non-local change to how your application works: the memory cannot be freed here, but it should be freed at some point, namely when the user of the data is done with it.

Some fixes applied:

        int stride = columns * sizeof(double);
IntPtr p = Marshal.AllocHGlobal(rows * stride);
for (int i = 0; i < rows; i++) //rows
{
double[] temp = new double[columns];

for (int x = 0; x < columns; x++)
temp[x] = input.cells[i, x];

Marshal.Copy(temp, 0, p + stride * i, columns);
}

toReturn.cells = p;
return toReturn;

Keep in mind you should still free the memory at the appropriate time.



Related Topics



Leave a reply



Submit