Skip to content

part 2 of the joe shaw seminar series

9 February 2006

You thought you had avoided it, didn’t you?

Writing code in a managed environment created after 64-bit processors became common, you thought you had avoided all the issues dealing with 64-bit uncleanliness. And mostly, you have. It’s only when you interoperate with unmanaged code that it really gets you.

Code like this:

IntPtr elementPtr = (IntPtr) Marshal.ReadInt32 (arrayPtr, i*IntPtr.Size);

is never safe. It works for 32-bit because IntPtr is 32 bits and you’re reading in a 32-bit integer value, and it might even work on 64-bit depending on the exact memory address, but at some point it will bite you in the ass, as it did me yesterday.

The real funny thing about this is that it handles the offset in the array correctly by doing i*IntPtr.Size, but doesn’t read the right value. Simply changing this to:

IntPtr elementPtr = Marshal.ReadIntPtr (arrayPtr, i*IntPtr.Size);

avoids the problem entirely. Use the APIs they give you!

One thing I don’t quite understand, though, is why doesn’t the runtime throw an exception if you try to cast a negative number to an IntPtr?

Update: Dan pointed out on IRC that the reason the cast works is because IntPtr is a signed type, and that UIntPtr is the unsigned one. Which is fine. But why have a signed pointer type at all? I suspect that dealing with memory addresses is a lot more common as the primary use for IntPtr than dealing with architecture dependent values stored in, say, files on disk.