我在尝试用C#中的FSCTL_GET_RETRIEVAL_POINTERS调用DeviceIOControl时遇到一个奇怪的问题。我已经尝试过Jeffrey Walls blog中的代码以及我自己的代码,但是仍然遇到相同的问题。问题似乎出在Extents中的RETRIEVAL_POINTERS_BUFFER结构中。在C ++中,当Extents[0]->NextVcn1Extents[0]->Lcn217550时,在C#中,我不断得到Extents[0]->NextVcn4294967296Extents[0]->Lcn934370135244800。与RETRIEVAL_POINTERS_BUFFER->StartingVcn相同,当它应为0时,在C#中将变为其他一些数字(如1012016)。

C#代码(不起作用)

        // Get cluster allocation information
        PInvoke.LARGE_INTEGER StartingVCN = new PInvoke.LARGE_INTEGER();
        PInvoke.RETRIEVAL_POINTERS_BUFFER Retrieval;
        IntPtr pDest = IntPtr.Zero;
        uint RetSize;
        uint Extents;
        uint BytesReturned = 0;

        // Grab info one extent at a time, until it's done grabbing all the extent data
        // Yeah, well it doesn't give us a way to ask L"how many extents?" that I know of ...
        // btw, the Extents variable tends to only reflect memory usage, so when we have
        // all the extents we look at the structure Win32 gives us for the REAL count!
        Extents = 10;
        RetSize = 0;

        const uint RETRIEVAL_POINTERS_BUFFER_SIZE = 28;

        StartingVCN.QuadPart = 0;

        GCHandle handle = GCHandle.Alloc(StartingVCN, GCHandleType.Pinned);
        IntPtr StartingVCNPtr = handle.AddrOfPinnedObject();

        do
        {
            Extents *= 2;
            RetSize = RETRIEVAL_POINTERS_BUFFER_SIZE + (uint)((Extents - 1) * Marshal.SizeOf(typeof(PInvoke.LARGE_INTEGER)) * 2);

            if (pDest != IntPtr.Zero)
                pDest = Marshal.ReAllocHGlobal(pDest, (IntPtr)RetSize);
            else
                pDest = Marshal.AllocHGlobal((int)RetSize);

            Result = PInvoke.DeviceIoControl
            (
                Handle,
                PInvoke.FSConstants.FSCTL_GET_RETRIEVAL_POINTERS,
                StartingVCNPtr,
                (uint)Marshal.SizeOf(typeof(PInvoke.LARGE_INTEGER)),
                pDest,
                RetSize,
                ref BytesReturned,
                IntPtr.Zero
            );

            if (!Result)
            {
                if (Marshal.GetLastWin32Error() != PInvoke.ERROR_MORE_DATA)
                {
                    Debug.WriteLine("Error #{0} occurred trying to get retrieval pointers for file '{1}", Marshal.GetLastWin32Error(), FullName);

                    Info.Clusters = 0;
                    Info.Attributes.AccessDenied = true;
                    Info.Attributes.Process = false;
                    Info.Fragments.Clear();
                    PInvoke.CloseHandle(Handle);
                    Marshal.FreeHGlobal(pDest);

                    return false;
                }

                Extents++;
            }
        } while (!Result);

        Retrieval = new PInvoke.RETRIEVAL_POINTERS_BUFFER(pDest);

        // Readjust extents, as it only reflects how much memory was allocated and may not
        // be accurate
        Extents = (uint)Retrieval.ExtentCount;

        // Ok, we have the info. Now translate it. hrmrmr

        Info.Fragments.Clear();
        for (int i = 0; i < Extents; i++)
        {
            Extent Add;

            Add.StartLCN = (ulong)Retrieval.Extents[(int)i].Lcn.QuadPart;
            if (i != 0)
                Add.Length = (ulong)Retrieval.Extents[(int)i].NextVcn.QuadPart - (ulong)Retrieval.Extents[(int)i - 1].NextVcn.QuadPart;
            else
                Add.Length = (ulong)Retrieval.Extents[(int)i].NextVcn.QuadPart - (ulong)Retrieval.StartingVcn;

            Info.Fragments.Add(Add);
        }


