MAX_PATHとWindowsのパス・ファイル名の落とし穴 ── 260文字制限、予約名、末尾ドット、大文字小文字

· · MAX_PATH, ファイルパス, ロングパス, ファイル名, NTFS, Win32, C#, .NET, Windows開発, 不具合調査, 技術相談

「ユーザーの端末でだけ『ファイルが見つかりません』になる」「コピーはできたのに、そのファイルが開けない」── ファイル入出力を伴う業務アプリの不具合調査で、最終的にパスの長さやファイル名そのものが原因だったというケースは珍しくありません。ユーザーは案件名や日付をフォルダー名に詰め込み、階層を深く掘り、こちらの想定を軽々と超えてきます。

厄介なのは、この領域の制限が「Win32 APIの制限」「ファイルシステムの制限」「シェル(Explorer)の制限」「.NETランタイムの制限」と何層にも分かれていて、どこまで対応できて何が残るのかが分かりにくいことです。この記事では、MAX_PATH=260文字の正体から、長いパスを合法的に扱う条件、CONなどの予約名や末尾ドットといったファイル名の罠、大文字小文字の扱いまで、C#での実務対応とセットで整理します。

1. まず結論

  • MAX_PATH=260は「ドライブ文字+コロン+バックスラッシュ+最大256文字のパス文字列+終端NUL」を含んだWin32 APIの制限です。ファイルシステム側(NTFS等)はもっと長いパスを扱え、Unicode版APIに \\?\ プレフィックスを付ければ合計約32,767文字まで指定できます。12
  • 260文字制限の解除には、Windows 10 バージョン1607以降で「レジストリのLongPathsEnabled=1」と「アプリマニフェストのlongPathAware」の両方が必要です。片方だけでは有効になりません。3
  • .NET(Core)/.NET 5以降のランタイムはMAX_PATHチェックを行わず、長いパスを暗黙に扱います。.NET Frameworkは4.6.2以降をターゲットにするとランタイム側の260文字チェックが外れます。45
  • ただし長パス非対応のアプリは現実に残ります。Win32 APIで作れるパスをシェル(Explorer)が正しく解釈できないことがある、と公式ドキュメント自身が明記しています。1
  • ファイル名には < > : " / \ | ? * と制御文字(0〜31)が使えず、CON・PRN・AUX・NUL・COM1〜9・LPT1〜9は拡張子を付けても(CON.txtでも)予約名扱いです。6
  • 名前の末尾のスペースとドットは、パス正規化の過程で黙って取り除かれます。「hoge.」を指定したつもりが「hoge」に化ける、他OSが作った末尾スペース付きファイルにWindowsからアクセスできない、という事故の原因です。67
  • Windowsのファイル名は「大文字小文字を保持するが、区別しない」が既定です。NTFSはディレクトリ単位の区別(fsutil.exe file setCaseSensitiveInfo)もサポートしますが、Windowsアプリ側が追随できない副作用があります。89
  • 実装面では、パス結合はPath.Combineの「ルート付き引数で前段が捨てられる」仕様に注意し(.NET Core系ならPath.Joinも選択肢)、ユーザー入力のファイル名はPath.GetInvalidFileNameChars+予約名・末尾文字の自前チェックでサニタイズします。1011

2. MAX_PATH=260の正体

Win32 APIにおけるパスの最大長は、一部の例外を除いてMAX_PATH=260文字と定義されています。この260には内訳があります。ローカルパスは「ドライブ文字、コロン、バックスラッシュ、バックスラッシュで区切られた名前部分、終端のNUL文字」で構成され、たとえばDドライブなら「D:\ + 256文字ぶんのパス文字列 + 終端NUL」が最大です。1

つまり260文字を「ファイル名に使える長さ」と思っていると、実際にはドライブ表記と終端NULで4文字ぶん目減りしている、ということです。さらに細かい制限として、ディレクトリ作成APIでは8.3形式のファイル名を後ろに足せる余地が要求されるため、ディレクトリのパスはMAX_PATH−12文字を超えられません1

重要なのは、これがWin32 APIレイヤーの制限であって、ファイルシステムの限界ではないことです。NTFSは長いファイル名と拡張パスをサポートしており、多くのWin32関数のUnicode版は合計約32,767文字の拡張長パスを受け付けます。パスを構成する個々のコンポーネント(1つのフォルダー名・ファイル名)の上限はGetVolumeInformationが返す値で、一般に255文字です。12

