C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

長いパス名のファイルをリネーム、削除する方法

[トピック内 20 記事 (1 - 20 表示)]  << 0 >>

■82875 / inTopicNo.1)  長いパス名のファイルをリネーム、削除する方法
  
□投稿者/ がもう (1回)-(2017/02/17(Fri) 12:44:26)

分類:[.NET 全般] 

VB.NETで、.net frameworkを使うと
260文字以上のパス名を扱うことができません。

http://lightbox.matrix.jp/ginpro/patio.cgi?mode=view&no=76

このページにあるように
APIのFindFirstFileやFindNextFileを使って
長いファイルパスのパス名を取得するコードを作りました

あとは、リネーム、削除を行いたいのですが
どのようなコードを使えば良いですか?




引用返信 編集キー/
■82876 / inTopicNo.2)  Re[1]: 長いパス名のファイルをリネーム、削除する方法
□投稿者/ 魔界の仮面弁士 (1123回)-(2017/02/17(Fri) 13:36:51)
No82875 (がもう さん) に返信
> あとは、リネーム、削除を行いたいのですが
> どのようなコードを使えば良いですか?

リネームは MoveFileW API、削除は DeleteFileW API です。
"\\?\" プレフィックスのパスを使うことで、MAX_PATH (260)の制限を回避できます。
引用返信 編集キー/
■82877 / inTopicNo.3)  Re[2]: 長いパス名のファイルをリネーム、削除する方法
□投稿者/ がもう (3回)-(2017/02/17(Fri) 13:53:06)
ありがとうございます。
うまくいきました。

ところで、もう一つ質問があります
http://macro-excel-vba.blogspot.jp/2015/06/dir.html

このページにあるようにDir関数では、
256文字以上のファイルパスを取得することができないはずです。
しかし私の環境では260文字であっても普通に取得することができます。

これはVB.NETとVBAの仕様の違いなのでしょうか?

引用返信 編集キー/
■82878 / inTopicNo.4)  Re[3]: 長いパス名のファイルをリネーム、削除する方法
□投稿者/ がもう (4回)-(2017/02/17(Fri) 14:42:11)
あと、
フォルダーのリネーム、削除はどのようにすれば良いですか?
https://msdn.microsoft.com/ja-jp/library/cc429618.aspx

このページには、ディレクトリもいけると書かれてあるのですが
フォルダーパスを指定しても、移動したり、削除したりすることができないのですが・・・
 
引用返信 編集キー/
■82879 / inTopicNo.5)  Re[3]: 長いパス名のファイルをリネーム、削除する方法
□投稿者/ 魔界の仮面弁士 (1124回)-(2017/02/17(Fri) 15:08:56)
No82877 (がもう さん) に返信
> このページにあるようにDir関数では、
> 256文字以上のファイルパスを取得することができないはずです。

「X:\ 256文字のパス文字列 <NUL>」が 260文字分である(MAX_PATH) ということですね。


> しかし私の環境では260文字であっても普通に取得することができます。

Dir 関数から長いパスが返ってくる場合の話でしょうか。
それとも、Dir 関数に長いパスを渡す場合の話でしょう。

たとえば手元の環境では、
 ' Len(fileName) < MAX_PATH
 ' Len(fullPath) > MAX_PATH
 fullPath = folderPath & "\" & fileName
のような場合において、
 S = Dir(folderPath & "*.*")
は取得できても、
 S = Dir(fullPath)
のように指定することはできません。

VBA
 エラー53
 ファイルが見つかりません

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

引用返信 編集キー/
■82880 / inTopicNo.6)  Re[4]: 長いパス名のファイルをリネーム、削除する方法
□投稿者/ 魔界の仮面弁士 (1125回)-(2017/02/17(Fri) 15:30:46)
No82878 (がもう さん) に返信
> フォルダーのリネーム、削除はどのようにすれば良いですか?
> https://msdn.microsoft.com/ja-jp/library/cc429618.aspx

C:\OLD_FOLDER_NAME\ を NEW_FOLDER_NAME に改名するなら

oldPath = "\\?\C:\OLD_FOLDER_NAME" & vbNullChar
newPath = "\\?\C:\NEW_FOLDER_NAME" & vbNullChar

