gpt4 book ai didi

c# - 以编程方式检查 Windows 10 的区分大小写的目录属性

转载 作者:可可西里 更新时间:2023-11-01 14:10:02 25 4
gpt4 key购买 nike

自 2018 年 4 月起,Windows 10 可以使用 fsutil.exe 获取或设置目录是否标记为区分大小写。

有没有一种方法可以在不运行 fsutil.exe 或 hackishly 创建具有不同大小写的文件以查看它们是否发生冲突的情况下以编程方式查询目录的大小写敏感性?

我还没有真正找到任何方法来通过研究来测试这一点。我读过这是一个实际的 NTFS 属性,但在获取文件的属性时它没有显示出来。我还注意到,如果存在两个不同的大小写,FindFirstFile 将返回正确文件的大小写。除此之外,我不知道该去哪里,因为这方面的信息确实不多。这东西还是很新的。

正如其他人所提到的,由于可比性问题,使某些内容区分大小写在 Windows 中不是一个好主意。我知道这一点,我的目标是扫描和处理文件系统中现有的区分大小写的目录。

进度:

我发现即使不使用 FIND_FIRST_EX_CASE_SENSITIVE,Windows 的 FindFirstFile 和其他函数也会区分大小写。它不会返回带有无效大小写的文件。现在我想弄清楚是否有利用它的好方法。

最佳答案

感谢@eryksun 的评论,这是我的 P/Invoke 解决方案。

编辑 2: 添加了 SetDirectoryCaseSensitive()

编辑 3: 添加了 IsDirectoryCaseSensitivitySupported()

我已经实现了本地方法 NtQueryInformationFile在使用 FILE_INFORMATION_CLASSFileCaseSensitiveInformation 读取 FILE_CASE_SENSITIVE_INFORMATION结构。