この「APIは260、ファイルシステムは約32,767」というギャップこそが、現場の障害の源泉です。あるツールでは作れたパスが、別のツール(あるいは自社アプリ)では開けないという非対称な状況が正当に発生します。git cloneで深いリポジトリを長い名前のフォルダーに展開したらビルドが通らなくなった、というのは公式ドキュメントにも載っている典型例です。1

なお、旧来の.NET Frameworkではフルパスが260文字以上になるとSystem.IO.PathTooLongExceptionが投げられていました。この例外を見たら、まずパス長を疑ってください。12

3. 260文字の壁を超える方法と、その条件

長いパスを扱う手段は大きく2つ、「\\?\プレフィックス」と「OSの長パス有効化」です。

3.1. \\?\ プレフィックス

パス文字列の先頭に \\?\ を付けると、Win32 APIは文字列の解析を止めてそのままファイルシステムへ渡します。これによりMAX_PATHの制限を超えられます(UNCパスは \\?\UNC\server\share 形式)。ただし条件と副作用があります。16

  • Unicode版API(〜Wや.NETのようにUTF-16で呼ぶもの)であること。
  • 正規化がスキップされるため、/区切りや...による相対指定は使えません。相対パスには\\?\を付けられないので、相対パスは常にMAX_PATHまでです。1
  • すべてのAPIが対応しているわけではなく、対応可否は各APIのリファレンスで確認が必要です。6

3.2. Windows 10 1607以降の長パス有効化 ── 条件は「両方」

Windows 10 バージョン1607以降では、多くの一般的なWin32ファイル・ディレクトリ関数(CreateFileW、FindFirstFileW、GetFileAttributesWなど)からMAX_PATH制限を外せます。ただしアプリ側のオプトインが前提で、次の2条件を両方満たす必要があります。3

  1. レジストリ値 HKLM\SYSTEM\CurrentControlSet\Control\FileSystemLongPathsEnabled (REG_DWORD) が1であること。グループポリシー「コンピューターの構成 > 管理用テンプレート > システム > ファイルシステム > Win32の長いパスを有効にする」でも設定できます。
  2. アプリケーションマニフェストに longPathAware 要素があること。
<application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
        <ws2:longPathAware>true</ws2:longPathAware>
    </windowsSettings>
</application>
# レジストリ側(管理者権限)
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" `
-Name "LongPathsEnabled" -Value 1 -PropertyType DWORD -Force

「レジストリを設定したのに効かない」という相談は大抵、マニフェスト側の欠落です。公式ドキュメントも「このレジストリ設定は、新機能を利用するよう修正されたアプリにしか影響しない」と念押ししています。また、レジストリ値は最初のファイル関数呼び出し時にプロセス単位でキャッシュされ、プロセスの生存中は再読み込みされません。設定変更を全アプリに確実に反映させるには再起動が必要な場合があります。3

3.3. .NETではどうなるか

  • .NET (Core) / .NET 5以降: ランタイムはMAX_PATHチェックを行わず、長いパスを暗黙に扱います。アプリ側で特別なコードは不要です。4
  • .NET Framework: 4.6.2以降をターゲットにすると260文字のランタイムチェックが外れ、PathTooLongExceptionは「32,767文字超」または「OSがエラーを返した場合」に限られます。それ以前をターゲットにした既存アプリでも、Switch.System.IO.BlockLongPaths=false(および旧パス処理を無効にするSwitch.System.IO.UseLegacyPathHandling=false)のAppContextスイッチでオプトインできます。512
  • .NET Frameworkアプリが実務で長パスを通すには、上記ランタイム設定に加えてOS側の長パス有効化とマニフェストの併用が必要になります。NuGet.exeの長パス対応ドキュメントが、この構成(Windows 10 1607以降+longPathAwareマニフェスト+UseLegacyPathHandling無効化)を実例として明記しています。13

3.4. それでも残る「非対応アプリ」という現実

