<# .SYNOPSIS Specifies a directory and lists all processes that have open file handles within that directory and its subdirectories. .DESCRIPTION This script utilizes the handle.exe tool from Microsoft's Sysinternals suite to find open file handles. It filters the results to show only file handles within the specified target directory path. The script requires handle.exe to be downloaded and its path correctly configured in the $handleExePath variable. For best results, run this script with Administrator privileges. .PARAMETER TargetDirectory The path to the directory to scan for open file handles. Defaults to the Windows directory (C:\Windows). .EXAMPLE .\Find-FileHandles.ps1 (Scans the default C:\Windows directory) .EXAMPLE .\Find-FileHandles.ps1 -TargetDirectory "D:\Project\video-av1" (Scans the D:\Project\video-av1 directory) #> param ( [string]$TargetDirectory = $env:SystemRoot # Defaults to C:\Windows ) # 콘솔 인코딩 설정 (한글 깨짐 방지) [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 $OutputEncoding = [System.Text.Encoding]::UTF8 # --- CONFIGURATION --- # handle.exe 경로 자동 탐지 $possiblePaths = @( "C:\Sysinternals\handle.exe", "$env:ProgramFiles\Sysinternals\handle.exe", "$env:USERPROFILE\Downloads\handle.exe", "$env:TEMP\handle.exe", "handle.exe" # PATH에 있는 경우 ) $handleExePath = $null foreach ($path in $possiblePaths) { if (Test-Path $path) { $handleExePath = $path break } } # PATH에서 handle.exe 찾기 if (-not $handleExePath) { try { $handleExePath = (Get-Command "handle.exe" -ErrorAction Stop).Source } catch { $handleExePath = $null } } # --------------------- # handle.exe 파일 존재 여부 확인 if (-not $handleExePath -or -not (Test-Path $handleExePath)) { Write-Host "[ERROR] handle.exe not found in any of the following locations:" -ForegroundColor Red $possiblePaths | ForEach-Object { Write-Host " - $_" -ForegroundColor Yellow } Write-Host "" Write-Host "Please download handle.exe from:" -ForegroundColor Cyan Write-Host "https://learn.microsoft.com/en-us/sysinternals/downloads/handle" -ForegroundColor Cyan Write-Host "" Write-Host "And place it in one of the above locations, or add it to your PATH." -ForegroundColor Green return } Write-Host "Scanning for open file handles in '$TargetDirectory'..." Write-Host "This may take a moment..." # Execute handle.exe to get all open file handle information try { Write-Host "Executing handle.exe..." -ForegroundColor Gray $handleOutput = & $handleExePath -nobanner -a -u "$TargetDirectory" 2>$null | Out-String } catch { Write-Host "[ERROR] Failed to execute handle.exe: $($_.Exception.Message)" -ForegroundColor Red return } # 결과를 저장할 배열 초기화 $foundProcesses = @() # Analyze handle.exe output line by line if ($handleOutput) { # Use regex to extract process name, PID, and file path # handle.exe output format variations: # "ProcessName.exe pid: 1234 type: File C:\path\to\file" # "ProcessName.exe pid: 1234 user: DOMAIN\User type: File C:\path\to\file" $regex = "^(.+?)\s+pid:\s*(\d+)\s+.*?\s+type:\s+File\s+(.+?)\s*$" $handleOutput.Split([Environment]::NewLine) | ForEach-Object { if ($_ -match $regex) { $processName = $matches[1].Trim() $processId = [int]$matches[2] $filePath = $matches[3].Trim() # 지정된 디렉토리 경로에 포함되는 결과만 필터링 if ($filePath.StartsWith($TargetDirectory, [System.StringComparison]::OrdinalIgnoreCase)) { # PSCustomObject를 생성 $customObject = [PSCustomObject]@{ ProcessName = $processName PID = $processId FilePath = $filePath } $foundProcesses += $customObject } } } } # 결과 출력 if ($foundProcesses.Count -gt 0) { Write-Host "`n[+] Found $($foundProcesses.Count) open file handles in '$TargetDirectory':" $foundProcesses | Sort-Object -Property ProcessName, FilePath | Format-Table -AutoSize } else { Write-Host "`n[-] No open file handles were found for the specified directory." }