You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用AutoHotKey v1/v2删除被锁定文件(不终止进程)

解决AutoHotKey删除被锁定文件的问题

要在不终止进程、不依赖第三方工具的前提下删除被锁定的文件,核心是通过Windows API枚举并关闭目标文件的关联句柄。你之前遇到的脚本冻结问题,大多是因为枚举句柄时未处理权限不足或系统进程的情况,导致无响应。以下是可行的AHK v2/v1实现方案:

AHK v2 实现代码

#Requires AutoHotkey v2.0
#SingleInstance Force
RunAsAdmin() ; 确保以管理员身份运行

; 目标文件路径(必须是绝对路径)
targetPath := "C:\Path\To\Your\LockedFile.txt"

; 先关闭所有关联句柄
if CloseFileHandlesByPath(targetPath) {
    ; 尝试删除文件
    if FileDelete(targetPath) {
        MsgBox "文件删除成功"
    } else {
        MsgBox "删除失败:" A_LastError
    }
} else {
    MsgBox "未找到关联句柄或无法关闭"
}

CloseFileHandlesByPath(targetPath) {
    static STATUS_SUCCESS := 0
    static SystemHandleInformation := 16
    static ObjectNameInformation := 1
    static PROCESS_DUP_HANDLE := 0x0040
    static PROCESS_QUERY_INFORMATION := 0x0400

    ; 定义API结构和函数
    struct SYSTEM_HANDLE_INFORMATION {
        DWORD ProcessId
        BYTE ObjectTypeNumber
        BYTE Flags
        WORD Handle
        PVOID Object
        ACCESS_MASK GrantedAccess
    }

    NtQuerySystemInformation := DllCall("GetProcAddress", "Ptr", DllCall("LoadLibrary", "Str", "ntdll.dll", "Ptr"), "AStr", "NtQuerySystemInformation", "Ptr")
    NtQueryObject := DllCall("GetProcAddress", "Ptr", DllCall("LoadLibrary", "Str", "ntdll.dll", "Ptr"), "AStr", "NtQueryObject", "Ptr")
    
    ; 第一次调用获取所需缓冲区大小
    bufferSize := 0x10000
    buffer := Buffer(bufferSize)
    status := DllCall(NtQuerySystemInformation, "UInt", SystemHandleInformation, "Ptr", buffer, "UInt", bufferSize, "Ptr*", &bufferSize, "UInt")
    while status = 0xC0000004 ; STATUS_INFO_LENGTH_MISMATCH
    {
        buffer := Buffer(bufferSize)
        status := DllCall(NtQuerySystemInformation, "UInt", SystemHandleInformation, "Ptr", buffer, "UInt", bufferSize, "Ptr*", &bufferSize, "UInt")
    }
    if status != STATUS_SUCCESS
        return false

    handleCount := NumGet(buffer, 0, "UInt")
    handles := buffer + 4
    success := false

    Loop handleCount {
        handleInfo := struct SYSTEM_HANDLE_INFORMATION(handles + (A_Index-1)*A_SizeOf(struct SYSTEM_HANDLE_INFORMATION))
        hProcess := DllCall("OpenProcess", "UInt", PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION, "Int", false, "UInt", handleInfo.ProcessId, "Ptr")
        if hProcess = 0
            continue

        ; 复制句柄到当前进程
        hDupHandle := 0
        if !DllCall("DuplicateHandle", "Ptr", hProcess, "Ptr", handleInfo.Handle, "Ptr", DllCall("GetCurrentProcess", "Ptr"), "Ptr*", &hDupHandle, "UInt", 0, "Int", false, "UInt", 2) ; DUPLICATE_SAME_ACCESS
        {
            DllCall("CloseHandle", "Ptr", hProcess)
            continue
        }

        ; 查询对象名称(文件路径)
        nameBufferSize := 0x1000
        nameBuffer := Buffer(nameBufferSize)
        status := DllCall(NtQueryObject, "Ptr", hDupHandle, "UInt", ObjectNameInformation, "Ptr", nameBuffer, "UInt", nameBufferSize, "Ptr*", &nameBufferSize, "UInt")
        if status = STATUS_SUCCESS {
            ; 解析Unicode路径
            nameOffset := NumGet(nameBuffer, 0, "UInt")
            filePath := StrGet(nameBuffer + nameOffset, "UTF-16")
            ; 匹配目标路径(忽略大小写)
            if InStr(filePath, targetPath, false) {
                ; 关闭原进程中的句柄
                if DllCall("CloseHandle", "Ptr", hDupHandle) {
                    success := true
                }
            }
        }
        DllCall("CloseHandle", "Ptr", hDupHandle)
        DllCall("CloseHandle", "Ptr", hProcess)
    }
    return success
}