ここまでやっても、世の中のすべてのアプリが長パスを扱えるようになるわけではありません。公式ドキュメントは「シェルとファイルシステムは要件が異なる。Win32 APIで作成できるパスをシェルUIが正しく解釈できないことがある」と明記しており1、実際、長パス対応をうたわないツールも残っています(たとえばNuGetのドキュメントは、Visual Studioやmsbuild -t:restoreのrestoreが長パス未対応であることを記載しています13)。自アプリが長いパスでファイルを作れても、ユーザーがExplorerや他のツールでそれを開けるかは別問題です。この非対称性を踏まえた設計判断は第6章の判断表にまとめます。

4. 使えない文字と予約デバイス名、末尾のドット・スペース

パス長と並ぶもう1つの地雷原が、ファイル名そのものの規則です。公式の命名規則から、業務アプリで踏みやすいものを整理します。6

分類 内容 備考
予約文字 < > : " / \ \| ? * パス区切りの\(と/)、ドライブの:を含む
制御文字 整数値0(NUL)と1〜31 代替データストリーム内を除き不可
予約デバイス名 CON, PRN, AUX, NUL, COM1〜COM9, LPT1〜LPT9(および上付き数字のCOM¹〜³, LPT¹〜³) 拡張子を付けても不可(NUL.txtやNUL.tar.gzはNULと等価)
末尾の文字 スペースまたはドットで終わる名前 ファイルシステムは許容してもシェルとUIが非対応

4.1. 予約デバイス名 ── CON.txtでもダメ

CONやNULはMS-DOS時代のデバイス名で、現在もNT名前空間の予約名として残っています。だから「CON」というファイルは通常の方法では作れず、CON.txtのように拡張子を付けても予約名として解釈されます6 シリアルポート連携のログを「COM1.log」のような名前で保存しようとして事故る、Linux側で作られた「aux」というフォルダーがWindowsで展開できない、というのが現場での現れ方です。

補足として、パス正規化の仕様上、従来は「CON」「COM1.TXT」のように予約名で始まるパスはデバイスパス(\\.\CON)に変換されて解釈されてきました。Windows 11ではこの解釈が変わり、レガシーデバイスを指すには\\.\CONのような完全な形式の指定が必要になっています。7 とはいえ古いOSと既存アプリの両方が従来解釈のまま残っているので、業務データの名前として予約名を避けるべきという結論は変わりません

4.2. 末尾のスペース・ドットは「黙って」消える

公式の命名規則は「ファイル名・ディレクトリ名をスペースまたはドットで終わらせないこと」と定めています。6 さらに踏み込むと、Windowsのパス正規化には「パスが区切り文字で終わらない場合、末尾のドットとスペース(U+0020)をすべて取り除く」という明確なルールがあります。7

これが実務で厄介なのは、エラーにならず黙って別の名前になることです。ユーザーが「報告書v2.」という名前を入力すると、作成されるのは「報告書v2」です。逆に、SMB経由でLinux側から作られた「report 」(末尾スペース)のようなファイルは、Windowsの通常のパス指定では正規化で名前が変わってしまうため到達できません。こうした「正規化では到達できないが合法な名前」へアクセスする手段が、正規化をスキップする\\?\プレフィックスです。公式ドキュメントも「hidden.のようなファイルは他の方法ではアクセス不可能」と、この用途を明示しています。7

なお、先頭のドットは合法です(.gitignoreのような名前は問題なく作れます)。6

5. 大文字小文字は「保持するが、区別しない」

Windowsのファイルシステムの既定動作はcase-preserving, case-insensitiveです。Readme.txtという名前で作成すればその大文字小文字が保持されて表示されますが、検索や比較では大文字小文字が無視され、README.TXTでも同じファイルに到達します。ドライブ文字も同様に大文字小文字を区別しません。86

公式の命名規則はアプリ開発者向けに「大文字小文字の区別を仮定するな(OSCAR、Oscar、oscarは同じ名前とみなせ)」と明記する一方、NTFS自体はPOSIX的な大文字小文字の区別をサポートしている(ただし既定では無効)とも述べています。6

これが表に出てくるのがLinux連携です。Windows 10 ビルド17107以降では、ディレクトリ単位で大文字小文字の区別を有効化できます。9

# 管理者権限のPowerShellで
fsutil.exe file setCaseSensitiveInfo C:\work\linux-src enable
fsutil.exe file queryCaseSensitiveInfo C:\work\linux-src

