A certain engineer "COMPLEX"

開発メモ その9 260文字を超えるパスを有効にする

Windows 10 Anniversary Updateが出ましたね。


2018/04/05 更新
Creators Updateから挙動が変わっています。詳細は、開発メモ その104 260文字を超えるパスを有効にする 追試験を参考にしてください。

MAXPATHの制限がなくなります


制限付きですが、忌まわしきファイルパス長の制限がなくなります。
.NETからのアクセスの制限をなくすなら、.NET 4.6.2をインストール、app.configを編集し、かつローカルグループポリシーを修正する必要があります。

app.configに下記のような記述を追加します。


<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<AppContextSwitchOverrides value="Switch.System.IO.UseLegacyPathHandling=false;Switch.System.IO.BlockLongPaths=false" />
</runtime>
</configuration>

また、ローカルグループポリシーで下記の項目を有効にします。変更後再起動します。

ローカル コンピューター ポリシー -> コンピューターの構成 -> 管理用テンプレート -> システム -> ファイルシステム -> Win32 の長いパスを有効にする

Win32 の長いパスを有効にする

説明を見ると、

Win32 の長いパスを有効にすると、明示された win32 アプリケーションと Windows ストア アプリケーションが、ノードあたり通常 260 文字の制限を超えるパスにアクセスできるようになります。この設定を有効にすると、プロセス内で長いパスにアクセスできるようになります。

とありますが、.NETも安心して有効にできます。
Windows上の.NETアプリはWin32のラッパーですからね。

ポイントは、明示されたです。
この明示が前段のapp.configへの追記になります。

これを有効にしないと、app.configに追記しても動きません。

設定が効いてない?


ここまでを確認するために、下記のサンプルを置きました。

今回のソースです。

Sample source code for Demonstration, Experiment and Test - takuya-takeuchi/Demo

簡単なサンプルで


using System;
using System.IO;
using System.Linq;

namespace net462manifest
{
class Program
{
static void Main(string[] args)
{
var random = new Random();
var length = 200;
var str = string.Join("", Enumerable.Range(0, length).Select(s => random.Next(0, 9).ToString()));
var path = Path.Combine("B:\\", str);
path = Path.Combine(path, str);

try
{
Directory.CreateDirectory(path);
Console.WriteLine($"{path} is created.");
}
catch (Exception ex)
{
Console.WriteLine(ex.GetType().Name);
Console.WriteLine(ex.Message);
}
}
}
}

という感じです。名前空間が違うだけでサンプル全て、B:\<200文字のフォルダ名>\<200文字のフォルダ名>なフォルダを作成するだけです。
これを、.NET 4.6.1と4.6.2をターゲットでビルドし、app.configの有無で、Windows 10 + .NET 4.6.2で動かしてみました。

が、不思議な結果になりました。
.NET 4.6.1 + app.configなしの環境「だけ」で、


指定されたパス、ファイル名、またはその両方が長すぎます。完全限定型名は 260 文字未満で指定し、ディレクトリ名は 248 未満で指定してください。

という例外を投げました。
理想では、「.NET 4.6.2 + app.configあり」の環境だけで実行できるつもりでした。
少なくとも、app.configがないとダメ、という感じだと思いましたが、この結果は不思議です。

ローカルグループポリシーの有効前後で結果が変化し、最終的にこうなりました。

 ローカルグループポリシー有効ローカルグループポリシー無効
App.configありApp.configなしApp.configありApp.configなし
.NET 4.6.2OKOKDirectoryNotFoundExceptionDirectoryNotFoundException
.NET 4.6.1OKPathTooLongExceptionDirectoryNotFoundExceptionPathTooLongException

調べてみると、下記のMSDN Blogに記事がありました。

In my prior post I talked briefly about the new path handling in .NET 4.6.2. In the latest drop (14367) PowerShell opts into both of the new behaviors so you...

If you target 4.6.2 this isn’t necessary, but if you want to enable the behavior for existing code, here is config file snippet you need to use.

つまり、4.6.2なら特別な設定は不要で、既存のコードで有効にしたいなら、configファイルが必要だよ、ってことらしいです。
グループポリシーで設定有効後、4.6.2で常に動いたのはそういうことのようです。

Conclusion


4.6.2以降はグループポリシー以外は特に気にすることはない模様。
ただ、実運用において、客先でこの設定をバッチか何かで有効にすることはできるのだろうか?無理な気がする。

Source Code


https://github.com/takuya-takeuchi/Demo/tree/master/Memo9

