SetFileInformationByHandle(FileRenameInfo)でRootDirectoryと相対パスを指定するとERROR_INVALID_PARAMETER (87)になる

nhpokon 60 評価のポイント
2026-09-25T15:03:20.8866667+00:00

Windowsのファイル移動処理について質問です。
C# / .NET 10 のデスクトップアプリで、既に開いて検証済みの移動先ディレクトリハンドルを基準に、ファイルを相対名で同一ボリューム内移動したいと考えています。
MicrosoftのFILE_RENAME_INFOドキュメントでは、FileNameが相対名の場合、RootDirectoryに新しい名前を解決するディレクトリハンドルを指定できると記載されています。
しかし、以下の条件では SetFileInformationByHandle が ERROR_INVALID_PARAMETER (87) を返します。

環境:

  • Windows 10.0.26200.0 x64
  • NTFS
  • Windows SDK 10.0.26100.0
  • .NET 10 / C# P/Invoke

確認済み:

  • FileRenameInfo = 3
  • FILE_RENAME_INFO のx64レイアウト確認済み
  • RootDirectory = NULL + 絶対パス → 成功
  • RootDirectory = 有効な移動先ディレクトリハンドル
    • 相対FileName → ERROR_INVALID_PARAMETER (87)
  • FileRenameInfoExでも同様
  • 同じsource handle / destination directory handle / 相対名をNtSetInformationFile(FileRenameInformation)へ渡すと成功

移動先ディレクトリハンドルは、FILE_READ_ATTRIBUTES | FILE_TRAVERSE で開いています。

質問です。

  1. SetFileInformationByHandle(FileRenameInfo)で、非NULLのRootDirectoryと相対FileNameを使う方法は、現在のWindowsのuser-mode Win32アプリで正式にサポートされていますか。
  2. サポートされている場合、ERROR_INVALID_PARAMETER (87)になる可能性がある追加条件・必要なアクセス権・バッファ条件・OS制限はありますか。
  3. この方法が利用できない場合、既に開いているdestination directory handleを基準に、絶対パスを再解決せずatomic rename/moveできるdocumented Win32 APIはありますか。

補足:
絶対パス方式は動作しますが、検証後にjunction等へ差し替えられた場合のraceを避けるため、検証済みdirectory handleを基準に操作したいという要件があります。
Microsoft Store向けのfull-trust MSIXを予定していますが、まずはSetFileInformationByHandleの正しい仕様・利用方法を確認したいです。


<モデレーター注>
この質問スレッドは、スパムフィルターの誤判定により削除されていましたがスレッドを復元させて頂きました。

開発者テクノロジ | C#
開発者テクノロジ | C#

C 言語ファミリをルーツとし、コンポーネント指向プログラミングのサポートを含む、オブジェクト指向およびタイプセーフのプログラミング言語。


質問作成者が受け入れた回答
gekka 14,551 評価のポイント MVP ボランティア モデレーター
2026-09-27T09:15:24.32+00:00

テスト用に作ったコード

解析結果

SetFileInformationByHandleに以下のFILE_RENAME_INFOを渡したとする

#SetFileInformationByHandle : FILE_RENAME_INFO
01 00 00 00 00 00 00 00 44 00 00 00 00 00 00 00
20 00 00 00 2E 00 5C 00 54 00 65 00 73 00 74 00
31 00 39 00 32 00 35 00 32 00 34 00 2E 00 74 00
78 00 74 00

これはRootDirectoryが0x44で、 FileNameは文字数が0x20の./Test192524.txtである。(ReplaceIfExistsはtrue)

SetFileInformationByHandleはいくつか処理したのちにNtSetInformationFileを呼び出しているが、NtSetInformationFileへ渡されるFILE_RENAME_INFORMATIONは以下のようになってしまっている。

これはRootDirectoryが0x44で、 FileNameは文字数が0x56の\??\D:Test\bin\Debug\net481\Test192524.txtである。

#NtSetInformationFile : FILE_RENAME_INFORMATION
01 00 00 00 0D F0 AD BA 44 00 00 00 00 00 00 00
56 00 00 00 5C 00 3F 00 3F 00 5C 00 44 00 3A 00
5C 00 54 00 65 00 73 00 74 00 5C 00 62 00 69 00
6E 00 5C 00 44 00 65 00 62 00 75 00 67 00 5C 00
6E 00 65 00 74 00 34 00 38 00 31 00 5C 00 54 00
65 00 73 00 74 00 31 00 39 00 32 00 35 00 32 00
34 00 2E 00 74 00 78 00 74 00

SetFileInformationByHandleの逆アセンブラを追っていくと、受けとった引数をFILE_RENAME_INFORMATIONに変換してNtSetInformationFileを実行している。

引数チェックが通ったら元のファイル名がドライブレターを含んでいなければRtlDosPathNameToNtPathName_U_WithStatus関数でNTパスに変換されている。
この変換により相対パスが絶対パスに変換されている。
(RtlDosPathNameToNtPathName_U_WithStatusではカレント基準で絶対パスに変換されているかもしれない)