WSLでLinux由来のソースツリー(Makefilemakefileが共存するなど)を扱う場合には有効な手段ですが、公式ドキュメント自身が警告する副作用があります。ファイルシステムを大文字小文字非区別と仮定しているWindowsアプリは、区別が有効なディレクトリでファイルにアクセスできなくなることがあります。また、フラグの変更は対象ディレクトリが空でないとできず、新規作成されるサブディレクトリは親の設定を継承します。9 歴史的には、名前が大文字小文字だけ異なる2つのファイルがあると、Explorerには両方表示されるのにどちらを選んでも同じ一方しか開けない、という現象も公式に記録されています。9

業務アプリの設計としては、「Windows上では大文字小文字違いは同名」と扱うのが既定、ただしLinuxへ渡るファイル名は大文字小文字違いの衝突をチェックする、が実務的な落とし所です。Linux連携ではファイル名以前に文字コードでも罠があるので、「Windows文字コード入門 - Linux連携で起きる文字化け」も併せて確認してください。

6. 業務アプリでの実務 ── パス結合・サニタイズ・判断表

6.1. パス結合はPath.Combineの仕様を知って使う

パスの文字列連結を+で書くのは論外として、Path.Combineにも知っておくべき仕様があります。2番目以降の引数にルート付きパスが渡されると、それより前の引数はすべて無視されます10

var baseDir = @"C:\App\Data";

// ユーザー入力がルート付きだと、baseDirは黙って捨てられる
Path.Combine(baseDir, @"C:\Windows\secret.txt"); // → "C:\Windows\secret.txt"
Path.Combine(baseDir, @"\evil.txt");             // → "\evil.txt"(現在ドライブのルート)

ユーザー入力や設定ファイル由来の文字列をそのまま第2引数に渡すと、意図した保存フォルダーの外へ書き込む脆弱性になります。公式ドキュメントもこの挙動が機微なファイルへの意図しないアクセスにつながり得ると注意しており、代替としてPath.Join / Path.TryJoin(.NET Frameworkにはありません)を挙げています。1014 どちらを使うにせよ、最終的にはPath.GetFullPathで正規化した結果がベースディレクトリ配下かを検証するのが定石です。

// ベース側も正規化したうえで、相対パスに変換して判定する。
// 文字列の前方一致より、末尾区切りの有無やベースがドライブルートの場合の揺れに強い
var baseFull = Path.GetFullPath(baseDir);
var fullPath = Path.GetFullPath(Path.Combine(baseFull, userInput));
var relative = Path.GetRelativePath(baseFull, fullPath);
if (relative == ".." ||
    relative.StartsWith(".." + Path.DirectorySeparatorChar) ||
    Path.IsPathRooted(relative)) // 別ドライブ・UNCへ抜けた場合は絶対パスが返る
{
    throw new InvalidOperationException("保存先が想定フォルダーの外を指しています。");
}

Path.GetRelativePath はOS既定の流儀でパスを比較します。つまりWindowsでは大文字小文字を区別しない前提の判定で、これは次章で説明する「Windowsの既定は大文字小文字を区別しない」という動作に合っています。裏を返すと、ディレクトリ単位で大文字小文字の区別を有効化した場所(次章参照)では、Datadata が別ディレクトリになり得るため、大文字小文字を無視した判定では「大文字小文字だけが違う別のフォルダー」を配下と誤判定する余地が生まれます。そうした構成を扱う可能性があるなら、区別が有効な場所をベースディレクトリとして受け付けない方針にするのが安全です。

もう1点、この判定は文字列として正規化したパスに対する判定でしかないことも意識しておいてください。ベースディレクトリ配下にジャンクションやシンボリックリンクが存在すると、文字列上は配下を指していても実体はベースの外、ということが起こり得ます。しかもリンクは末端のファイルだけでなく途中のフォルダーにも入り込めるため(ベース\リンク\ファイル.txt の形)、末端だけを File.ResolveLinkTarget で調べても見抜けません。まず、信頼できない利用者がベース配下にリンクやジャンクションを作成できる構成をそもそも避けるのが第一です。そのうえで厳密に守る必要があるなら、実際にファイルを開いたハンドルから確定パスを取得して(Win32の GetFinalPathNameByHandle)それがベース配下かを検証するか、パスの各フォルダー構成要素を順にリンクでないか検査してください。

6.2. ユーザー入力ファイル名のサニタイズ