ですね。MoveFileW の引数は MarshalAs(UnmanagedType.LPWStr) にしています。
vbNullChar は省略しても良さそうですが一応指定。


> 移動したり、削除したりすることができないのですが・・・

パス全体の長さとは別に、アイテム単位の長さ制限もあったはずですが、
拡張エラー情報は何を返してきていますか?
 Dim x = Err.LastDllError
 Dim y = Marshal.GetLastWin32Error()
 Dim z As New Win32Exception()


たとえば上記の MoveFileW の例でいえば、
 newPath = "\\?\C:\" & StrDup(255, "X") & vbNullChar
へのリネームは行えますが、
 newPath = "\\?\C:\" & StrDup(256, "X") & vbNullChar
となると、「New Win32Exception(123)」相当の理由で失敗します。


※ .NET 4.6.2 + ローカルポリシー「Win32 の長いパスを有効にする」を指定した場合の結果は未確認です。
引用返信 編集キー/
■82881 / inTopicNo.7)  Re[5]: 長いパス名のファイルをリネーム、削除する方法
□投稿者/ がもう (5回)-(2017/02/17(Fri) 16:07:48)
ありがとうございます。

ありがとうございます。

一つずつ質問いたします。


<DllImport("kernel32.dll", CharSet:=CharSet.Auto)> _
Function MoveFileW(ByVal src As String, ByVal dst As String) As Boolean
End Function


MoveFileW("\\?\D:\新しいフォルダー" & vbNullChar, "\\?\C:\ddd" & vbNullChar)


とやってみたのですがフォルダーの移動・リネームは行われません

なにが原因でしょうか?


引用返信 編集キー/
■82883 / inTopicNo.8)  Re[6]: 長いパス名のファイルをリネーム、削除する方法
□投稿者/ がもう (6回)-(2017/02/17(Fri) 16:10:49)
> S = Dir(folderPath & "*.*")
> は取得できても、
> S = Dir(fullPath)

そうです、前者のパターンで使用しています。

パスを直接指定しない場合には260文字制限がないのでしょうか?

検索しても見つかりませんでしたが
何文字までいけるのでしょうか?
 
引用返信 編集キー/
■82886 / inTopicNo.9)  Re[7]: 長いパス名のファイルをリネーム、削除する方法
□投稿者/ 魔界の仮面弁士 (1126回)-(2017/02/17(Fri) 16:57:30)
2017/02/17(Fri) 16:58:03 編集(投稿者)

No82883 (がもう さん) に返信
> S = Dir(folderP> パスを直接指定しない場合には260文字制限がないのでしょうか?

引数の長さに制限があるということですね。なので、引数に
「C:\200文字のフォルダ\200文字のサブフォルダ\*.TXT」
を指定するのならアウトでしょう。

しかし、「C:\200文字のフォルダ」のサブフォルダを調べることなら、
DirectoryInfo.GetFileSystemInfos メソッドや Dir でも行えます。

とはいえ、サブフォルダあるいはファイルの FileInfo インスタンスを
得られたとしても、使用できるのは一部のメンバーに限られます。
たとえば、FullName プロパティにアクセスすれば PathTooLongException 、
Delete メソッドを呼べば DirectoryNotFoundException といった具合に。


> 検索しても見つかりませんでしたが
> 何文字までいけるのでしょうか?

VBA の Dir にせよ VB.NET の Dir にせよ .NET の GetFileSystemInfos にせよ
基本的には FindFirstFile API のラッパーです。

なので、指定できる引数に対して MAX_PATH 制限が入るわけで。
https://msdn.microsoft.com/ja-jp/library/cc429233.aspx
引用返信 編集キー/
■82888 / inTopicNo.10)  Re[8]: 長いパス名のファイルをリネーム、削除する方法
□投稿者/ がもう (7回)-(2017/02/17(Fri) 17:24:10)
ありがとうございます。
納得しました。

一つ上の
<DllImport("kernel32.dll", CharSet:=CharSet.Auto)> _
Function MoveFileW(ByVal src As String, ByVal dst As String) As Boolean
End Function


MoveFileW("\\?\D:\新しいフォルダー" & vbNullChar, "\\?\C:\ddd" & vbNullChar)

