DOBON.NET DOBON.NETプログラミング掲示板過去ログ

ロングファイル名パスでのファイル操作

環境/言語:[Windows XP .NET3.5]
分類:[.NET]

いつも参考にさせていただいております。

タイトルにもる通りなんですが、ロングファイル名(260文字超過)パスにおいて
Pathクラス、Fileクラス、Directoryクラスを利用したいのですが、
メソッドによっては例外が起こってしまって使用できません。

ロングファイル名パスでファイルコピー/削除やフォルダ作成/削除処理を
するにはどうすれば良いでしょうか?

対策をご存知の方、教えてください。お願いします。

================開発環境===================
OS: Windows XP SP3
VS: 2008
.NET:3.5
2012/04/11(Wed) 19:34:22 編集(投稿者)

■No30279に返信(ホエホエさんの記事)
> ロングファイル名パスでファイルコピー/削除やフォルダ作成/削除処理を
> するにはどうすれば良いでしょうか?

MAX_PATH 制限は、"\\?\" で始まる UNC パスを使う事で回避できます。
これで3万2千文字まで許可されます。

ただしこの手法は、System.IO 名前空間のクラスに対しては利用できません。
「?」を含んだパス指定だと .NET 側のパス検証で弾かれるため、P/Invoke にて
DeleteFile、CopyFile、RemoveDirectory 等の API (の Unicode バージョン)を
呼び出す必要があります。


ちなみに Windows 98 や ME の場合は、これらの API に Unicode 版が無いため、
いずれにしても MAX_PATH が上限となります。(今回は WinXP なので問題なし)
■No30280に追記(魔界の仮面弁士の記事)
> MAX_PATH 制限は、"\\?\" または "\\?\UNC" で始まる UNC パスを使う事で

API を使う以外の方法としては、COM の FileSystemObject を使うことでも、
"\\?\" で始まるパス指定によるディレクトリ作成を行えるようです。


以下は、JScript.NET での検証コードです。
肝心の『C# から MAX_PATH を超えるパスを指定した場合』は未検証ですが…。

import System;
import System.IO;
import System.Runtime.InteropServices;

var fso : System.Object = new ActiveXObject("Scripting.FileSystemObject");
if(fso.FolderExists("\\\\?\\C:\\TEMP")) {
  if(!fso.FolderExists("\\\\?\\C:\\TEMP\\SampleDir")) {
    fso.CreateFolder("\\\\?\\C:\\TEMP\\SampleDir");
  }
}
if(Marshal.IsComObject(fso)) Marshal.ReleaseComObject(fso);
魔界の仮面弁士さん>
ご返信ありがとうございます。

早速.NET標準のクラス&メソッドではなく、WIN32API関数の
PathIsDirectoryWやCreateDirectoryWに置き換えて、
フォルダの有無を調べつつ1階層ずつフォルダを作成する
処理にしてみましたがうまくいきません。
コードの1部を以下に記載します。

    string strRoot = "\\\\?\\C:"
    string strSubFolder = (TODO:260文字を超えるパスが入る)
    string[]PathData = strSubFolder.Split( '\\' );
    int nDataNum = PathData.Length;
    bool bRet = nDataNum > 0;
    string strTmpPath = strRoot;

    for( int nIndex = 0; bRet && nIndex < nDataNum; nIndex++ )
    {
        strTmpPath += "\\" + PathData[nIndex];

        //作成先パスのフォルダチェック
        bRet = Win32.PathIsDirectoryW( strTmpPath );
        if( !bRet )
        {
            //フォルダ作成
            bRet = Win32.CreateDirectoryW( strTmpPath, IntPtr.Zero );
        }
    }

動作させるとMAX_PATHのところまではフォルダが作成されるのですが
以降はフォルダ作成に失敗してしまってダメでした。

ちなみにプログラムの開発環境はWindowsXP SP3なんですが
実行環境はWindowsServer2008SR2をリモートデスクトップ経由で実行させており、
そのせいかエクスプローラ上でもMAX_PATHを超える位置でフォルダの作成に
失敗してしまいます。

何かマズい障害をしてしまってるのでしょうか?
2012/04/12(Thu) 18:16:50 編集(投稿者)

■No30294に返信(ホエホエさんの記事)
> そのせいかエクスプローラ上でもMAX_PATHを超える位置でフォルダの作成に
> 失敗してしまいます。

エクスプローラは基本的に、MAX_PATH の制限を受けるアプリであり、
深い階層のパスに対しては、ゴミ箱への移動やコピー処理に失敗します。
特に、ショートファイル名ですら MAX_PATH を越えてしまうようなパスになると、
ファイルのコンテキストメニューなどが正しく表示されないことさえあります。

フォルダ階層を辿っていって、ファイルの一覧を表示するぐらいならば
大丈夫だと思いますけれど、OS からの各種操作が制限されますし、
ほかのアプリも非対応であることが殆どです。ですから、MAX_PATH を超える
ファイルの読み込みぐらいならば良いですが、書き込みは避けた方が良いでしょう。


> PathIsDirectoryWやCreateDirectoryWに置き換えて、
CreateDirectory の方は、先の方法で MAX_PATH の上限を突破できますが、
http://msdn.microsoft.com/ja-jp/library/cc429194.aspx
PathIsDirectory や PathFileExists は MAX_PATH の制限を受けるような(未確認)。
http://msdn.microsoft.com/en-us/library/windows/desktop/bb773621.aspx

ディレクトリの存在チェックなら、GetFileAttributes が使えるかと思います。
http://msdn.microsoft.com/ja-jp/library/cc429313.aspx
もしくは FindFirstFile した上で、WIN32_FIND_DATA.dwFileAttributes を見るとか。

それと、パス全体として MAX_PATH を超える分には構いませんが、
ディレクトリ名単体のアイテム名が長すぎるのは NG だと思います。
添付ファイル: TooLongPath.png (63 KB)
魔界の仮面弁士さん>
ご返信ありがとうございます。本当に感謝しております。

エクスプローラでフォルダが作成できない件は了解しました。

あとご忠告通り、PathIsDirectoryではなくGetFileAttributesを
使用することにしました。

それで結果的にはWIN32APIとUNCパス(\\?\)を使用すれば
ローカルPC上であればMAX_PATHを超えるフォルダ作成が
できるようになりました!!

で追記で質問ばっかりで申し訳ないのですが、ネットワークPCの
共有フォルダパスにUNCパスを使用すると失敗してしまいます。

例えばコンピュータ名「PC001」にある共有フォルダ「shared」に
UNCパスで「\\?\\\PC001\shared」とするとダメで、UNCパスを
使わなければ260文字までのパスなら作成成功します。

ネットワークPCへのUNCパスは何か違う指定が必要なんでしょうか?
http://msdn.microsoft.com/ja-jp/library/aa365247.aspx
UNCを使う場合はプリフィクスがもうちょっと長いようですね。
Hongliangさん>
ご返信ありがとうございます。

ローカルPCロングパス  →\\?\C:\aaa
ネットワークPCロングパス→\\?\UNC\PC001\shared

とすることで処理できました!ありがとうございます♪
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板