「取引先名+日付.csv」のようにユーザー入力からファイル名を組み立てる機能では、サニタイズを1か所に集約します。Path.GetInvalidFileNameCharsが出発点ですが、この配列は不正文字の完全な集合を保証しないと公式に明記されています。11 予約デバイス名と末尾のドット・スペースはこのAPIでは検出できないので、自前のチェックを足します。

private static readonly HashSet<string> ReservedNames =
    new(StringComparer.OrdinalIgnoreCase)
    {
        "CON", "PRN", "AUX", "NUL",
        "COM1","COM2","COM3","COM4","COM5","COM6","COM7","COM8","COM9",
        "LPT1","LPT2","LPT3","LPT4","LPT5","LPT6","LPT7","LPT8","LPT9",
        "COM¹","COM²","COM³",  // 上付き数字のCOM¹〜COM³も予約名
        "LPT¹","LPT²","LPT³",  // 同じくLPT¹〜LPT³
    };

public static string SanitizeFileName(string input)
{
    var invalid = Path.GetInvalidFileNameChars();
    var name = new string(input.Select(c => invalid.Contains(c) ? '_' : c).ToArray());

    name = name.TrimEnd(' ', '.');            // 末尾スペース・ドットは黙って落とされるため除去

    // ファイル名1要素の長さ上限(通常255文字)にも収める。
    // フォルダー階層やアプリが後付けするサフィックスの余地を残して控えめに切り詰める
    const int MaxNameLength = 120;
    if (name.Length > MaxNameLength)
    {
        var ext = Path.GetExtension(name);
        if (ext.Length > 20)
        {
            ext = ""; // 異常に長い「拡張子」は拡張子として温存しない(負の範囲指定で例外になるのを防ぐ)
        }
        name = name[..(MaxNameLength - ext.Length)].TrimEnd(' ', '.') + ext;
    }

    // 空・予約名の判定は必ず「最終形」に対して行う。
    // 切り詰めやTrimEndの結果、空文字や予約名(NULなど)に化けるケースを拾うため
    var stem = name.Split('.')[0];            // NUL.txt対策: 拡張子前の部分で予約名を判定
    if (name.Length == 0 || ReservedNames.Contains(stem))
    {
        name = "_" + name;                    // 1文字足しても上限255には十分収まる
    }
    return name;
}

CSV出力のファイル名でこの問題に遭遇することが多いので、CSVそのものの実務は「CSVは「ただのテキスト」ではない ── C#業務アプリのCSV実務」も参考にしてください。

6.3. 相対パスとカレントディレクトリの罠

相対パスには2つの罠があります。第一に、カレントディレクトリはプロセス単位の設定なので、どのスレッドからでもいつでも変更され得ます。公式ドキュメントは「相対パスはマルチスレッドアプリでは危険」とまで書いており、.NET Core 2.1以降なら基準パスを明示できるPath.GetFullPath(string, string)が使えます。7 第二に、C:tmp.txtのようにドライブ文字直後にバックスラッシュがない形式は「Cドライブのカレントディレクトリからの相対パス」であり、絶対パスではありません。この「ドライブ相対パス」はプログラムやスクリプトの定番のバグ源として公式に名指しされています。7

設定ファイルやユーザー入力で受け取ったパスは、受け取った時点でPath.GetFullPathにより絶対パス化してからログに残す・検証する、を習慣にしてください。

6.4. 判断表 ── 長いパスをサポートすべきか、入口で弾くべきか

状況 推奨 理由
ユーザーが保存先を自由に選ぶ一般業務アプリ 入口で検証して弾く(フルパス長・ファイル名を保存前にチェックし明確なエラーを出す) 自アプリが対応してもExplorerや連携先ツールが開けない事故が残るため1
バックアップ・同期・アーカイブ展開など、他者が作った深い階層を「読む」側 長パス対応する(.NET Core系+必要に応じマニフェスト、Frameworkなら4.6.2+設定) 入力を制御できず、読めないと業務が止まるため54
自アプリが深い階層を「作る」側 原則作らない設計に見直す(階層のフラット化、ハッシュ名の採用など) 作ったパスの利用者(人間・他アプリ)が非対応である可能性が高いため1
Linux/WSLとファイルをやり取りする 予約名・大文字小文字衝突・末尾文字を転送前にチェック Windows側で到達不能なファイルが生まれるため69
ユーザー入力からファイル名を生成する GetInvalidFileNameChars+予約名・末尾文字のサニタイズを共通関数に集約 APIの配列だけでは不完全なため11

