向IntPtr添加偏移量

时间:2009-12-08 11:02:12

标签: c# .net pinvoke interop

我正在寻找一种在C#或.NET中执行指针操作的方法。

我想做一些非常简单的事情

有一个指针IntPtr我想获得指向前面2个字节的IntPtr对象。

我读了一些帖子,说明这个愚蠢的代码片段会起作用......

IntPtr ptr = new IntPtr(oldptr.ToInt32() + 2);

但是我怀疑这个语句对于64位机器是否也有效(因为那里的地址是64位)..

我发现这种优雅的方法可以添加偏移量,但不幸的是只在.NET 4.0中http://msdn.microsoft.com/en-us/library/system.intptr.add%28VS.100%29.aspx

7 个答案:

答案 0 :(得分:44)

在.net 4中添加了静态Add()和Subtract()方法。

IntPtr ptr = IntPtr.Add(oldPtr, 2);

http://msdn.microsoft.com/en-us/library/system.intptr.add.aspx

答案 1 :(得分:37)

我建议您使用ToInt64()并长时间执行计算。这样就可以避免在64位版本的.NET框架上出现问题。

IntPtr ptr = new IntPtr(oldptr.ToInt64() + 2);

这会在32位系统上增加一些开销,但它更安全。

答案 2 :(得分:9)

对于C#中的指针算法,您应该在unsafe上下文中使用正确的指针:

class PointerArithmetic
{
    unsafe static void Main() 
    {
        int* memory = stackalloc int[30];
        long* difference;
        int* p1 = &memory[4];
        int* p2 = &memory[10];

        difference = (long*)(p2 - p1);

        System.Console.WriteLine("The difference is: {0}", (long)difference);
    }
}

IntPtr类型用于传递句柄或指针,也用于编组支持指针的语言。但它不适用于指针运算。

答案 3 :(得分:8)

我发现我可以通过使用Marshal.ReadByte(),Marshal.ReadInt16()等方法来避免指针操作。这组方法允许指定相对于IntPtr的偏移...

答案 4 :(得分:6)

public static class IntPtrExtensions
{
    #region Methods: Arithmetics
    public static IntPtr Decrement(this IntPtr pointer, Int32 value)
    {
        return Increment(pointer, -value);
    }

    public static IntPtr Decrement(this IntPtr pointer, Int64 value)
    {
        return Increment(pointer, -value);
    }

    public static IntPtr Decrement(this IntPtr pointer, IntPtr value)
    {
        switch (IntPtr.Size)
        {
            case sizeof(Int32):
                return (new IntPtr(pointer.ToInt32() - value.ToInt32()));

            default:
                return (new IntPtr(pointer.ToInt64() - value.ToInt64()));
        }
    }

    public static IntPtr Increment(this IntPtr pointer, Int32 value)
    {
        unchecked
        {
            switch (IntPtr.Size)
            {
                case sizeof(Int32):
                    return (new IntPtr(pointer.ToInt32() + value));

                default:
                    return (new IntPtr(pointer.ToInt64() + value));
            }
        }
    }

    public static IntPtr Increment(this IntPtr pointer, Int64 value)
    {
        unchecked
        {
            switch (IntPtr.Size)
            {
                case sizeof(Int32):
                    return (new IntPtr((Int32)(pointer.ToInt32() + value)));

                default:
                    return (new IntPtr(pointer.ToInt64() + value));
            }
        }
    }

    public static IntPtr Increment(this IntPtr pointer, IntPtr value)
    {
        unchecked
        {
            switch (IntPtr.Size)
            {
                case sizeof(int):
                    return new IntPtr(pointer.ToInt32() + value.ToInt32());
                default:
                    return new IntPtr(pointer.ToInt64() + value.ToInt64());
            }
        }
    }
    #endregion

    #region Methods: Comparison
    public static Int32 CompareTo(this IntPtr left, Int32 right)
    {
        return left.CompareTo((UInt32)right);
    }

    public static Int32 CompareTo(this IntPtr left, IntPtr right)
    {
        if (left.ToUInt64() > right.ToUInt64())
            return 1;

        if (left.ToUInt64() < right.ToUInt64())
            return -1;

        return 0;
    }

    public static Int32 CompareTo(this IntPtr left, UInt32 right)
    {
        if (left.ToUInt64() > right)
            return 1;

        if (left.ToUInt64() < right)
            return -1;

        return 0;
    }
    #endregion

    #region Methods: Conversion
    public unsafe static UInt32 ToUInt32(this IntPtr pointer)
    {
        return (UInt32)((void*)pointer);
    }

    public unsafe static UInt64 ToUInt64(this IntPtr pointer)
    {
        return (UInt64)((void*)pointer);
    }
    #endregion

    #region Methods: Equality
    public static Boolean Equals(this IntPtr pointer, Int32 value)
    {
        return (pointer.ToInt32() == value);
    }

