Last active
June 10, 2024 14:47
-
-
Save KOZ60/8ad4648d80737ff316579d595c9dad47 to your computer and use it in GitHub Desktop.
WideFileName.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Runtime.InteropServices; | |
using static System.IO.Path; | |
/// <summary> | |
/// 32000 character filename | |
/// </summary> | |
/// <remarks> | |
/// When used as an argument to a Windows API, it will convert the filename to one that is 32K characters long. | |
/// You can also use the Win32FileName property as an argument to System.IO to support up to 32,000 characters. | |
/// </remarks> | |
public class WideFileName : SafeHandle, | |
IEquatable<WideFileName>, | |
IEquatable<string>, | |
IComparable<WideFileName>, | |
IComparable<string> | |
{ | |
private const string PREFIX_NARROW_UNC = @"\\"; | |
private const string PREFIX_WIDE_FILE = @"\\?\"; | |
private const string PREFIX_WIDE_UNC = @"\\?\UNC\"; | |
private const string RELATIVE_PATH_PARENT = @"..\"; | |
private readonly string win32FileName; | |
private readonly string displayFileName; | |
private readonly GCHandle gcHandle; | |
private int? hashCode; | |
private WideFileName(string fileName) | |
: base(IntPtr.Zero, true) { | |
displayFileName = ToDisplayFileName(fileName); | |
win32FileName = ToWin32FileNameInternal(displayFileName); | |
gcHandle = GCHandle.Alloc(win32FileName, GCHandleType.Pinned); | |
if (win32FileName == null) { | |
SetHandle(IntPtr.Zero); | |
} else { | |
unsafe { | |
fixed (char* ptr = win32FileName) { | |
SetHandle(new IntPtr(ptr)); | |
} | |
} | |
} | |
} | |
protected override bool ReleaseHandle() { | |
gcHandle.Free(); | |
return true; | |
} | |
public override bool IsInvalid { | |
get { | |
return !gcHandle.IsAllocated; | |
} | |
} | |
public static implicit operator WideFileName(string value) { | |
return new WideFileName(value); | |
} | |
public static bool operator ==(WideFileName a, WideFileName b) { | |
return Compare(a, b) == 0; | |
} | |
public static bool operator !=(WideFileName a, WideFileName b) { | |
return Compare(a, b) != 0; | |
} | |
public string Win32FileName { | |
get { | |
return win32FileName; | |
} | |
} | |
public string DisplayFileName { | |
get { | |
return displayFileName; | |
} | |
} | |
public override int GetHashCode() { | |
if (!hashCode.HasValue) { | |
hashCode = displayFileName.ToUpper().GetHashCode(); | |
} | |
return hashCode.Value; | |
} | |
public override bool Equals(object obj) { | |
if (obj is WideFileName other) { | |
return Equals(other); | |
} | |
if (obj is string str) { | |
return Equals(str); | |
} | |
return false; | |
} | |
public override string ToString() { | |
return displayFileName; | |
} | |
public bool Equals(WideFileName other) { | |
return Compare(this, other) == 0; | |
} | |
public bool Equals(string other) { | |
return Compare(this, new WideFileName(other)) == 0; | |
} | |
public int CompareTo(WideFileName other) { | |
return Compare(this, other); | |
} | |
public int CompareTo(string other) { | |
return Compare(this, new WideFileName(other)); | |
} | |
public static int Compare(WideFileName x, WideFileName y) { | |
object ox = x; | |
object oy = y; | |
if (ReferenceEquals(ox, oy)) { | |
return 0; | |
} | |
if (ox == null) { | |
return -1; | |
} | |
if (oy == null) { | |
return 1; | |
} | |
// The comparison follows the Explorer's order. | |
return StrCmpLogicalW(x.displayFileName, y.displayFileName); | |
} | |
public static string ToDisplayFileName(string fileName) { | |
// null | |
if (fileName == null) { | |
return null; | |
} | |
// Replace '/' with '\' | |
fileName = fileName.Replace(AltDirectorySeparatorChar, DirectorySeparatorChar); | |
// If the file name starts with "\\?\", it is a 32,000 character filename so delete it. | |
if (fileName.StartsWith(PREFIX_WIDE_FILE)) { | |
// The beginning is "\\?\UNC\" | |
if (fileName.StartsWith(PREFIX_WIDE_UNC)) { | |
fileName = PREFIX_NARROW_UNC + fileName.Substring(PREFIX_WIDE_UNC.Length); | |
} else { | |
fileName = fileName.Substring(PREFIX_WIDE_FILE.Length); | |
} | |
} | |
// Volume ex) C: → C:\ | |
if (fileName.Length == 2 && fileName[1] == VolumeSeparatorChar) { | |
return fileName + DirectorySeparatorChar; | |
} | |
return GetFullPath(fileName); | |
} | |
public static string ToWin32FileName(string fileName) { | |
return ToWin32FileNameInternal(ToDisplayFileName(fileName)); | |
} | |
internal static string ToWin32FileNameInternal(string displayName) { | |
// null | |
if (displayName == null) { | |
return null; | |
} | |
if (IsUncInternal(displayName)) { | |
// UNC | |
return PREFIX_WIDE_UNC + displayName.Substring(PREFIX_NARROW_UNC.Length); | |
} else { | |
// local file | |
return PREFIX_WIDE_FILE + displayName; | |
} | |
} | |
internal static bool IsUncInternal(string fileName) { | |
var uri = new Uri(fileName); | |
if (uri.IsUnc) { | |
return true; | |
} | |
return false; | |
} | |
[DllImport("shlwapi.dll", CharSet = CharSet.Unicode, ExactSpelling = true)] | |
public static extern int StrCmpLogicalW(string x, string y); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
How to use