7. トラブルシューティング ── 「エクスプローラーでは見えるのに開けない」

「Explorerにはファイルが見えているのに、アプリで開くと『ファイルが見つかりません』」という相談の切り分け手順です。

確認ポイント 手段 該当したら
フルパスが260文字近辺か PowerShellで (Get-ChildItem -Recurse).FullName \| Where-Object { $_.Length -ge 250 } 上位フォルダー名を短縮するか、長パス対応(第3章)を検討
ファイル名が予約名か(aux、con、com1など) 名前を目視確認。拡張子付きも対象6 リネーム(作成元がLinux等なら転送時に変換)
末尾にスペース・ドットがないか cmd /c dir /x や引用符付き表示で確認 \\?\プレフィックス付きパスで削除・リネーム7
大文字小文字だけ異なる同名ファイルがないか WSL/Git由来のフォルダーで発生しやすい9 片方をリネームするか、対象ディレクトリの用途を見直す
相対パス・ドライブ相対パスを使っていないか ログに実際に開こうとしたパスを絶対パスで記録 Path.GetFullPathで絶対化してから使用7

調査の第一歩としておすすめなのは、アプリのエラーログに「開こうとしたパスそのもの」を絶対パス・引用符付きで記録することです。「ファイルが見つかりません」という例外メッセージだけでは、パスが切り詰められたのか、正規化で名前が変わったのか、そもそも別のディレクトリを見ていたのかを事後に区別できません。引用符で囲んで記録しておけば、末尾スペースのような視認しにくい問題も一目で分かります。

なお、DLLのロード失敗も「ファイルが見つかりません」系エラーの常連ですが、こちらはパス長よりも検索順序の問題であることが多く、「Windows DLL名前解決の仕組み - 検索順序とSxS」で整理しています。