注目するのはFILE_RENAME_INFOのRootDirectoryがそのままコピーされてしまっているということと、RtlDosPathNameToNtPathName_U_WithStatusが1文字の判定のみで変換が行われていること。

なお、NtSetInformationFileでブレークさせて、FILE_RENAME_INFORMATIONのFileNameを相対パス文字列に書きかえると想定したとおりにRootDirectory以下に移動されました。

結論

SetFileInformationByHandleの現在の実装はFILE_RENAME_INFORMATIONに対して

  • RootDirectoryにNULLを設定する方法がない
  • FileNameに絶対パスを渡す場合RootDirectoryはNULLにしなければならない決まりとなっているのに RootDirectoryが設定されている。
  • フルパスへの変換の判定が手抜きであること。

によりこの問題が発生しているのである。

多分バグ

現状では、RootDirectoryを0にすることを忘れているのか、間違ってフルパスに変換してしまっているのかのどちらかは判断がつきません。

回避方法

SetFileInformationByHandleでRootDirectoryを使わない

  • 絶対パスを指定する。
  • SetFileInformationByHandleを使わずにNtSetInformationFileを直接使う

Use


FullTrustでNtSetInformationFileつかえるかは、コミュニティーサポートではどうなるかは公式情報ではないのであてにはなりません。

最小限の機能の非公開アプリを作ってNtSetInformationFile使ってますという説明を書いて申請して試すか、有料チケット使って公式に質問するしかないと思う。

# アプリローカルではなくグローバルにディスクアクセスするようなアプリは判定が厳しくなるだろうし

この回答は役に立ちましたか?

2 人がこの回答が役に立ったと思いました。

1 件の追加の回答

並べ替え方法: 最も役に立つ
  1. motosan 3,570 評価のポイント
    2026-09-26T08:04:11.5166667+00:00

    Stack Overflow に下記の投稿があります。

    c++ - Moving a file using SetFileInformationByHandle - Stack Overflow

    この中に下記のコメントがあります。

    Gerardo Sánchez さん

    I cannt find any reason why RootDirectory must be NULL, but i guess: it works in kernel mode, or because FILE_RENAME_INFO is shared for SetFileInformationByHandle, NtSetInformationFile & ZwSetInformationFile, maybe it has some sense use RootDirectory != NULL in other functions

    [翻訳] RootDirectoryがNULLでなければならない理由は見つかりませんが、おそらくカーネルモードで動作するか、SetFileInformationByHandle、NtSetInformationFile、ZwSetInformationFileで共有されているFILE_RENAME_INFO、他の関数でRootDirectory != NULLを使う意味があるのかもしれません

    dyp さん

    Wow, it says why it needs in the description you've linked. I did not see that because I was navigating from , where you end up at the FILE_RENAME_INFO structure instead of the FILE_RENAME_INFORMATION structure you've linked to. Many thanks!RootDirectory == NULLSetFileInformationByHandle

    [翻訳] わあ、リンクした説明文になぜ必要なのかが書かれていますね。私は、リンクしたFILE_RENAME_INFORMATION構造体ではなく、FILE_RENAME_INFO構造体にたどり着くので、その点は見られませんでした。どうもありがとうございます!RootDirectory == NULLSetFileInformationByHandle

    • FILE_RENAME_INFORMATION構造体のドキュメント (ほとんどFILE_RENAME_INFOと同じ構造)

    FILE_RENAME_INFORMATION (ntifs.h) - Windows drivers | Microsoft Learn

    この中に下記の記述があります、

    ■ RootDirectory : IopOpenLinkOrRenameTarget がターゲット ディレクトリを開くために使用するハンドル。

    正確にはわかりませんが、dtiver の IopOpenLinkOrRenameTarget が使うためのディレクトリのようです。

    • FILE_RENAME_INFO構造体 (winbase.h)のドキュメント

    FILE_RENAME_INFO (winbase.h) - Win32 apps | Microsoft Learn

    ここには下記の記述があります、

    FileName[1] : ファイルへの新しいパスを含む NUL で終わるワイド文字文字列。 値は次のいずれかを指定できます。

    • 絶対パス (ドライブ、ディレクトリ、ファイル名)。
    • プロセスの現在のディレクトリに対する相対パス。
    • で始まる NTFS ファイル ストリームの新しい名前。

    結局、FILE_RENAME_INFO の RootDirectoryになぜ NULL以外指定できないかの回答ではないので申し訳ありません。

    どうしても RootDirectory にハンドルを指定したいのであれば上記の Stack Overflow の投稿などを参考にして調べてください。

    よろしくお願いします。

    この回答は役に立ちましたか?


お客様の回答

質問作成者は回答に "承認済み"、モデレーターは "推奨" とマークできます。これにより、ユーザーは作成者の問題が回答によって解決したことを把握できます。