Windows ではパスの長さは MAX_PATH(260)文字までということになっていて、C#(というか .NET Framework)のファイル系ライブラリも昔は 260 文字までのパスしか使えなかった。
260 文字を超えるとても長いパスを扱えるようにするのが extended-length パスで、単純に、通常のパスの先頭に "\\?\" を付与するだけで良い。
使い方
extended-length パスは .NET Framework 4.6.2 でサポートされたので、プロジェクトのプロパティーでターゲットフレームワークを .NET Framework 4.6.2 以上にしたうえで、以下のコードを実行すると、正しく結果が得られる。
const String EXTENDED_LENGTH_PATH_PREFIX = @"\\?\";
String aPath = EXTENDED_LENGTH_PATH_PREFIX + @"C:\Tmp\とても長いパス\123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789A123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890B\1234567890123456789Z\1234567890123456789Y\VeryVeryLongPath";
MessageBox.Show("Len: " + aPath.Length.ToString() + "\nExists: " + Directory.Exists(aPath).ToString());
"\\?\" を付与しないと、フォルダーが存在する場合でも False が返されてしまう。
互換性
ターゲットフレームワークを .NET Framework 4.6.2 とすると動作する環境が限られてしまう。
.NET Framework 4.6.2 未満の環境でも、extended-length パスはサポートされないにしても、短いパスである限りは動くようにしたい場合は、ターゲットフレームワークを例えば .NET Framework 4.5 としたうえで、まず App.config の <runtime> に <AppContextSwitchOverrides> を追加する。
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/>
</startup>
<runtime>
<AppContextSwitchOverrides value="Switch.System.IO.UseLegacyPathHandling=false;Switch.System.IO.BlockLongPaths=false"/>
</runtime>
</configuration>
実行時に .NET のバージョンを確認し、4.6.2 以上であれば extended-length パスを使うようにする。
private Boolean GetClrVersionRegistryNumber(out Int32 oClrVersion)
{
using (RegistryKey aKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32).OpenSubKey(@"SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full\"))
{
if (aKey != null && aKey.GetValue("Release") != null)
{
oClrVersion = (Int32)aKey.GetValue("Release");
return true;
}
else
{
oClrVersion = 0;
return false;
}
}
}
private Boolean Is462()
{
Int32 aClrVer;
GetClrVersionRegistryNumber(out aClrVer);
return aClrVer >= 394802;
}
private String ExtendPath(String oPath)
{
// extended-length パスが使えない環境や、既に extended-length パスになっている場合はそのまま返る
if (!Is462() || oPath.StartsWith(EXTENDED_LENGTH_PATH_PREFIX))
{
return oPath;
}
// extended-length パスにする
// MAX_PATH 文字以上のフォルダー名をダイアログから取得した場合など、短いファイル名形式になっていることがあるため、
// Path.GetFullPath() で長いファイル名形式に変換する
return EXTENDED_LENGTH_PATH_PREFIX + Path.GetFullPath(oPath);
}
private void button1_Click(object sender, EventArgs e)
{
try
{
String aPath = ExtendPath(@"C:\Tmp\短いパス");
//String aPath = ExtendPath(@"C:\Tmp\とても長いパス\123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789A123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890B\1234567890123456789Z\1234567890123456789Y\VeryVeryLongPath");
MessageBox.Show("4.6.2 or higher: " + Is462().ToString() + "\nLen: " + aPath.Length.ToString() + "\nExists: " + Directory.Exists(aPath).ToString());
}
catch (Exception oExcep)
{
MessageBox.Show(oExcep.Message);
}
}
このようにすると、.NET 4.6.2 以上の環境で動作させると長いパス含めて正しく動作するし、.NET 4.6.2 未満の環境では長いパスは動作しないが短いパスなら動作する。