8. まとめ

  • MAX_PATH=260は「D:\+最大256文字+終端NUL」を含むWin32 APIの制限で、NTFS自体は約32,767文字の拡張長パスを扱えます。ディレクトリはさらにMAX_PATH−12までという制限もあります。
  • 260文字を超えるには、\\?\プレフィックス(Unicode版API限定・相対パス不可)か、Windows 10 1607以降の長パス有効化(レジストリLongPathsEnabled+マニフェストlongPathAware両方)が必要です。
  • .NET (Core)/5+は長いパスを暗黙に扱い、.NET Frameworkは4.6.2以降ターゲットでランタイムチェックが外れます。ただしExplorerを含む非対応アプリが残るため、「作れるか」と「ユーザーが扱えるか」を分けて判断します。
  • ファイル名は予約文字(< > : " / \ | ? *)と制御文字が不可、CON・NUL・COM1などの予約デバイス名は拡張子付きでも不可、末尾のスペース・ドットは正規化で黙って消えます。
  • 大文字小文字は「保持するが、区別しない」が既定です。fsutil file setCaseSensitiveInfoによるディレクトリ単位の区別はWSL連携では有効ですが、Windowsアプリ側の誤動作リスクと引き換えです。
  • 実装は、Path.Combineのルート付き引数の仕様を踏まえたベースディレクトリ検証、GetInvalidFileNameChars+予約名・末尾文字チェックのサニタイズ共通化、相対パス排除(Path.GetFullPathでの絶対化)が定石です。

関連記事

関連する相談領域

合同会社小村ソフトでは、「特定の環境・特定のファイルだけ開けない」といったファイルI/O起因の不具合調査、既存業務アプリの長パス対応やファイル名バリデーション設計の見直し、Windows・Linux混在環境でのファイル連携の設計相談を扱っています。

参考リンク

  1. Microsoft Learn, Maximum Path Length Limitation. MAX_PATH=260の定義と「ドライブ文字+コロン+バックスラッシュ+256文字+終端NUL」という内訳、Unicode版APIと\\?\プレフィックスによる約32,767文字の拡張長パス、コンポーネント長(一般に255文字)、相対パスが常にMAX_PATHに制限されること、ディレクトリ作成がMAX_PATH−12までであること、シェルとファイルシステムの要件が異なりWin32で作れるパスをシェルUIが解釈できない場合があることについて。  2 3 4 5 6 7 8 9 10 11

  2. Microsoft Learn, NTFS overview. NTFSが長いファイル名と約32,767文字の拡張長パスをサポートすること、8.3エイリアスによる後方互換について。  2

  3. Microsoft Learn, Maximum Path Length Limitation ── Enable long paths in Windows 10, version 1607, and later. Windows 10 1607以降でレジストリ値LongPathsEnabled=1とアプリマニフェストのlongPathAware要素の両方が必要なこと、グループポリシーでの設定、レジストリ値がプロセス単位でキャッシュされること、制限が外れるWin32関数の一覧について。  2 3

  4. Microsoft Learn, File path formats on Windows systems ── Skip normalization. .NET Coreおよび.NET 5以降が長いパスを暗黙に処理しMAX_PATHチェックを行わないこと(MAX_PATHチェックは.NET Frameworkのみ)、\\?\が正規化をスキップする仕組みであることについて。  2 3

  5. Microsoft Learn, Retargeting changes for migration to .NET Framework 4.6.x. .NET Framework 4.6.2ターゲットで長いパス(最大32K文字)がサポートされ260文字制限が除去されたこと、旧ターゲットのアプリがSwitch.System.IO.BlockLongPaths=falseでオプトインできることについて。  2 3

  6. Microsoft Learn, Naming Files, Paths, and Namespaces. 予約文字(< > : " / \ | ? *)と制御文字(0〜31)、予約デバイス名(CON/PRN/AUX/NUL/COM1〜9/LPT1〜9および上付き数字)、NUL.txtのような拡張子付きも予約名と等価であること、名前を末尾スペース・ドットで終わらせないこと、先頭ドットが合法なこと、大文字小文字の区別を仮定すべきでないこととNTFSのPOSIXセマンティクス、\\?\プレフィックスの動作とUnicode API要件について。  2 3 4 5 6 7 8 9 10 11 12

  7. Microsoft Learn, File path formats on Windows systems ── Path normalization. パス正規化で末尾のドットとスペースが取り除かれること、hidden.のような名前は\\?\でしかアクセスできないこと、CONなどのレガシーデバイス名の解釈とWindows 11での変更、ドライブ相対パス(C:tmp.txt)がバグの一般的な原因であること、カレントディレクトリがプロセス単位で相対パスがマルチスレッドで危険なこと、Path.GetFullPath(String, String)について。  2 3 4 5 6 7 8

  8. Microsoft Learn, File path formats on Windows systems ── Case and the Windows file system. ディレクトリ名・ファイル名が作成時の大文字小文字を保持する一方、名前の比較は大文字小文字を区別しないことについて。  2

  9. Microsoft Learn, Adjust case sensitivity. Windows 10 ビルド17107以降のディレクトリ単位の大文字小文字区別(fsutil.exe file setCaseSensitiveInfo)、変更には管理者権限と空のディレクトリが必要なこと、新規サブディレクトリが設定を継承すること、大文字小文字非区別を仮定するWindowsアプリが誤動作し得るという警告、大文字小文字違いの2ファイルはExplorerに両方表示されても一方しか開けなかったことについて。  2 3 4 5 6

  10. Microsoft Learn, Path.Combine Method. 最初以外の引数にルート付きパスが含まれると、それより前のパス要素が無視されルート付き要素から始まる文字列が返ること、機微なファイルへの意図しないアクセスにつながり得ること、代替としてJoin/TryJoin(.NET Frameworkでは利用不可)が挙げられていることについて。  2 3

  11. Microsoft Learn, Path.GetInvalidFileNameChars Method. ファイル名に使えない文字の配列を返すこと、返される配列が不正文字の完全な集合を保証せず、ファイルシステムによって異なり得ることについて。  2 3

  12. Microsoft Learn, PathTooLongException Class. パスがシステム定義の最大長を超えたときに投げられる例外であること、.NET Framework 4.6.2以降では「32,767文字超」または「OSがエラーを返した場合」に限って投げられることについて。  2

  13. Microsoft Learn, Long Path Support (NuGet CLI). .NET Frameworkベースのツールが長パスを使うための実際の構成(Windows 10 1607以降または1511+.NET Framework 4.6.2、Win32 long pathsポリシー、longPathAwareマニフェスト+UseLegacyPathHandling無効化)と、Visual Studioやmsbuildのrestoreが長パス未対応であることについて。  2

  14. Microsoft Learn, Path.Join Method. Joinがルート付きの後続パスを破棄せず連結すること、Combineとの挙動差の実例について。 

同じタグを共有する最新の記事です。さらに近い話題で知識を深められます。

ネットワークドライブとUNCパスの落とし穴 ── 業務アプリでファイルサーバー(共有フォルダ)を扱う実務

業務アプリからファイルサーバー(共有フォルダ)へ出力・取り込み・監視を行うときの定番トラブルを整理します。ドライブ文字(Z:)がログオンセッション単位でありサービスや昇格プロセスから見えない理由、Windowsサービスの実行アカウントごとに共有側で必要な権限、資格情報とエラ...

自社開発のWindowsアプリがウイルス扱いされたら ── Microsoft Defenderの誤検知対応と、性能影響との付き合い方

配布した自社開発のWindowsアプリがMicrosoft Defenderにウイルスとして検知・隔離されたときの正規の対処を整理します。機械学習・クラウド保護・レピュテーションという現代のウイルス対策の仕組み、Microsoftへの誤検知報告(ファイル申請)、隔離からの復...

このテーマと近いトピックページです。記事を起点に、関連するサービスや他の記事へ進めます。

この記事は次のサービスページにつながります。近い入口からご覧ください。

よくある質問

この記事のテーマについて、相談時によくある質問をまとめています。

Windowsのパスは何文字まで使えますか?
Win32 APIの既定ではMAX_PATH=260文字で、これは「ドライブ文字+コロン+バックスラッシュ+256文字ぶんのパス文字列+終端NUL」を含んだ長さです。NTFSなどのファイルシステム自体はもっと長いパスを扱え、Unicode版APIに\\?\プレフィックス付きのパスを渡せば合計約32,767文字まで指定できます。ただし1つのフォルダー名・ファイル名(コンポーネント)は一般に255文字までで、相対パスは常にMAX_PATHまでに制限されます。
MAX_PATHの260文字制限はどうすれば解除できますか?
Windows 10 バージョン1607以降で、レジストリ値LongPathsEnabled=1(またはグループポリシーの「Win32の長いパスを有効にする」)と、アプリケーションマニフェストのlongPathAware要素の両方を設定すると、多くのWin32ファイル関数で260文字制限が外れます。片方だけでは有効になりません。.NET(Core)/.NET 5以降のランタイムはMAX_PATHチェックを行わず長いパスを暗黙に扱い、.NET Frameworkは4.6.2以降をターゲットにするとランタイム側の260文字チェックが外れます。ただしExplorerを含め長パス非対応のアプリは残るため、作った長いパスを誰が扱うかまで含めて判断が必要です。
CONやNULという名前のファイルが作れないのはなぜですか?
CON、PRN、AUX、NUL、COM1〜COM9、LPT1〜LPT9などはMS-DOS時代から続くデバイスの予約名で、Windowsはこれらの名前をファイルではなくデバイスとして解釈するためです。NUL.txtのように拡張子を付けてもNULと同じ扱いになるので回避できません。Windows 11ではパス解釈の挙動が一部変わりましたが、古いOSや多数のアプリが従来の解釈のままなので、業務データのファイル名としては引き続き避けるのが安全です。
ファイル名の大文字小文字はWindowsでは区別されますか?
既定では「保持するが、区別しない」(case-preserving, case-insensitive)です。Readme.txtという名前で作れば表示上はその大文字小文字が保たれますが、README.TXTを開こうとしても同じファイルに到達します。NTFSはPOSIX的な大文字小文字の区別もサポートしており、Windows 10 ビルド17107以降ではfsutil.exe file setCaseSensitiveInfoでディレクトリ単位の区別を有効化できますが、大文字小文字を区別しない前提のWindowsアプリが誤動作する副作用があるため、WSL連携など必要な場面に限定すべきです。

著者プロフィール

記事の著者プロフィールページです。

小村 豪

合同会社小村ソフト 代表

Windows ソフト開発、技術相談、不具合調査を中心に、既存資産が残る案件や原因が見えにくい障害調査に強みがあります。

ブログ一覧に戻る