8 thoughts on “開発メモ その9 260文字を超えるパスを有効にする

  1. 村井誠

    貴重な情報ありがとうございます。

    ただ、
    全く同じことをやっても駄目ですね。
    「ローカル コンピューター ポリシー -> コンピューターの構成 -> 管理用テンプレート -> システム -> ファイルシステム -> Win32 の長いパスを有効にする」
    の設定が全く効きません。
    結果としては、少し異なり、.net 4.6.1 も .net 4.6.2 も App.confなしの場合、文字数オーバーのエラー
    App.Conf 有りの場合、DirectoryNotFoundException のエラーとなります。

    なぜ?ローカル コンピューター ポリシー の設定が効かないのか徹夜含みで丸2日
    ネット上の情報等で調べましたが、調べたことはやりつくしたのですが、NGです。

    Win10 Pro スタンドアロンワークステーション / VS2013を使っています。

    何かわかることがあれば、教えてください。
    よろしくお願いいたします。

    1. Takuya Takeuchi Post author

      Windows10 1709 (.NET 4.7.1インストール)で試したところ、確かに動かないですね。
      HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem\LongPathsEnabled1になっていて、長いパスが有効になっているのですが....

      • 4.7.1でビルドし直しても動かない。
      • グループポリシーの設定をON->OFF->ON、再起動しても変化なし。

      なので、Hyper-V上にAniversary Update(1607)の英語版を用意して検証してみました。
      そちらでは問題なく動作しています。挙動が変わったのか、もう少し検証が必要ですね。

      1. 村井誠

        わざわざ検証していただき誠にありがとうございます。
        レジストリも確認しましたが確かに1入ってるんですよね。

        英語版で大丈夫んあのですね。

        5万円かかりますが、インシデント発生させて、MSに問合せしてみようと思まス。

        よろしくお願いいたします。

          1. 村井誠

            app.manifest ファイルを追加して
            「開発メモ その104」の通り記述し、サンプルを動かしてみましたら、
            上手に動きました。結果も。「開発メモ その104」の通りです。
            ありがとうございました。

            今回は私は、スマホをUSB接続して、スマホSDカード内で選択したフォルダのファイルをコピーしようとしています。
            しかし、スマホのSDカードのrootであれば、まだかろうじてパスが230文字程度で、その処理は成功しますが、
            ほとんどのパスは、260文字を超えてしまいます。
            越えた場合、ダイアログでフォルダ選択時にすでにエラーとなってしまいます。

            サンプルは動作したので、私の今回の本命である
            C#より使用している shell32 の BrowseForFolder で 260を超えるパスになるフォルダを選択してみました。
            USB接続スマホのフォルダ選択のためこにこのフォルダ選択ダイアログを必要としています。
            スマホ(ポータブル)デバイス内のほとんどのパスは、ユーザフレンドリーではない長いMTPパスです。
            結果は残念ながら、ローカルディスク上でも、260文字を超える場合、shell32.BrowseForFolderでは、NGでした。
            スマホのSDカード内でも同様にNGでした。
            ダイアログ内でフォルダ選択時に、すでにエラーとなってしまう現象です。この現象は以前より変わりません。
            shell32.BrowseForFolderを使っている理由は、通常の.net付属のダイアログですと、接続されたポータブルデバイスが出てこないためです。

            今回書いていただいた記事は、.net アプリケーションへの設定なので、COMが絡むと駄目かな?と思いましたが、
            やはりだめでした。

            この件は、ネット検索でも多くの人がとん挫しているようですが、
            「ローカル コンピューター ポリシー -> コンピューターの構成 -> 管理用テンプレート -> システム -> ファイルシステム -> Win32 の長いパスを有効にする」
            は、相当に有益な情報でしたので、COMのShell32でこの設定を効かせるためにためにどうすればよいのか?
            もう少し、頑張って調べてみようと思います。

            ありがとうございました。
            よろしくお願いいたします。

          2. Takuya Takeuchi Post author

            MTPとか私は全く知りませんでしたので、軽く調べてみたのですが、そもそもMTPはストレージデバイスでは無いため、Windowsの「シェル」からはアクセスできない、というのが定番の模様。
            結局、シェルはWin32のラッパーでしか無いので、MTPは想定していないのでしょう。
            できるかどうかはしりませんが、Dokan.netでも使って、MTPを経由してファイル一覧を列挙取得する仮想ドライブをマウントする、というのはできるかもしれません。
            そうすれば、オブジェクトの列挙は、Dokan.netを経由して取得できると思いますので。オブジェクトのスキャンとかは、https://sugi.sakura.ne.jp/a/150829a.html が参考になるかも。
            というか、Dokan.netが関与した、MTPのアクセスとして、https://www.stanislaschevallier.fr/software/mtpexplorer/ ってのがありました。
            英語サイトでは、WebDavやFTPでアクセスしろ、ともありました。rootがとれるならもっと別の方法もあるとか。

            あと、Shell32がどうとかではなく、長いパスの影響を受けるのが一部のWin32 APIだけなので、選択ダイアログ系統が、その影響を受けるAPIを使っていない、または使っているが戻りの領域がMAX_PATHの制限を解除できていないのはないのでしょうか?

  2. 村井誠

    ご返事ありがとうございます。
    >選択ダイアログ系統が、その影響を受けるAPIを使っていない、
    >または使っているが戻りの領域がMAX_PATHの制限を解除できていないのはないのでしょうか?
    その通りだと思います。
    「ローカル コンピューター ポリシー -> コンピューターの構成 -> 管理用テンプレート -> システム -> ファイルシステム -> Win32 の長いパスを有効にする」だけでこのダイアログ選択APIも動作するのではないか?という期待もあったです。
    これは多分おしいですが、いかんともしがたいと思うので、
    上げていただいた他の手法を試してみようと思います。
    上げていただいた他の手法は、頂いた文章の中ではまだほとんど理解できておりませんので、
    ちょっと、挑戦してみます。
    場所違いかもしれませんが、色々試してみましたら、どちらにせよ結果をこちらでご報告させていただきます。

    色々、ありがとうございます。
    よろしくお願いいたします。

コメントを残す

メールアドレスが公開されることはありません。

%d人のブロガーが「いいね」をつけました。