    public static Boolean Equals(this IntPtr pointer, Int64 value)
    {
        return (pointer.ToInt64() == value);
    }

    public static Boolean Equals(this IntPtr left, IntPtr ptr2)
    {
        return (left == ptr2);
    }

    public static Boolean Equals(this IntPtr pointer, UInt32 value)
    {
        return (pointer.ToUInt32() == value);
    }

    public static Boolean Equals(this IntPtr pointer, UInt64 value)
    {
        return (pointer.ToUInt64() == value);
    }

    public static Boolean GreaterThanOrEqualTo(this IntPtr left, IntPtr right)
    {
        return (left.CompareTo(right) >= 0);
    }

    public static Boolean LessThanOrEqualTo(this IntPtr left, IntPtr right)
    {
        return (left.CompareTo(right) <= 0);
    }
    #endregion

    #region Methods: Logic
    public static IntPtr And(this IntPtr pointer, IntPtr value)
    {
        switch (IntPtr.Size)
        {
            case sizeof(Int32):
                return (new IntPtr(pointer.ToInt32() & value.ToInt32()));

            default:
                return (new IntPtr(pointer.ToInt64() & value.ToInt64()));
        }
    }

    public static IntPtr Not(this IntPtr pointer)
    {
        switch (IntPtr.Size)
        {
            case sizeof(Int32):
                return (new IntPtr(~pointer.ToInt32()));

            default:
                return (new IntPtr(~pointer.ToInt64()));
        }
    }

    public static IntPtr Or(this IntPtr pointer, IntPtr value)
    {
        switch (IntPtr.Size)
        {
            case sizeof(Int32):
                return (new IntPtr(pointer.ToInt32() | value.ToInt32()));

            default:
                return (new IntPtr(pointer.ToInt64() | value.ToInt64()));
        }
    }

    public static IntPtr Xor(this IntPtr pointer, IntPtr value)
    {
        switch (IntPtr.Size)
        {
            case sizeof(Int32):
                return (new IntPtr(pointer.ToInt32() ^ value.ToInt32()));

            default:
                return (new IntPtr(pointer.ToInt64() ^ value.ToInt64()));
        }
    }
    #endregion
}

答案 5 :(得分:1)

您可以使用扩展方法:

public static IntPtrExtensions {
    public static IntPtr Add( this IntPtr ptr, int offSet ) {
        IntPtr ret = new IntPtr( ptr.ToInt64() + offSet );
        return ret;
    }
}
// ... somewhere else ...
IntPtr pointer = GetHandle().Add( 15 );

答案 6 :(得分:-1)

从MSDN:https://docs.microsoft.com/en-us/dotnet/api/system.intptr?view=netcore-3.1

“ IntPtr类型设计为整数,其大小为 特定于平台。也就是说,预期这种类型的实例是 32位硬件和操作系统上为32位,而32位硬件上为64位 64位硬件和操作系统。”

        // Summary:
        //     Gets the size of this instance.
        //
        // Returns:
        //     The size of a pointer or handle in this process, measured in bytes. The value
        //     of this property is 4 in a 32-bit process, and 8 in a 64-bit process. You can
        //     define the process type by setting the /platform switch when you compile your
        //     code with the C# and Visual Basic compilers.
        public static int Size { get; }

因此,如果当前代码在所有者进程的上下文中运行,则类型 IntPtr 直接继承所有者进程的指针大小,并且应该返回 IntPtr.Size 属性拥有过程中指针的大小。

由于以上所有陈述都是正确的,因此我们可以简单地使用基本指针算法

偏移量= index * IntPtr.Size

注意:唯一需要关注的是了解流程/应用程序目标平台和所使用的代码。例如:如果您的代码需要与像钩子一样的x86和x64进程进行交互,则需要遵守额外的安全性(如果只是其中之一),请使用纯C样式指针算法

再次从MSDN:

static void ReadWriteIntPtr()
{
    // Allocate unmanaged memory. 
    int elementSize = Marshal.SizeOf(typeof(IntPtr));
    IntPtr unmanagedArray = Marshal.AllocHGlobal(10 * elementSize);

    // Set the 10 elements of the C-style unmanagedArray
    for (int i = 0; i < 10; i++)
    {
        Marshal.WriteIntPtr(unmanagedArray, i * elementSize, ((IntPtr)(i + 1)));
    }
    Console.WriteLine("Unmanaged memory written.");

    Console.WriteLine("Reading unmanaged memory:");
    // Print the 10 elements of the C-style unmanagedArray
    for (int i = 0; i < 10; i++)
    {
        Console.WriteLine(Marshal.ReadIntPtr(unmanagedArray, i * elementSize));
    }

    Marshal.FreeHGlobal(unmanagedArray);

    Console.WriteLine("Done. Press Enter to continue.");
    Console.ReadLine();
}