After some debate amost collegues, one of the other senior dev's suggested I write a wrapper and let the environment decide what it needs to do itself.
After testing this, when using Marshal.SizeOf(typeof(CPtr)) it returned
which is exactly as expected. but then with the added bonus of never having to cast yourself.
public struct CPtr
{
public IntPtr InnerPointer { get; }
public static IntPtr Zero { get { return IntPtr.Zero; } }
public CPtr(IntPtr innerPointer)
{
InnerPointer = innerPointer;
}
public CPtr(long o) : this(new IntPtr(o))
{ }
public CPtr(uint o) : this(new IntPtr(o))
{ }
public CPtr(int o) : this(new IntPtr(o))
{ }
public static CPtr operator +(CPtr a, long b)
{
return new CPtr((long)a.InnerPointer + b);
}
public static CPtr operator +(CPtr a, IntPtr b) { return a + (long)b; }
public static CPtr operator +(CPtr a, CPtr b) { return a + b.InnerPointer; }
public static CPtr operator +(CPtr a, uint b) { return a + (long)b; }
public static CPtr operator +(CPtr a, int b) { return a + (long)b; }
public static CPtr operator -(CPtr a, long b) { return a + -b; }
public static CPtr operator -(CPtr a, IntPtr b) { return a + -(long)b; }
public static CPtr operator -(CPtr a, CPtr b) { return a - b.InnerPointer; }
public static CPtr operator -(CPtr a, uint b) { return a + -b; }
public static CPtr operator -(CPtr a, int b) { return a + -b; }
public static implicit operator CPtr(IntPtr ptr) { return new CPtr(ptr); }
public static implicit operator CPtr(long ptr) { return new CPtr(ptr); }
public static implicit operator CPtr(uint ptr) { return new CPtr(ptr); }
public static implicit operator CPtr(int ptr) { return new CPtr(ptr); }
public static implicit operator IntPtr(CPtr ptr) { return ptr.InnerPointer; }
public static implicit operator long (CPtr ptr) { return (long)ptr.InnerPointer; }
public static implicit operator uint (CPtr ptr) { return (uint)ptr.InnerPointer; }
public static implicit operator int (CPtr ptr) { return (int)ptr.InnerPointer; }
}