P /调用代码

    [DllImport("kernel32.dll", SetLastError = true)]
    internal static extern bool DeviceIoControl(
        IntPtr hDevice,
        uint dwIoControlCode,
        IntPtr lpInBuffer,
        uint nInBufferSize,
        [Out] IntPtr lpOutBuffer,
        uint nOutBufferSize,
        ref uint lpBytesReturned,
        IntPtr lpOverlapped);

    [StructLayout(LayoutKind.Explicit, Size = 8)]
    internal struct LARGE_INTEGER
    {
        [FieldOffset(0)]
        public UInt64 QuadPart;
        [FieldOffset(0)]
        public UInt32 LowPart;
        [FieldOffset(4)]
        public Int32 HighPart;
    }
    internal struct Extent
    {
        public LARGE_INTEGER NextVcn;
        public LARGE_INTEGER Lcn;

        public Extent(IntPtr ptr)
        {
            IntPtr extentPtr = ptr;

            NextVcn = (LARGE_INTEGER)Marshal.PtrToStructure(extentPtr, typeof(LARGE_INTEGER));
            Lcn = (LARGE_INTEGER)Marshal.PtrToStructure(IntPtr.Add(extentPtr, 8), typeof(LARGE_INTEGER));
        }
    }

    internal struct RETRIEVAL_POINTERS_BUFFER
    {
        public int ExtentCount;
        public Int64 StartingVcn;
        public List<Extent> Extents;

        public RETRIEVAL_POINTERS_BUFFER(IntPtr ptr)
        {
            this.ExtentCount = (Int32)Marshal.PtrToStructure(ptr, typeof(Int32));

            ptr = IntPtr.Add(ptr, 4);

            // StartingVcn
            this.StartingVcn = (Int64)Marshal.PtrToStructure(ptr, typeof(Int64));

            ptr = IntPtr.Add(ptr, 8);

            this.Extents = new List<Extent>();

            for (int i = 0; i < this.ExtentCount; i++)
            {
                this.Extents.Add(new Extent(ptr));

                ptr = IntPtr.Add(ptr, 16);
            }
        }
    }

    /// <summary>
    /// constants lifted from winioctl.h from platform sdk
    /// </summary>
    internal class FSConstants
    {
        const uint FILE_DEVICE_DISK = 0x00000007;
        const uint IOCTL_DISK_BASE = FILE_DEVICE_DISK;
        const uint FILE_DEVICE_FILE_SYSTEM = 0x00000009;

        const uint METHOD_NEITHER = 3;
        const uint METHOD_BUFFERED = 0;

        const uint FILE_ANY_ACCESS = 0;
        const uint FILE_SPECIAL_ACCESS = FILE_ANY_ACCESS;

        public static uint FSCTL_GET_VOLUME_BITMAP = CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 27, METHOD_NEITHER, FILE_ANY_ACCESS);
        public static uint FSCTL_GET_RETRIEVAL_POINTERS = CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 28, METHOD_NEITHER, FILE_ANY_ACCESS);
        public static uint FSCTL_MOVE_FILE = CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 29, METHOD_BUFFERED, FILE_SPECIAL_ACCESS);

        public static uint IOCTL_DISK_GET_DRIVE_GEOMETRY = CTL_CODE(IOCTL_DISK_BASE, 0x0000, METHOD_BUFFERED, FILE_ANY_ACCESS);

        static uint CTL_CODE(uint DeviceType, uint Function, uint Method, uint Access)
        {
            return ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method);
        }
    }


C ++至C#代码(有效)

C#代码

        uint Extents = 0;
        ulong StartingVcn = 0;
        IntPtr ExtentsPtr = IntPtr.Zero;

        bool ret = PInvoke.GetRetrievalPointers(Handle, ref Extents, ref StartingVcn, ref ExtentsPtr);

        IntPtr ptr = ExtentsPtr;

        Info.Fragments.Clear();

        for (uint i = 0; i < Extents; i++)
        {
            PInvoke.Extent Extent = new PInvoke.Extent(ptr);
            Extent Add;

            Add.StartLCN = Extent.Lcn.QuadPart;

            if (i != 0)
            {
                // Get previous extent
                PInvoke.Extent PrevExtent = new PInvoke.Extent(IntPtr.Subtract(ptr, 16));
                Add.Length = Extent.NextVcn.QuadPart - PrevExtent.NextVcn.QuadPart;
            }
            else
            {
                Add.Length = Extent.NextVcn.QuadPart - StartingVcn;
            }

            Info.Fragments.Add(Add);

            ptr = IntPtr.Add(ptr, 16);
        }


