我正在尝试仅使用唯一文件ID获取NTFS文件系统上的文件信息。 我遇到的问题是从ID生成句柄,因为我的代码导致访问冲突,我不知道原因。
要获取唯一文件ID,我使用了HERE中的代码。我很确定问题是描述符。我已经阅读了HERE(第一回答第二条评论)我可以使用长整数而不是guid,但这似乎不起作用。但我不知道如何使用我的文件ID信息创建一个guid。
这是我到目前为止的代码。
public class WinAPI
{
[DllImport("ntdll.dll", SetLastError = true)]
public static extern IntPtr NtQueryInformationFile(IntPtr fileHandle, ref IO_STATUS_BLOCK IoStatusBlock, IntPtr pInfoBlock, uint length, FILE_INFORMATION_CLASS fileInformation);
public struct IO_STATUS_BLOCK
{
uint status;
ulong information;
}
public struct _FILE_INTERNAL_INFORMATION
{
public ulong IndexNumber;
}
// Abbreviated, there are more values than shown
public enum FILE_INFORMATION_CLASS
{
FileDirectoryInformation = 1, // 1
FileFullDirectoryInformation, // 2
FileBothDirectoryInformation, // 3
FileBasicInformation, // 4
FileStandardInformation, // 5
FileInternalInformation // 6
}
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool GetFileInformationByHandle(IntPtr hFile, out BY_HANDLE_FILE_INFORMATION lpFileInformation);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr OpenFileById(IntPtr hFile, FILE_ID_DESCRIPTOR lpFileID, uint dwDesiredAccess, uint dwShareMode, uint dwFlagas);
[StructLayout(LayoutKind.Explicit)]
public struct FILE_ID_DESCRIPTOR
{
[FieldOffset(0)] public uint dwSize;
[FieldOffset(4)] public FILE_ID_TYPE type;
// [FieldOffset(8)] public Guid guid;
[FieldOffset(8)] public long FileReferenceNumber;
}
public enum FILE_ID_TYPE
{
FileIdType = 0,
ObjectIdType = 1,
ExtendedFileIdType = 2,
MaximumFileIdType
};
public struct BY_HANDLE_FILE_INFORMATION
{
public uint FileAttributes;
public FILETIME CreationTime;
public FILETIME LastAccessTime;
public FILETIME LastWriteTime;
public uint VolumeSerialNumber;
public uint FileSizeHigh;
public uint FileSizeLow;
public uint NumberOfLinks;
public uint FileIndexHigh;
public uint FileIndexLow;
}
}
public class File_Handle
{
public ulong Get_Index()
{
WinAPI.BY_HANDLE_FILE_INFORMATION objectFileInfo = new WinAPI.BY_HANDLE_FILE_INFORMATION();
FileInfo fi = new FileInfo(@"D:\Test\Testfile.txt");
FileStream fs = fi.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
WinAPI.GetFileInformationByHandle(fs.Handle, out objectFileInfo);
fs.Close();
ulong fileIndex = ((ulong)objectFileInfo.FileIndexHigh << 32) + (ulong)objectFileInfo.FileIndexLow;
return fileIndex;
}
public string Retrieve_File(ulong Index)
{
WinAPI.FILE_ID_DESCRIPTOR Descriptor = new WinAPI.FILE_ID_DESCRIPTOR { dwSize = 24, type = WinAPI.FILE_ID_TYPE.FileIdType, FileReferenceNumber = (long)Index };
FileInfo fi = new FileInfo(@"D:\Test\TestfileRef.txt");
FileStream fs = fi.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
FileStream wf = new FileStream(WinAPI.OpenFileById(fs.Handle, Descriptor, 0, 0, 0x08000000), FileAccess.ReadWrite);
WinAPI.BY_HANDLE_FILE_INFORMATION objectFileInfo = new WinAPI.BY_HANDLE_FILE_INFORMATION();
WinAPI.GetFileInformationByHandle(wf.Handle, out objectFileInfo);
fs.Close();
wf.Close();
return "Dummy";
}
}
答案 0 :(得分:0)
正如我所说,使用pinvoke中的代码。这工作
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
File_Handle handle = new File_Handle();
handle.Get_Index();
handle.Retrieve_File();
}
}
public class WinAPI
{
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool GetFileInformationByHandle(IntPtr hFile, out BY_HANDLE_FILE_INFORMATION lpFileInformation);
[DllImport("kernel32.dll", BestFitMapping = false, ThrowOnUnmappableChar = true)]
public static extern int OpenFile([System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPStr)]string lpFileName, out OFSTRUCT lpReOpenBuff,
OpenFileStyle uStyle);
[StructLayout(LayoutKind.Explicit)]
public struct FILE_ID_DESCRIPTOR
{
[FieldOffset(0)]
public uint dwSize;
[FieldOffset(4)]
public FILE_ID_TYPE type;
// [FieldOffset(8)] public Guid guid;
[FieldOffset(8)]
public long FileReferenceNumber;
}
public enum FILE_ID_TYPE
{
FileIdType = 0,
ObjectIdType = 1,
ExtendedFileIdType = 2,
MaximumFileIdType
};
public struct BY_HANDLE_FILE_INFORMATION
{
public uint FileAttributes;
public FILETIME CreationTime;
public FILETIME LastAccessTime;
public FILETIME LastWriteTime;
public uint VolumeSerialNumber;
public uint FileSizeHigh;
public uint FileSizeLow;
public uint NumberOfLinks;
public uint FileIndexHigh;
public uint FileIndexLow;
}
[System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential)]
public struct OFSTRUCT
{
public byte cBytes;
public byte fFixedDisc;
public UInt16 nErrCode;
public UInt16 Reserved1;
public UInt16 Reserved2;
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst = 128)]
public string szPathName;
}
[Flags]
public enum OpenFileStyle : uint
{
OF_CANCEL = 0x00000800, // Ignored. For a dialog box with a Cancel button, use OF_PROMPT.
OF_CREATE = 0x00001000, // Creates a new file. If file exists, it is truncated to zero (0) length.
OF_DELETE = 0x00000200, // Deletes a file.
OF_EXIST = 0x00004000, // Opens a file and then closes it. Used to test that a file exists
OF_PARSE = 0x00000100, // Fills the OFSTRUCT structure, but does not do anything else.
OF_PROMPT = 0x00002000, // Displays a dialog box if a requested file does not exist
OF_READ = 0x00000000, // Opens a file for reading only.
OF_READWRITE = 0x00000002, // Opens a file with read/write permissions.
OF_REOPEN = 0x00008000, // Opens a file by using information in the reopen buffer.
// For MS-DOS–based file systems, opens a file with compatibility mode, allows any process on a
// specified computer to open the file any number of times.
// Other efforts to open a file with other sharing modes fail. This flag is mapped to the
// FILE_SHARE_READ|FILE_SHARE_WRITE flags of the CreateFile function.
OF_SHARE_COMPAT = 0x00000000,
// Opens a file without denying read or write access to other processes.
// On MS-DOS-based file systems, if the file has been opened in compatibility mode
// by any other process, the function fails.
// This flag is mapped to the FILE_SHARE_READ|FILE_SHARE_WRITE flags of the CreateFile function.
OF_SHARE_DENY_NONE = 0x00000040,
// Opens a file and denies read access to other processes.
// On MS-DOS-based file systems, if the file has been opened in compatibility mode,
// or for read access by any other process, the function fails.
// This flag is mapped to the FILE_SHARE_WRITE flag of the CreateFile function.
OF_SHARE_DENY_READ = 0x00000030,
// Opens a file and denies write access to other processes.
// On MS-DOS-based file systems, if a file has been opened in compatibility mode,
// or for write access by any other process, the function fails.
// This flag is mapped to the FILE_SHARE_READ flag of the CreateFile function.
OF_SHARE_DENY_WRITE = 0x00000020,
// Opens a file with exclusive mode, and denies both read/write access to other processes.
// If a file has been opened in any other mode for read/write access, even by the current process,
// the function fails.
OF_SHARE_EXCLUSIVE = 0x00000010,
// Verifies that the date and time of a file are the same as when it was opened previously.
// This is useful as an extra check for read-only files.
OF_VERIFY = 0x00000400,
// Opens a file for write access only.
OF_WRITE = 0x00000001
}
}
public class File_Handle
{
public ulong Get_Index()
{
WinAPI.BY_HANDLE_FILE_INFORMATION objectFileInfo = new WinAPI.BY_HANDLE_FILE_INFORMATION();
FileInfo fi = new FileInfo(@"c:\Temp\Test.txt");
FileStream fs = fi.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
WinAPI.GetFileInformationByHandle(fs.Handle, out objectFileInfo);
fs.Close();
ulong fileIndex = ((ulong)objectFileInfo.FileIndexHigh << 32) + (ulong)objectFileInfo.FileIndexLow;
return fileIndex;
}
public string Retrieve_File()
{
WinAPI.OFSTRUCT ofStruct = new WinAPI.OFSTRUCT();
int err = WinAPI.OpenFile(@"c:\Temp\Test.txt", out ofStruct, WinAPI.OpenFileStyle.OF_READ);
return "Dummy";
}
}
}
答案 1 :(得分:0)
我知道这很晚了,但是我花了几天时间解决了这个问题:
根据OpenByFileId API,这是方法的定义:
HANDLE OpenFileById(
HANDLE hVolumeHint,
LPFILE_ID_DESCRIPTOR lpFileId,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwFlagsAndAttributes
);
请注意lpFileId
是一个LP,也就是指向FILE_ID_DESCRIPTOR
结构的指针(在参数说明中也提到了)。因此,如果我们将c#方法修改为采用ref
,我们将获得如下签名:
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr OpenFileById(IntPtr hFile, ref FILE_ID_DESCRIPTOR lpFileID, uint dwDesiredAccess, uint dwShareMode, uint dwFlagas);
这应该使您克服看到的访问冲突异常,但是您很可能会从此处返回无效的句柄。这是因为您的FILE_ID_DESCRIPTOR
类的大小可能正确,但与FILE_ID_DESCRIPTOR signature不匹配。
您可以找到一个完整的,兼容的解决方案(在c#和c ++中),它们既可以从path获取FileId,又可以从FileId获取Path:https://github.com/nolanblew/openbyfileid
注意:要使其正常工作,您必须 在要访问的驱动器上的某些文件或目录上具有句柄。此示例(以及github示例)使用程序的本地路径,因此,如果您在C:\上运行程序,但文件位于E:上,则它将失败。
这是我从FileId获取路径的代码:
public static class FileIdHelper
{
public static string GetFilePath(long fileSystemId)
{
using var handle = _CreateSafeFileHandle(".");
if (handle == null || handle.IsInvalid)
{
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
}
var size = Marshal.SizeOf(typeof(FILE_ID_DESCRIPTOR));
var descriptor = new FILE_ID_DESCRIPTOR { Type = FILE_ID_TYPE.FileIdType, FileId = fileSystemId, dwSize = size };
try
{
using var handle2 = _OpenFileById(handle, ref descriptor);
if (handle2 == null || handle2.IsInvalid)
{
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
}
const int length = 128;
var builder = new StringBuilder(length);
GetFinalPathNameByHandleW(handle2, builder, length, 0);
return builder.ToString();
}
catch
{
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
}
return null;
}
static SafeFileHandle _CreateSafeFileHandle(string path) =>
CreateFileW(
path,
FileAccess.Read,
FileShare.ReadWrite,
IntPtr.Zero,
FileMode.Open,
(FileAttributes)0x02000000, //FILE_FLAG_BACKUP_SEMANTICS
IntPtr.Zero);
static SafeFileHandle _OpenFileById(SafeFileHandle hint, ref FILE_ID_DESCRIPTOR fileId) =>
OpenFileById(
hint,
ref fileId,
FileAccess.ReadWrite,
FileShare.ReadWrite,
IntPtr.Zero,
(FileAttributes)0x02000000); //FILE_FLAG_BACKUP_SEMANTICS
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "CreateFileW")]
static extern SafeFileHandle CreateFileW(
[In, MarshalAs(UnmanagedType.LPWStr)] string lpFileName,
[In, MarshalAs(UnmanagedType.U4)] FileAccess dwDesiredAccess,
[In, MarshalAs(UnmanagedType.U4)] FileShare dwShareMode,
[In] IntPtr lpSecurityAttributes,
[In, MarshalAs(UnmanagedType.U4)] FileMode dwCreationDisposition,
[In, MarshalAs(UnmanagedType.U4)] FileAttributes dwFlagsAndAttributes,
[In] IntPtr hTemplateFile);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "OpenFileById")]
static extern SafeFileHandle OpenFileById(
[In] SafeFileHandle hVolumeHint,
[In, Out] ref FILE_ID_DESCRIPTOR lpFileId,
[In, MarshalAs(UnmanagedType.U4)] FileAccess dwDesiredAccess,
[In, MarshalAs(UnmanagedType.U4)] FileShare dwShareMode,
[In] IntPtr lpSecurityAttributes,
[In, MarshalAs(UnmanagedType.U4)] FileAttributes dwFlagsAndAttributes
);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "GetFinalPathNameByHandleW")]
static extern int GetFinalPathNameByHandleW(
SafeFileHandle hFile,
StringBuilder lpszFilePath,
int cchFilePath,
int dwFlags);
}
[StructLayout(LayoutKind.Explicit)]
public struct FILE_ID_DESCRIPTOR
{
[FieldOffset(0)]
public int dwSize;
[FieldOffset(4)]
public FILE_ID_TYPE Type;
[FieldOffset(8)]
public long FileId;
[FieldOffset(8)]
public Guid ObjectId;
[FieldOffset(8)]
public Guid ExtendedFileId; //Use for ReFS; need to use v3 structures or later instead of v2 as done in this sample
}
public enum FILE_ID_TYPE
{
FileIdType = 0,
ObjectIdType = 1,
ExtendedFileIdType = 2,
MaximumFileIdType
};