がうまくいかない理由についてもご回答願います
引用返信 編集キー/
■82889 / inTopicNo.11)  Re[9]: 長いパス名のファイルをリネーム、削除する方法
□投稿者/ みい (59回)-(2017/02/17(Fri) 17:42:50)
No82888 (がもう さん) に返信
> MoveFileW("\\?\D:\新しいフォルダー" & vbNullChar, "\\?\C:\ddd" & vbNullChar)
>
> がうまくいかない理由についてもご回答願います

https://msdn.microsoft.com/ja-jp/library/cc429618.aspx
の解説欄
「ディレクトリを他のボリュームへ移動しようとすると、MoveFile 関数が失敗することに注意してください。」
かな?
引用返信 編集キー/
■82890 / inTopicNo.12)  Re[10]: 長いパス名のファイルをリネーム、削除する方法
□投稿者/ がもう (8回)-(2017/02/17(Fri) 17:46:21)
仰る通り、ボリュームを同じにするとうまくいきました。

もしボリュームを変えたい場合には
どうすれば良いのでしょうか?
 
引用返信 編集キー/
■82893 / inTopicNo.13)  Re[11]: 長いパス名のファイルをリネーム、削除する方法
□投稿者/ 魔界の仮面弁士 (1127回)-(2017/02/17(Fri) 18:30:14)
No82890 (がもう さん) に返信
> 仰る通り、ボリュームを同じにするとうまくいきました。

System.IO.Directory.Move("D:\DDD", "C:\DDD")
Rename("D:\DDD", "C:\DDD")

が失敗するのも同じ理由ですね。
API というよりは、ファイルシステム側の都合な気もしますが。



> もしボリュームを変えたい場合には
> どうすれば良いのでしょうか?

別ボリュームに新規フォルダを作成し、
逐次移動(Copy & Delete)させてください。サブフォルダも同様です。


ちなみに、My.Computer.FileSystem.MoveDirectory は
ボリューム間の移動に対応していますが、こちらも結局は
別ボリュームに新規フォルダを作成していたりします。
(UIOption を指定した場合は SHFileOperation API が使われますが)

このような事情から、My.Computer.FileSystem.MoveDirectory を使うと、
別ボリュームだと、フォルダのタイムスタンプが新規作成相当の日時になります。
同一ボリューム内だとタイムスタンプが更新されないのですけれどね。
引用返信 編集キー/
■82894 / inTopicNo.14)  Re[12]: 長いパス名のファイルをリネーム、削除する方法
□投稿者/ がもう (9回)-(2017/02/17(Fri) 18:47:14)
ありがとうございます。

納得しました

あと、ちなみにフォルダーを削除したい場合にはどうすれば良いですか?

DeleteFileだとファイルは削除できても
フォルダーは削除できないのですが・・・

引用返信 編集キー/
■82895 / inTopicNo.15)  Re[13]: 長いパス名のファイルをリネーム、削除する方法
□投稿者/ 魔界の仮面弁士 (1128回)-(2017/02/17(Fri) 19:06:11)
No82894 (がもう さん) に返信
> DeleteFileだとファイルは削除できても
> フォルダーは削除できないのですが・・・

RemoveDirectory API です。

System.IO.Directory.Delete メソッドや
VBA / VB.NET の RmDir もこれを利用しています。


フォルダーが空で無い場合は、再帰的に消していく必要があります。
ボリューム間の移動と同じですね。
引用返信 編集キー/
■82896 / inTopicNo.16)  Re[1]: 長いパス名のファイルをリネーム、削除する方法
□投稿者/ 魔界の仮面弁士 (1129回)-(2017/02/17(Fri) 19:20:58)
No82875 (がもう さん) に返信
> VB.NETで、.net frameworkを使うと
> 260文字以上のパス名を扱うことができません。

参考資料として:

http://qiita.com/jugemjugemu/items/4db1dfd3d2737d3979df
引用返信 編集キー/
■82897 / inTopicNo.17)  Re[2]: 長いパス名のファイルをリネーム、削除する方法
□投稿者/ がもう (10回)-(2017/02/17(Fri) 20:37:09)
ありがとうございます。

