我在尝试用C#中的FSCTL_GET_RETRIEVAL_POINTERS调用DeviceIOControl
时遇到一个奇怪的问题。我已经尝试过Jeffrey Walls blog中的代码以及我自己的代码,但是仍然遇到相同的问题。问题似乎出在Extents
中的RETRIEVAL_POINTERS_BUFFER
结构中。在C ++中,当Extents[0]->NextVcn
是1
且Extents[0]->Lcn
是217550
时,在C#中,我不断得到Extents[0]->NextVcn
是4294967296
和Extents[0]->Lcn
是934370135244800
。与RETRIEVAL_POINTERS_BUFFER->StartingVcn
相同,当它应为0
时,在C#中将变为其他一些数字(如101
或2016
)。
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]->NextVcn
和Extents[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位代码。