如何用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
相关产品推荐
相关产品推荐

