检查文件被哪个进程占用

经常遇到要删除文件或文件夹时, Windows系统弹出类似下面的窗口:

检查文件被哪个进程占用

但并不会告诉你是什么程序在占用, 所以微软专门开发了一个命令行程序Handle来检查, 以下是官方简介:
有没有想过哪个程序打开了特定的文件或目录? 现在可以了解了。Handle 是一个实用工具,用于显示有关系统中任何进程的打开句柄的信息。 可以使用它查看打开了文件的程序,或查看程序的所有句柄的对象类型和名称。
Handle 有很多参数, 平时一般都用不到, 所以我用AutoHotkey制作了一个简单的GUI, 并借助Handle来简单的查询并关闭占用进程
下面是完整代码, 90%的代码为AI编写

#NoTrayIcon
#SingleInstance Force
#NoEnv
SplitPath, A_ScriptFullPath,, scriptDir
if (scriptDir != A_WorkingDir)
    SetWorkingDir %ScriptDir%
; 初始化变量
global handlePath := ""
global lastOutput := ""
; 创建GUI
Gui, Font, s10, Segoe UI
Gui, Add, Text, w410 h20, 请输入被占用的文件或文件夹完整路径:(支持拖拽)
Gui, Add, Edit, y+5 w325 h25 vFilePath, 
Gui, Add, Button, x+5 w80 h25 vCheckHandle gCheckHandle, 检查占用
Gui, Add, Text, x10 y+5 w410 h20, 占用进程列表:
Gui, Add, ListView, y+5 w410 h100 vProcessList gSelectProcess, 进程名称|PID
Gui, Add, Button, y+5 w80 h25 vKillProcess gKillProcess, 结束进程
Gui, Add, Text, x+5 yp+4 w600 h20 vStatusText, 就绪
GUI +Resize +MinSize430x200
GuiDropFiles(GuiHwnd, FileArray, CtrlHwnd, X, Y) {
    FirstFile := FileArray[1] ; 只取第一个文件
    GuiControl,, FilePath, %FirstFile%
}
Gui, Show, w430 h225, 文件被进程占用检测工具
; 检查handle.exe/handle64.exe是否存在
CheckHandleExe()
return
GuiSize:
StatusTextw := 600
if (A_GuiWidth>690) {
    StatusTextw = %A_GuiWidth%-90
}
GuiControl, Move, FilePath, % "w" (A_GuiWidth-105)
GuiControl, Move, CheckHandle, % "x" (A_GuiWidth-90)
GuiControl, Move, ProcessList, % "h" (A_GuiHeight-130) " w" (A_GuiWidth-20)
GuiControl, Move, KillProcess, % "y" (A_GuiHeight-35)
GuiControl, Move, StatusText, % "w" (StatusTextw) " y" (A_GuiHeight-31)
return   

CheckHandleExe() {
    ; 检查系统目录
    if (FileExist(A_WinDir "\System32\handle64.exe")) {
        global handlePath := A_WinDir "\System32\handle64.exe"
        return true
    }
    if (FileExist(A_WinDir "\System32\handle.exe")) {
        global handlePath := A_WinDir "\System32\handle.exe"
        return true
    }
    ; 检查脚本目录
    if (FileExist(A_ScriptDir "\handle64.exe")) {
        global handlePath := A_ScriptDir "\handle64.exe"
        return true
    }
    if (FileExist(A_ScriptDir "\handle.exe")) {
        global handlePath := A_ScriptDir "\handle.exe"
        return true
    }
    ; 都不存在,提示下载
    GuiControl,, StatusText, 缺失handle,请下载后重试
    Gui, MyPopup:New
    Gui, MyPopup:Font, s12, 微软雅黑  
    Gui, MyPopup:Add, Text, w420, 本脚本依赖handle,请从微软官网下载: `nhttps://learn.microsoft.com/zh-cn/sysinternals/downloads/handle`n解压里面的handle.exe或handle64.exe到脚本所在目录即可`n按确定可尝试自动下载该文件的64位版本并继续脚本`n如果失败则会打开上面的网址,请手动下载并解压后重试
    Gui, MyPopup:Add, Button, w60 Default, 确定  ;按钮
    Gui, MyPopup:Show, Center, 警告  ;标题栏
    return false
    pause
}