public static partial class NativeMethods {
public static readonly IntPtr INVALID_HANDLE = new IntPtr(-1);

public const FileAttributes FILE_FLAG_BACKUP_SEMANTICS = (FileAttributes) 0x02000000;

public enum NTSTATUS : uint {
SUCCESS = 0x00000000,
NOT_IMPLEMENTED = 0xC0000002,
INVALID_INFO_CLASS = 0xC0000003,
INVALID_PARAMETER = 0xC000000D,
NOT_SUPPORTED = 0xC00000BB,
DIRECTORY_NOT_EMPTY = 0xC0000101,
}

public enum FILE_INFORMATION_CLASS {
None = 0,
// Note: If you use the actual enum in here, remember to
// start the first field at 1. There is nothing at zero.
FileCaseSensitiveInformation = 71,
}

// It's called Flags in FileCaseSensitiveInformation so treat it as flags
[Flags]
public enum CASE_SENSITIVITY_FLAGS : uint {
CaseInsensitiveDirectory = 0x00000000,
CaseSensitiveDirectory = 0x00000001,
}

[StructLayout(LayoutKind.Sequential)]
public struct IO_STATUS_BLOCK {
[MarshalAs(UnmanagedType.U4)]
public NTSTATUS Status;
public ulong Information;
}

[StructLayout(LayoutKind.Sequential)]
public struct FILE_CASE_SENSITIVE_INFORMATION {
[MarshalAs(UnmanagedType.U4)]
public CASE_SENSITIVITY_FLAGS Flags;
}

// An override, specifically made for FileCaseSensitiveInformation, no IntPtr necessary.
[DllImport("ntdll.dll")]
[return: MarshalAs(UnmanagedType.U4)]
public static extern NTSTATUS NtQueryInformationFile(
IntPtr FileHandle,
ref IO_STATUS_BLOCK IoStatusBlock,
ref FILE_CASE_SENSITIVE_INFORMATION FileInformation,
int Length,
FILE_INFORMATION_CLASS FileInformationClass);

[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr CreateFile(
[MarshalAs(UnmanagedType.LPTStr)] string filename,
[MarshalAs(UnmanagedType.U4)] FileAccess access,
[MarshalAs(UnmanagedType.U4)] FileShare share,
IntPtr securityAttributes, // optional SECURITY_ATTRIBUTES struct or IntPtr.Zero
[MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
[MarshalAs(UnmanagedType.U4)] FileAttributes flagsAndAttributes,
IntPtr templateFile);

[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(
IntPtr hObject);

public static bool IsDirectoryCaseSensitive(string directory, bool throwOnError = true) {
// Read access is NOT required
IntPtr hFile = CreateFile(directory, 0, FileShare.ReadWrite,
IntPtr.Zero, FileMode.Open,
FILE_FLAG_BACKUP_SEMANTICS, IntPtr.Zero);
if (hFile == INVALID_HANDLE)
throw new Win32Exception();
try {
IO_STATUS_BLOCK iosb = new IO_STATUS_BLOCK();
FILE_CASE_SENSITIVE_INFORMATION caseSensitive = new FILE_CASE_SENSITIVE_INFORMATION();
NTSTATUS status = NtQueryInformationFile(hFile, ref iosb, ref caseSensitive,
Marshal.SizeOf<FILE_CASE_SENSITIVE_INFORMATION>(),
FILE_INFORMATION_CLASS.FileCaseSensitiveInformation);
switch (status) {
case NTSTATUS.SUCCESS:
return caseSensitive.Flags.HasFlag(CASE_SENSITIVITY_FLAGS.CaseSensitiveDirectory);

case NTSTATUS.NOT_IMPLEMENTED:
case NTSTATUS.NOT_SUPPORTED:
case NTSTATUS.INVALID_INFO_CLASS:
case NTSTATUS.INVALID_PARAMETER:
// Not supported, must be older version of windows.
// Directory case sensitivity is impossible.
return false;
default:
throw new Exception($"Unknown NTSTATUS: {(uint)status:X8}!");
}
}
finally {
CloseHandle(hFile);
}
}
}

这里是通过实现 NTSetInformationFile 设置目录区分大小写的实现. (它有一个与 NTQueryInformationFile 相同的参数列表。同样,由于@eryksun 的洞察力,问题得到了解决。

FILE_WRITE_ATTRIBUTES 是一个未在 C# 中实现的 FileAccess 标志,因此需要从值 0x100 中定义和/或转换它>.

partial class NativeMethods {
public const FileAccess FILE_WRITE_ATTRIBUTES = (FileAccess) 0x00000100;

// An override, specifically made for FileCaseSensitiveInformation, no IntPtr necessary.
[DllImport("ntdll.dll")]
[return: MarshalAs(UnmanagedType.U4)]
public static extern NTSTATUS NtSetInformationFile(
IntPtr FileHandle,
ref IO_STATUS_BLOCK IoStatusBlock,
ref FILE_CASE_SENSITIVE_INFORMATION FileInformation,
int Length,
FILE_INFORMATION_CLASS FileInformationClass);

// Require's elevated priviledges
public static void SetDirectoryCaseSensitive(string directory, bool enable) {
// FILE_WRITE_ATTRIBUTES access is the only requirement
IntPtr hFile = CreateFile(directory, FILE_WRITE_ATTRIBUTES, FileShare.ReadWrite,
IntPtr.Zero, FileMode.Open,
FILE_FLAG_BACKUP_SEMANTICS, IntPtr.Zero);
if (hFile == INVALID_HANDLE)
throw new Win32Exception();
try {
IO_STATUS_BLOCK iosb = new IO_STATUS_BLOCK();
FILE_CASE_SENSITIVE_INFORMATION caseSensitive = new FILE_CASE_SENSITIVE_INFORMATION();
if (enable)
caseSensitive.Flags |= CASE_SENSITIVITY_FLAGS.CaseSensitiveDirectory;
NTSTATUS status = NtSetInformationFile(hFile, ref iosb, ref caseSensitive,
Marshal.SizeOf<FILE_CASE_SENSITIVE_INFORMATION>(),
FILE_INFORMATION_CLASS.FileCaseSensitiveInformation);
switch (status) {
case NTSTATUS.SUCCESS:
return;
case NTSTATUS.DIRECTORY_NOT_EMPTY:
throw new IOException($"Directory \"{directory}\" contains matching " +
$"case-insensitive files!");

case NTSTATUS.NOT_IMPLEMENTED:
case NTSTATUS.NOT_SUPPORTED:
case NTSTATUS.INVALID_INFO_CLASS:
case NTSTATUS.INVALID_PARAMETER:
// Not supported, must be older version of windows.
// Directory case sensitivity is impossible.
throw new NotSupportedException("This version of Windows does not support directory case sensitivity!");
default:
throw new Exception($"Unknown NTSTATUS: {(uint)status:X8}!");
}
}
finally {
CloseHandle(hFile);
}
}
}

最后我添加了一个方法来计算一次是否 Windows 版本支持区分大小写的目录。这只是在 Temp 中创建一个具有常量 GUID 名称的文件夹,并检查 NTSTATUS 结果(因此它可以检查它知道它有权访问的文件夹)。

partial class NativeMethods {
// Use the same directory so it does not need to be recreated when restarting the program
private static readonly string TempDirectory =
Path.Combine(Path.GetTempPath(), "88DEB13C-E516-46C3-97CA-46A8D0DDD8B2");

private static bool? isSupported;
public static bool IsDirectoryCaseSensitivitySupported() {
if (isSupported.HasValue)
return isSupported.Value;

// Make sure the directory exists
if (!Directory.Exists(TempDirectory))
Directory.CreateDirectory(TempDirectory);

IntPtr hFile = CreateFile(TempDirectory, 0, FileShare.ReadWrite,
IntPtr.Zero, FileMode.Open,
FILE_FLAG_BACKUP_SEMANTICS, IntPtr.Zero);
if (hFile == INVALID_HANDLE)
throw new Exception("Failed to open file while checking case sensitivity support!");
try {
IO_STATUS_BLOCK iosb = new IO_STATUS_BLOCK();
FILE_CASE_SENSITIVE_INFORMATION caseSensitive = new FILE_CASE_SENSITIVE_INFORMATION();
// Strangely enough, this doesn't fail on files
NTSTATUS result = NtQueryInformationFile(hFile, ref iosb, ref caseSensitive,
Marshal.SizeOf<FILE_CASE_SENSITIVE_INFORMATION>(),
FILE_INFORMATION_CLASS.FileCaseSensitiveInformation);
switch (result) {
case NTSTATUS.SUCCESS:
return (isSupported = true).Value;
case NTSTATUS.NOT_IMPLEMENTED:
case NTSTATUS.INVALID_INFO_CLASS:
case NTSTATUS.INVALID_PARAMETER:
case NTSTATUS.NOT_SUPPORTED:
// Not supported, must be older version of windows.
// Directory case sensitivity is impossible.
return (isSupported = false).Value;
default:
throw new Exception($"Unknown NTSTATUS {(uint)result:X8} while checking case sensitivity support!");
}
}
finally {
CloseHandle(hFile);
try {
// CHOOSE: If you delete the folder, future calls to this will not be any faster
// Directory.Delete(TempDirectory);
}
catch { }
}
}
}

关于c# - 以编程方式检查 Windows 10 的区分大小写的目录属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52206212/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com