P /调用C#代码

    [DllImport("DLL.dll", CallingConvention=CallingConvention.Cdecl)]
    internal static extern bool GetRetrievalPointers(
        IntPtr hFile,
        [MarshalAs(UnmanagedType.U4)] ref uint ExtentCount,
        [MarshalAs(UnmanagedType.U8)] ref ulong StartingVcn,
        ref IntPtr ExtentsPtr
        );


C ++代码

#ifdef __cplusplus
extern "C" {
#endif
    EXTERNAPI bool GetRetrievalPointers(HANDLE hFile, DWORD &ExtentCount, UINT64 &StartingVcn, void **Extents) {
        // Get cluster allocation information
        STARTING_VCN_INPUT_BUFFER  StartingVCN;
        RETRIEVAL_POINTERS_BUFFER *Retrieval;
        unsigned __int64           RetSize;
        unsigned __int64           ExtentsCount;
        DWORD                      BytesReturned;
        BOOL                       Result;

        // Grab info one extent at a time, until it's done grabbing all the extent data
        // Yeah, well it doesn't give us a way to ask L"how many extents?" that I know of ...
        // btw, the Extents variable tends to only reflect memory usage, so when we have
        // all the extents we look at the structure Win32 gives us for the REAL count!
        ExtentsCount = 10;
        Retrieval = NULL;
        RetSize = 0;
        StartingVCN.StartingVcn.QuadPart = 0;

        do
        {
            ExtentsCount *= 2;
            RetSize = sizeof (RETRIEVAL_POINTERS_BUFFER)+((ExtentsCount - 1) * sizeof (LARGE_INTEGER)* 2);

            if (Retrieval != NULL)
                Retrieval = (RETRIEVAL_POINTERS_BUFFER *)realloc(Retrieval, RetSize);
            else
                Retrieval = (RETRIEVAL_POINTERS_BUFFER *)malloc(RetSize);

            Result = DeviceIoControl
                (
                hFile,
                FSCTL_GET_RETRIEVAL_POINTERS,
                &StartingVCN,
                sizeof (StartingVCN),
                Retrieval,
                RetSize,
                &BytesReturned,
                NULL
                );

            if (Result == FALSE)
            {
                DWORD dwLastError = GetLastError();
                if (dwLastError != ERROR_MORE_DATA)
                {
                    free(Retrieval);

                    return (false);
                }

                ExtentsCount++;
            }
        } while (Result == FALSE);

        // Readjust extents, as it only reflects how much memory was allocated and may not
        // be accurate
        ExtentsCount = Retrieval->ExtentCount;

        // Ok, we have the info. Now translate it. hrmrmr
        ExtentCount = ExtentsCount;
        StartingVcn = Retrieval->StartingVcn.QuadPart;
        *Extents = Retrieval->Extents;

        return true;
    }

#ifdef __cplusplus
};
#endif


其他

我注意到的一件事是,当我将Extents[0]->NextVcnExtents[0]->Lcn转换为LARGE_INTEGER(请参见上面的代码)时,HighPart会被LowPart切换,如下所示。但是,用LowPart切换HighPart不起作用RETRIEVAL_POINTERS_BUFFER->StartingVcn(因为HighPart / LowPart是一个数字,它们都应为0)。

LARGE_INTEGER错误

LARGE_INTEGER.HighPart = 2;
LARGE_INTEGER.LowPart = 0;


预期为LARGE_INTEGER

LARGE_INTEGER.HighPart = 0;
LARGE_INTEGER.LowPart = 2;


我想知道是否有什么东西正在改变内存,或者我是否不能正确使用内存?有任何想法吗?

最佳答案

范围[0]-> NextVcn是4294967296,范围[0]-> Lcn是934370135244800


这些不是随机数。将它们转换为十六进制,您将看到它们与您在C ++代码中获得的值完全相同,只是偏移了4个字节。换句话说,您正在读取未对齐的数据。该错误位于:

    ptr = IntPtr.Add(ptr, 4);


您已将偏移量硬编码到RETRIEVAL_POINTERS_BUFFER.StartingVcn到4。但是该字段需要对齐8,因为它包含64位值。因此,字段之间有4个字节的额外填充。固定:

    ptr = IntPtr.Add(ptr, 8);


好的,无论是32位还是64位代码。

08-25 06:54