; 检查文件占用
CheckHandle:
    Gui, Submit, NoHide
    filePath := Trim(FilePath)
    if (filePath = "") {
        GuiControl,, StatusText, 请输入文件路径
        return
    }
    if (!FileExist(filePath)) {
        GuiControl,, StatusText, 文件不存在: %filePath%
        return
    }
    if (handlePath = "") {
        if (!CheckHandleExe())
            return
    }
    GuiControl,, StatusText, 正在检查 请稍后...
    ; 执行handle命令
    RunWait, %ComSpec% /c ""%handlePath%" "%filePath%" > "%A_Temp%\handle_output.tmp"",,Hide
    FileRead, output, %A_Temp%\handle_output.tmp
    FileDelete, %A_Temp%\handle_output.tmp
    global lastOutput := output
    ; 解析输出
    LV_Delete()
    ; 提取进程信息
    count := 0
    Loop, Parse, output, `n, `r
    {
        line := Trim(A_LoopField)
        if (RegExMatch(line, "i)^(\S+)\s+pid:\s*(\d+)", match)) {
            LV_Add("", match1, match2)
            count++
        }
    }
    if (count = 0) {
        GuiControl,, StatusText, 未找到占用该文件的进程
    } else {
        GuiControl,, StatusText, 找到 %count% 个占用进程
    }
Loop % LV_GetCount("Col")
    LV_ModifyCol(A_Index, "AutoHdr")
return

; 选择进程
SelectProcess:
    if (A_GuiEvent = "DoubleClick") {
        row := A_EventInfo  ; 获取双击的行号
        LV_GetText(pid, row, 2)  ; 获取该行第2列(PID)的值
        KillProcessByPID(pid)
    }
return

; 结束进程
KillProcess:
    ; 获取当前选中的行号
    row := LV_GetNext()
    if (row = 0) {
        GuiControl,, StatusText, 请先选择要结束的进程
        return
    }
    ; 获取选中行的PID
    LV_GetText(pid, row, 2)
    KillProcessByPID(pid)
return

; 根据PID结束进程
KillProcessByPID(pid) {
    message := "确定要结束PID为 " pid " 的进程吗?`n这可能会导致数据丢失或程序异常。"
    MsgBox, 36, 确认, %message%
    ; 获取用户选择
    IfMsgBox, Yes
    {
        GuiControl,, StatusText, 正在结束进程 %pid%...
        RunWait, %ComSpec% /c "taskkill /F /PID %pid% > nul 2>&1",, Hide
        if (ErrorLevel = 1) {
        GuiControl,, StatusText, 结束进程失败
        } else GuiControl,, StatusText, 成功结束进程
    }
}

MyPopupButton确定:
Gui, MyPopup:Destroy
SetTimer, UpdateStatus_Downloading, 100  ; 100毫秒后执行更新
UrlDownloadToFile https://download.sysinternals.com/files/Handle.zip, Handle.zip
SetTimer, UpdateStatus_Extracting, 100
FileCopyDir, Handle.zip,Handle ,1    ;要求AutoHotkey 版本v1.1.34+
FileMove, %A_ScriptDir%\Handle\handle64.exe, %A_ScriptDir%\handle64.exe, 1
FileRemoveDir,%A_ScriptDir%\Handle,1
FileDelete,%A_ScriptDir%\Handle.zip
; 检查是否成功
if (FileExist("handle64.exe") || FileExist("handle.exe")) {
    SetTimer, UpdateStatus_Success, 100
    CheckHandleExe()  ; 重新检查handle路径
    } else {
    SetTimer, UpdateStatus_Failed, 100
  }
pause off
return

UpdateStatus_Downloading:
SetTimer, UpdateStatus_Downloading, Off
GuiControl,, StatusText, 正在下载handle 请稍候...
return

UpdateStatus_Extracting:
SetTimer, UpdateStatus_Extracting, Off
GuiControl,, StatusText, 正在解压handle文件...
return

UpdateStatus_Success:
SetTimer, UpdateStatus_Success, Off
GuiControl,, StatusText, 已成功下载handle 可以继续了
return

UpdateStatus_Failed:
SetTimer, UpdateStatus_Failed, Off
GuiControl,, StatusText, 下载或解压handle失败,请在打开的页面手动下载并解压
run https://learn.microsoft.com/zh-cn/sysinternals/downloads/handle
return

GuiClose:
ExitApp

没有添加版本检查代码, 请确保AutoHotkey版本为v1.1.34+, 该GUI会自动尝试下载Handle, 首次使用会弹出Handle的许可协议(见下图), 请点击Agree继续!

检查文件被哪个进程占用

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

给TA捐赠
共{{data.count}}人
人已捐赠
应用

查询最近的开关机记录

2025-7-17 19:17:17

其他

AHK右键菜单中文化带图标

2022-1-13 9:50:21

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索