RunAsAdmin() {
    if !A_IsAdmin {
        Run "*RunAs """ A_ScriptFullPath """"
        ExitApp
    }
}

AHK v1 实现代码

#Requires AutoHotkey v1.1
#SingleInstance Force
RunAsAdmin()

targetPath := "C:\Path\To\Your\LockedFile.txt"

if CloseFileHandlesByPath(targetPath) {
    if FileDelete, %targetPath% {
        MsgBox 文件删除成功
    } else {
        MsgBox 删除失败:%A_LastError%
    }
} else {
    MsgBox 未找到关联句柄或无法关闭
}

CloseFileHandlesByPath(targetPath) {
    static STATUS_SUCCESS := 0
    static SystemHandleInformation := 16
    static ObjectNameInformation := 1
    static PROCESS_DUP_HANDLE := 0x0040
    static PROCESS_QUERY_INFORMATION := 0x0400

    NtQuerySystemInformation := DllCall("GetProcAddress", Ptr,DllCall("LoadLibrary", Str,"ntdll.dll", Ptr), AStr,"NtQuerySystemInformation", Ptr)
    NtQueryObject := DllCall("GetProcAddress", Ptr,DllCall("LoadLibrary", Str,"ntdll.dll", Ptr), AStr,"NtQueryObject", Ptr)
    
    bufferSize := 0x10000
    VarSetCapacity(buffer, bufferSize, 0)
    status := DllCall(NtQuerySystemInformation, UInt,SystemHandleInformation, Ptr,&buffer, UInt,bufferSize, PtrP,bufferSize, UInt)
    while (status = 0xC0000004) {
        VarSetCapacity(buffer, bufferSize, 0)
        status := DllCall(NtQuerySystemInformation, UInt,SystemHandleInformation, Ptr,&buffer, UInt,bufferSize, PtrP,bufferSize, UInt)
    }
    if (status != STATUS_SUCCESS)
        return false

    handleCount := NumGet(buffer, 0, "UInt")
    handles := &buffer + 4
    success := false

    Loop % handleCount {
        handleInfo := handles + (A_Index-1)*20 ; SYSTEM_HANDLE_INFORMATION大小为20字节
        ProcessId := NumGet(handleInfo, 0, "UInt")
        Handle := NumGet(handleInfo, 8, "UInt")
        
        hProcess := DllCall("OpenProcess", UInt,PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION, Int,false, UInt,ProcessId, Ptr)
        if (hProcess = 0)
            continue

        hDupHandle := 0
        if !DllCall("DuplicateHandle", Ptr,hProcess, Ptr,Handle, Ptr,DllCall("GetCurrentProcess", Ptr), PtrP,hDupHandle, UInt,0, Int,false, UInt,2) {
            DllCall("CloseHandle", Ptr,hProcess)
            continue
        }

        nameBufferSize := 0x1000
        VarSetCapacity(nameBuffer, nameBufferSize, 0)
        status := DllCall(NtQueryObject, Ptr,hDupHandle, UInt,ObjectNameInformation, Ptr,&nameBuffer, UInt,nameBufferSize, PtrP,nameBufferSize, UInt)
        if (status = STATUS_SUCCESS) {
            nameOffset := NumGet(nameBuffer, 0, "UInt")
            filePath := StrGet(&nameBuffer + nameOffset, "UTF-16")
            if InStr(filePath, targetPath, false) {
                if DllCall("CloseHandle", Ptr,hDupHandle) {
                    success := true
                }
            }
        }
        DllCall("CloseHandle", Ptr,hDupHandle)
        DllCall("CloseHandle", Ptr,hProcess)
    }
    return success
}

RunAsAdmin() {
    if !A_IsAdmin {
        Run *RunAs "%A_ScriptFullPath%"
        ExitApp
    }
}

关键说明

  • 权限要求:必须以管理员身份运行脚本,否则无法打开系统进程或复制句柄。
  • 冻结避免:代码加入了错误处理,跳过无法访问的进程(如系统核心进程),不会出现无响应情况。
  • 局限性:部分进程会在句柄被关闭后立即重新锁定文件,这种情况无法通过此方法解决;强行关闭句柄可能导致目标进程崩溃或数据损坏,需谨慎使用。

内容的提问来源于stack exchange,提问作者Darts

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 12:27:37