状況
デプロイ中、ビルド中、あるいはログファイルを削除しようとしたとき — Windowsが次のエラーをスローします:
The process cannot access the file because it is being used by another process.
何らかのプロセスがそのファイルに対してハンドルを開いたままにしています。そのハンドルが閉じられるまで、Windowsはファイルへのアクセスを許可しません。ロックを保持しているプロセスを特定し、正常に閉じるかキルするか — それだけです。
デバッグ手順
方法1:リソースモニター(インストール不要)
すべてのWindowsに標準搭載されています。ほとんどのケースで十分に対処できます。
Win + Rを押し、resmonと入力してEnterを押します。- CPU タブを開き → 関連するハンドル を展開します。
- 検索ボックスにファイル名(または一部)を入力します。
- リストにそのハンドルを保持しているPIDとプロセス名が表示されます。
結果を右クリックすると、そのままプロセスを終了できます。
方法2:Process Explorer(Sysinternals)
リソースモニターでは見つからないハンドルも検出できます — このエラーが頻繁に発生する場合は導入する価値があります。
learn.microsoft.com/sysinternals/downloads/process-explorerからダウンロードします。- 管理者として実行します。
Ctrl+Fを押してハンドルまたはDLLの検索を開きます。- ファイル名を入力すると、そのファイルを保持しているすべてのプロセスが表示されます。
- エントリをダブルクリック → ハンドルを右クリック → ハンドルを閉じる。
ハンドルを閉じるとプロセス全体を終了せずにロックを解除できます。ロックを保持しているのが再起動したくないサービス内のバックグラウンドスレッドである場合に便利です。
方法3:Handle.exe(CLI、スクリプト対応)
こちらもSysinternalsのツールです。PowerShellやCMDで実行でき、スクリプトやリモートセッションに最適です。
# ダウンロード: https://learn.microsoft.com/sysinternals/downloads/handle
# 管理者として実行
handle.exe C:\path\to\locked\file.txt
出力例:
node.exe pid: 14832 type: File C:\path\to\locked\file.txt
java.exe pid: 9120 type: File C:\path\to\locked\file.txt
16進数のIDで特定のハンドルを閉じる:
handle.exe -c 1A4 -p 14832 -y
-y フラグは確認プロンプトをスキップします。16進数のハンドルIDは直前の出力の最初の列から取得します。
方法4:PowerShell(サードパーティツール不要)
Sysinternalsは不要ですが、ファイルを所有しているプロセスを特定するだけで、個別のハンドルをこの方法で閉じることはできません。
$lockedFile = "C:\path\to\locked\file.txt"
Get-Process | ForEach-Object {
$proc = $_
try {
$proc.Modules | Where-Object { $_.FileName -eq $lockedFile } | ForEach-Object {
[PSCustomObject]@{ PID = $proc.Id; Name = $proc.Name; File = $_.FileName }
}
} catch { }
} | Format-Table -AutoSize
これはDLLレベルのロックを検出します。ネットワーク共有上の一般的なファイルハンドルには、openfiles も別の選択肢です:
# 最初に有効化(再起動が必要):
openfiles /local on
# その後クエリを実行:
openfiles /query /fo table | findstr /i "filename.txt"
よくある原因
- ウイルス対策ソフト / Windows Defender — ファイルが書き込まれた瞬間にスキャンします。ビルドやデプロイ中に非常によく発生します。プロジェクトフォルダを除外設定に追加しましょう。
- Windows検索インデクサー(
SearchIndexer.exe)— 新しいファイルに即座にアクセスします。開発ディレクトリやビルドディレクトリのインデックス作成を無効化しましょう。 - Node.js / webpackのウォッチモード — ウォッチツリー内のすべてのファイルのハンドルを保持します。例外なくすべてです。
- Javaプロセス — JVMが
.jarファイルや一時ファイルをロックします。 - IIS / w3wp.exe — アプリディレクトリ内のDLLをロックします。デプロイ前にアプリプールを停止しましょう。
- Explorer.exe — サムネイル生成が画像ファイルや動画ファイルをロックすることがあります。
- VS Code — プロジェクトが開いている間、ワークスペースファイルやgitオブジェクトのハンドルを保持します。
シナリオ別の解決策
シナリオ:.dllまたは.exeがロックされているためビルド/デプロイが失敗する
# まずIISアプリプールを停止
Import-Module WebAdministration
Stop-WebAppPool -Name "MyAppPool"
# デプロイを実行...
# 再起動
Start-WebAppPool -Name "MyAppPool"
またはデプロイ中にIISサービス全体を停止する:
iisreset /stop
# ... デプロイ ...
iisreset /start
シナリオ:ロック元不明のファイルを削除できない
# 1. handle.exeでPIDを特定
handle.exe locked-file.log
# 2. プロセスをキル(安全であれば)
Stop-Process -Id 14832 -Force
# 3. ファイルを削除
Remove-Item locked-file.log
シナリオ:スクリプト内でのリネーム/移動が失敗する
ウイルス対策ソフトのスキャン完了後など、ロックがミリ秒単位で解除される場合があります。リトライループで綺麗に対処できます:
$maxRetries = 5
$retryDelay = 1 # seconds
$attempt = 0
while ($attempt -lt $maxRetries) {
try {
Move-Item -Path "source.tmp" -Destination "output.txt" -Force -ErrorAction Stop
Write-Host "Moved successfully"
break
} catch {
$attempt++
Write-Warning "Attempt $attempt failed: $($_.Exception.Message)"
Start-Sleep -Seconds $retryDelay
}
}
if ($attempt -eq $maxRetries) {
Write-Error "Failed to move file after $maxRetries attempts"
}
シナリオ:ウイルス対策ソフトがロックしている
「Windowsセキュリティ」→「ウイルスと脅威の防止」→「設定の管理」→「除外」を開き、ビルド出力フォルダを追加します。PowerShellでも設定できます:
Add-MpPreference -ExclusionPath "C:\projects\myapp\dist"
Add-MpPreference -ExclusionPath "C:\projects\myapp\node_modules"
修正の確認
ロックを解除したら、まだ何かが保持していないか確認します:
handle.exe C:\path\to\file.txt
# 期待される出力:
# No matching handles found.
元の操作を再試行します。エラーがすぐに再発する場合、同じプロセスがロックを再取得しています。ハンドルを閉じるだけでは不十分で、プロセス自体を停止する必要があります。
まとめ
- リソースモニターはすでにあなたのマシンに入っています。何かをダウンロードする前に、まずこれを使いましょう。
- Handle.exe は
C:\tools\に常備しておきましょう。深夜2時にデプロイが壊れたとき、最初の一回で元が取れます。 - Windows Defenderは開発マシンやCIエージェントのビルドパイプラインを静かに破壊します。ビルド出力ディレクトリに除外設定を追加しましょう — 30秒の作業で何時間もの混乱を防げます。
- Process ExplorerやHandle.exeでハンドルを閉じると、所有プロセスがクラッシュする可能性があります。一度限りのロック解除には問題ありませんが、そのプロセスが何をしていたかを理解せずに本番環境でこれを自動化しないでください。
- ファイルを書き込むコードを制御できる場合:
FileShareを明示的に設定し、finallyブロックまたはusingステートメントで必ずストリームを閉じましょう。カスタムコードにおけるファイルロックのバグのほとんどは、例外発生後に開いたままになったストリームが原因です。