うまくいきました、

ところで、
http://qiita.com/jugemjugemu/items/4db1dfd3d2737d3979df

このページの説明に
Unicode版とANSI版があるとありますが、
ANSI版を使うメリットは何なのでしょうか?
shift jisには禁則文字がかなりありますし、
使えない文字が多いので
unicodeを使った方が良いと思うのですが

検索して調べていると
http://www.vbforums.com/showthread.php?302668-DeleteFile-and-RemoveDirectory-close-handle

わざわざANSI版を使っているケースも多く見られますが
これは一体なぜなのでしょうか?
 
引用返信 編集キー/
■82899 / inTopicNo.18)  Re[3]: 長いパス名のファイルをリネーム、削除する方法
□投稿者/ Azulean (778回)-(2017/02/17(Fri) 23:14:08)
No82897 (がもう さん) に返信
> このページの説明に
> Unicode版とANSI版があるとありますが、
> ANSI版を使うメリットは何なのでしょうか?

ありません。
昔の名残です。
引用返信 編集キー/
■82900 / inTopicNo.19)  Re[3]: 長いパス名のファイルをリネーム、削除する方法
□投稿者/ 魔界の仮面弁士 (1130回)-(2017/02/17(Fri) 23:45:57)
No82897 (がもう さん) に返信
> ANSI版を使うメリットは何なのでしょうか?

非 Unicode アプリにとっては未だに必要だと思いますが、
.NET 製アプリなら、あえて A 系を使う必要も無いかと。

たとえば Win9x だと、SendMessageA はありますが SendMessageW はありません。
そうした歴史的な事情による名残ですね。


現在の Windows は Unicode ベースで設計されていましたが、
Windows 3.1 や Windows 95 などはそうではありませんでした。

Win16 の頃から ini ファイル読み込みに使われる GetPrivateProfileString API を例に挙げると:

GetPrivateProfileStringA 関数は Win95、WinMe、WinNT4.0、Win2000、WinXP、Vista、Win10 など、
いずれのバージョンの Windows でも利用できます。まぁ、WinCE とかは駄目ですが。

GetPrivateProfileStringW 関数は、Win95、Win98、WinMe 等には存在しません。
利用できるのは NT 系 の OS、たとえば NT3.51、NT4.0、Win2000、WinXP などに
限られます。現行バージョンの Windows はすべて W 系に対応しています。


ただ、非 Unicode アプリケーションは、NT 系であっても A 系が使われたりします。
(Unicode なファイル名を扱えないアプリは今も沢山ありますよね)

また、VB4 や VB6 などは、Win9x 系でも WinNT 系でもどちらも動作するよう、
A 系 API を呼ぶのが一般的となっており、VB6 の既定の文字列マーシャリングも、
ANSI 版を既定の動作としています。
VB.NET の Declare ステートメントもそれを踏襲しています。


こうした歴史背景により、昔からある API については、
ネットや書籍等で照会されているサンプルの多くが、未だに A 系のままですね。
理解して使っている人はともかく、サンプルをコピーして利用するだけだと、
そこまで意識されることもないでしょうし。


> shift jisには禁則文字がかなりありますし、

禁則文字、という表現には語弊がある気もしますが、
9x 系が駆逐された今となっては、A 系に拘る必要も無いでしょうね。


たとえばルート記号「√」を出力する際に、
Shift_JIS 0x81E3 のルート記号と
Shift_JIS 0x8795 のルート記号を区別したい場合、
W 系だとどちらも U+221A にマッピングされてしまい、
区別できなくなってしまいます。

WritePrivateProfileStringA であれば、0x8795 を出力できますが、
WritePrivateProfileStringW だと、0x81E3 または 0x221A にしかなりません。

そうした特殊な状況でも無いかぎりは、W 系を使うべきかと思います。
引用返信 編集キー/
■82902 / inTopicNo.20)  Re[4]: 長いパス名のファイルをリネーム、削除する方法
 
□投稿者/ がもう (11回)-(2017/02/18(Sat) 15:42:40)
どうもありがとうございました。

 
解決済み
引用返信 編集キー/

このトピックをツリーで一括表示


トピック内ページ移動 / << 0 >>

このトピックに書きこむ