Find the process that is locking a file/folder

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
Bruttosozialprodukt
Posts: 463
Joined: 24 Jan 2014, 22:28

Find the process that is locking a file/folder

07 Apr 2016, 10:12

I'd like to programmatically find out which process is responsible for locking a certain file/folder.
Much like the Resource monitor does under CPU/Associated Handles.
Image


Has anyone done this before? Any idea what would be the quickest way to find it?
Do you just "scan" every process for all their handles, resolve them and check if they contain the filename or how would this be done?
And if so, how would I get all the handles of a process?
Bruttosozialprodukt
Posts: 463
Joined: 24 Jan 2014, 22:28

Re: Find the process that is locking a file/folder

07 Apr 2016, 15:42

I found an interesting piece of code on the Internet that might do what I want. It's written in C++ though and I don't understand the crucial parts.

Code: Select all

//SOURCE: http://forums.codeguru.com/showthread.php?176997.html
#include "ntdll.h"
#include <stdlib.h>
#include <stdio.h>
#include "ntddk.h"

#define DUPLICATE_SAME_ATTRIBUTES   0x00000004

#pragma comment(lib,"ntdll.lib")

BOOL EnablePrivilege(PCSTR name)
{
    TOKEN_PRIVILEGES priv = {1, {0, 0, SE_PRIVILEGE_ENABLED}};
    LookupPrivilegeValue(0, name, &priv.Privileges[0].Luid);

    HANDLE hToken;
    OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken);

    AdjustTokenPrivileges(hToken, FALSE, &priv, sizeof priv, 0, 0);
    BOOL rv = GetLastError() == ERROR_SUCCESS;

    CloseHandle(hToken);
    return rv;
}

int main(int argc, char *argv[])
{
    if (argc == 1) return 0;

    ULONG pid = strtoul(argv[1], 0, 0);

    EnablePrivilege(SE_DEBUG_NAME);

    HANDLE hProcess = OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid);

    ULONG n = 0x1000;
    PULONG p = new ULONG[n];

    while (NT::ZwQuerySystemInformation(NT::SystemHandleInformation, p, n * sizeof *p, 0)
           == STATUS_INFO_LENGTH_MISMATCH)

        delete [] p, p = new ULONG[n *= 2];

    NT::PSYSTEM_HANDLE_INFORMATION h = NT::PSYSTEM_HANDLE_INFORMATION(p + 1);

    for (ULONG i = 0; i < *p; i++) {

        if (h[i].ProcessId == pid) {
            HANDLE hObject;

            if (NT::ZwDuplicateObject(hProcess, HANDLE(h[i].Handle), NtCurrentProcess(), &hObject,
                                      0, 0, DUPLICATE_SAME_ATTRIBUTES)
                != STATUS_SUCCESS) continue;

            NT::OBJECT_BASIC_INFORMATION obi;

            NT::ZwQueryObject(hObject, NT::ObjectBasicInformation, &obi, sizeof obi, &n);

            printf("%p %04hx %6lx %2x %3lx %3ld %4ld ", 
                   h[i].Object, h[i].Handle, h[i].GrantedAccess,
                   int(h[i].Flags), obi.Attributes,
                   obi.HandleCount - 1, obi.PointerCount - 2);

            n = obi.TypeInformationLength + 2;

            NT::POBJECT_TYPE_INFORMATION oti = NT::POBJECT_TYPE_INFORMATION(new CHAR[n]);

            NT::ZwQueryObject(hObject, NT::ObjectTypeInformation, oti, n, &n);

            printf("%-14.*ws ", oti[0].Name.Length / 2, oti[0].Name.Buffer);

            n = obi.NameInformationLength == 0 
                ? MAX_PATH * sizeof (WCHAR) : obi.NameInformationLength;

            NT::POBJECT_NAME_INFORMATION oni = NT::POBJECT_NAME_INFORMATION(new CHAR[n]);

            NTSTATUS rv = NT::ZwQueryObject(hObject, NT::ObjectNameInformation, oni, n, &n);
            if (NT_SUCCESS(rv))
                printf("%.*ws", oni[0].Name.Length / 2, oni[0].Name.Buffer);

            printf("\n");

            CloseHandle(hObject);
        }
    }
    delete [] p;

    CloseHandle(hProcess);

    return 0;
}
Here's what I got figured out for now. Using my functions RunAsAdmin(), OpenProcess(), SetPrivilege() and EnumProcesses() I managed to get the handles of every process... well sort of... I get a handle on most of them, but I always get a LastError=5 or 6 which is "ERROR_ACCESS_DENIED" and "ERROR_INVALID_HANDLE".
No idea what I'm doing wrong here tbh. But even if I got this figured out, I'm completely lost where it starts with "if (NT::ZwDuplicateObject [...]" in the C++ code.

Code: Select all

RunAsAdmin()                                         ; Run elevated
thisHProcess := OpenProcess()                        ; Get handle to this ahk process
SetPrivilege(thisHProcess, "SeDebugPrivilege", True) ; Set SeDebugPrivilege on this process
pidList := EnumProcesses()                           ; Get all process IDs

Loop % pidList.MaxIndex() {                          ; Loop through all the PIDs 
    pid := pidList[A_Index]                          ; Store current process ID in pid
    hProcess := OpenProcess(pidList[A_Index])        ; Store current process handle in hProcess
    
    
    If A_LastError                                   ; If an error occured print it out:
        MsgBox % "PID: " pid "`nHandle: " hProcess "`nLastError: " A_LastError
    
    
    CloseHandle(hProcess)                            ; Close the handle again
}


WinGetPid(winTitle := "A") {
    WinGet, pid, PID, %winTitle%
    Return pid
}

OpenProcess(pid := 0, privileges := -1) {
    Return DllCall("OpenProcess", "Uint", (privileges = -1) ? 0x1F0FFF|0x0400 : privileges, "Uint", False, "Uint", pid ? pid : DllCall("GetCurrentProcessId"))
}

CloseHandle(handle) {
    Return DllCall("CloseHandle", "Ptr", handle)
}

SetPrivilege(hProcess, privilegeName := "SeDebugPrivilege", enable := True) {
    DllCall("Advapi32.dll\OpenProcessToken", "Ptr", hProcess, "UInt", 32, "PtrP", tokenHandle)
    VarSetCapacity(newState, 16, 0)
    NumPut(1, newState, 0, "UInt")
    DllCall("Advapi32.dll\LookupPrivilegeValue", "Ptr", 0, "Str", privilegeName, "Int64P", luid)
    NumPut(luid, newState, 4, "Int64")
    If enable
    	NumPut(2, newState, 12, "UInt")
    returnValue := DllCall("Advapi32.dll\AdjustTokenPrivileges", "Ptr", tokenHandle, "Int", False, "Ptr", &newState, "UInt", 0, "Ptr", 0, "Ptr", 0)
    CloseHandle(tokenHandle)
    CloseHandle(hProcess)
    Return returnValue
}

EnumProcesses() {
    pidList := []
    VarSetCapacity(pProcessIds, 4096)
    DllCall("Psapi.dll\EnumProcesses", "Ptr", &pProcessIds, "UInt", 4096, "UIntP", pBytesReturned)
    Loop % pBytesReturned // 4
        If pid := NumGet(pProcessIds, A_Index*4, "UInt") ;pProcessIds = DWORD array (DWORD = 4 bytes)
            pidList.Insert(pid)
    Return pidList
}

RunAsAdmin() {
    If !A_IsAdmin {
        Run *runAs "%A_ScriptFullPath%"
        ExitApp
    }
}
HotKeyIt
Posts: 2364
Joined: 29 Sep 2013, 18:35
Contact:

Re: Find the process that is locking a file/folder

08 Apr 2016, 22:00

That is not that simple, you owe me a beer :D
Requires AutoHotkey_H v1 or v2:

Code: Select all

; http://forums.codeguru.com/showthread.php?176997.html
RunAsAdmin()

Gui,+Resize
Gui,Add,ListView,w640 r30 aw ah, Process Name|Process ID|File Handle|GrantedAccess|Flags|Attributes|HandleCount|File Name

FILETIME :="DWORD dwLowDateTime;DWORD dwHighDateTime"
OBJECT_BASIC_INFORMATION:="
(
	// Information Class 0
	int Attributes;
	int GrantedAccess;
	int HandleCount;
	int PointerCount;
	int PagedPoolUsage;
	int NonPagedPoolUsage;
	int Reserved1;
	int Reserved2;
	int Reserved3;
	int NameInformationLength;
	int TypeInformationLength;
	int SecurityDescriptorLength;
	FILETIME CreateTime;
)"
GENERIC_MAPPING :="
(
  DWORD GenericRead;
  DWORD GenericWrite;
  DWORD GenericExecute;
  DWORD GenericAll;
)"
OBJECT_TYPE_INFORMATION:="
(
	// Information Class 2
	UNICODE_STRING Name;
	int ObjectCount;
	int HandleCount;
	int Reserved1;
	int Reserved2;
	int Reserved3;
	int Reserved4;
	int PeakObjectCount;
	int PeakHandleCount;
	int Reserved5;
	int Reserved6;
	int Reserved7;
	int Reserved8;
	int InvalidAttributes;
	GENERIC_MAPPING GenericMapping;
	int ValidAccess;
	byte Unknown;
	byte MaintainHandleDatabase;
	int PoolType;
	int PagedPoolUsage;
	int NonPagedPoolUsage;
)"
UNICODE_STRING := "WORD Length;WORD MaximumLength;WORD *Buffer"
OBJECT_NAME_INFORMATION:="UNICODE_STRING Name"
SYSTEM_HANDLE_INFORMATION :="
(
	// Information Class 16
	int ProcessID;
	byte ObjectTypeNumber;
	byte Flags; // 0x01 = PROTECT_FROM_CLOSE, 0x02 = INHERIT
	ushort Handle;
	int Object_Pointer;
	int GrantedAccess;
)"

EnablePrivilege()
obi := Struct(OBJECT_BASIC_INFORMATION), oti := Struct("OBJECT_TYPE_INFORMATION[100]"), oni := Struct("OBJECT_NAME_INFORMATION[65]")
ObjectBasicInformation:=0, ObjectNameInformation:=1,ObjectTypeInformation:=2
VarSetCapacity(p,A_PtrSize*n:=0x1000)
SystemHandleInformation:=16
While 0xc0000004 = DllCall("ntdll\ZwQuerySystemInformation","UInt", SystemHandleInformation, "PTR", &p, "UInt", n*A_PtrSize, "UInt*", sz,"CDecl UInt") ;STATUS_INFO_LENGTH_MISMATCH:=0xc0000004
	VarSetCapacity(p, A_PtrSize * (n := n * 2))
h:=Struct("SYSTEM_HANDLE_INFORMATION", (&p) + A_PtrSize)
hCurrentProc:=GetCurrentProcess()
VarSetCapacity(procName,520)
i:=0
Loop % NumGet(&p,"UInt"){
	if !hProc:=OpenProcess(0x40|0x400, false, h[A_Index].ProcessID) ;PROCESS_DUP_HANDLE:=0x40, PROCESS_QUERY_INFORMATION:=0x400
		continue
	if (ZwDuplicateObject(hProc, h[A_Index].Handle, hCurrentProc, getvar(hObject:=0), 0, 0, 0x4)){ ;DUPLICATE_SAME_ATTRIBUTES := 0x4
		CloseHandle(hProc)
		continue
	}
	ZwQueryObject(hObject, ObjectBasicInformation, obi[], sizeof(obi))
	ZwQueryObject(hObject, ObjectTypeInformation, oti[], obi.TypeInformationLength + 2)
	if ("File" !=StrGet(oti.Name.Buffer["",""],oti.Name.Length // 2,"UTF-16") || GetFileType(hObject)!=1)
	{
		CloseHandle(hObject),CloseHandle(hProc)
		continue
	}
	DllCall("QueryFullProcessImageName","Ptr", hProc, "Uint", 0, "Str", procName, "UInt*", sz:=520)
	data:= [procName,h[A_Index].ProcessID, h[A_Index].Handle, h[A_Index].GrantedAccess, h[A_Index].flags, obi.Attributes, (obi.HandleCount - 1)]
	i++
	if ZwQueryObject(hObject, ObjectNameInformation, oni[], obi.NameInformationLength = 0 ? 520 : obi.NameInformationLength)<0x7FFFFFFF ; MAX_PATH*2:=520
		data.Push(StrGet(oni.Name.Buffer["",""],oni.Name.Length // 2,"UTF-16"))
	CloseHandle(hObject),CloseHandle(hProc)
	LV_Add("",data*)
}
Gui,Show, , % "Showing " i " File Handles"
return
GuiClose:
ExitApp
#DllImport, ZwDuplicateObject,ntdll\ZwDuplicateObject,PTR,,PTR,,PTR,,PTR,,UInt,,UInt,,UInt,,UInt
#DllImport, ZwQuerySystemInformation, ntdll\ZwQuerySystemInformation,UInt,,PTR,,UInt,,UInt,,UInt
#DllImport, ZwQueryObject, ntdll\ZwQueryObject,PTR,,UInt,,PTR,,UInt,,UInt,,UInt
#DllImport, ZwQueryInformationFile, ntdll\ZwQueryInformationFile,PTR,,PTR,,PTR,,UInt,,UInt,
RunAsAdmin() {
    If !A_IsAdmin {
        Run *runAs %A_AhkPath% "%A_ScriptFullPath%"
        ExitApp
    }
}
EnablePrivilege(name:="SeDebugPrivilege"){
	static LUID:="DWORD LowPart;LONG  HighPart",LUID_AND_ATTRIBUTES:="EnablePrivilege(LUID) Luid;DWORD Attributes;",TOKEN_PRIVILEGES:="DWORD PrivilegeCount;EnablePrivilege(LUID_AND_ATTRIBUTES) Privileges[1]"
	,priv:=Struct(TOKEN_PRIVILEGES,{PrivilegeCount:1}),init:=priv.Privileges.Attributes:=2 ;SE_PRIVILEGE_ENABLED
    LookupPrivilegeValue(0, name, priv.Privileges.Luid[""])
	OpenProcessToken(GetCurrentProcess(), 32, getvar(hToken:=0)) ; TOKEN_ADJUST_PRIVILEGES:=32
    AdjustTokenPrivileges(hToken, FALSE, priv[], sizeof(priv), 0, 0)
    rv := !A_LastError
    CloseHandle(hToken)
    return rv
}
User avatar
TLM
Posts: 1608
Joined: 01 Oct 2013, 07:52
Contact:

Re: Find the process that is locking a file/folder

09 Apr 2016, 06:10

HotKeyIt wrote:That is not that simple, you owe me a beer :D
Requires AutoHotkey_H v1 or v2:

Code: Select all

; http://forums.codeguru.com/showthread.php?176997.html
RunAsAdmin()

Gui,+Resize
Gui,Add,ListView,w640 r30 aw ah, Process Name|Process ID|File Handle|GrantedAccess|Flags|Attributes|HandleCount|File Name

FILETIME :="DWORD dwLowDateTime;DWORD dwHighDateTime"
OBJECT_BASIC_INFORMATION:="
(
	// Information Class 0
	int Attributes;
	int GrantedAccess;
	int HandleCount;
	int PointerCount;
	int PagedPoolUsage;
	int NonPagedPoolUsage;
	int Reserved1;
	int Reserved2;
	int Reserved3;
	int NameInformationLength;
	int TypeInformationLength;
	int SecurityDescriptorLength;
	FILETIME CreateTime;
)"
GENERIC_MAPPING :="
(
  DWORD GenericRead;
  DWORD GenericWrite;
  DWORD GenericExecute;
  DWORD GenericAll;
)"
OBJECT_TYPE_INFORMATION:="
(
	// Information Class 2
	UNICODE_STRING Name;
	int ObjectCount;
	int HandleCount;
	int Reserved1;
	int Reserved2;
	int Reserved3;
	int Reserved4;
	int PeakObjectCount;
	int PeakHandleCount;
	int Reserved5;
	int Reserved6;
	int Reserved7;
	int Reserved8;
	int InvalidAttributes;
	GENERIC_MAPPING GenericMapping;
	int ValidAccess;
	byte Unknown;
	byte MaintainHandleDatabase;
	int PoolType;
	int PagedPoolUsage;
	int NonPagedPoolUsage;
)"
UNICODE_STRING := "WORD Length;WORD MaximumLength;WORD *Buffer"
OBJECT_NAME_INFORMATION:="UNICODE_STRING Name"
SYSTEM_HANDLE_INFORMATION :="
(
	// Information Class 16
	int ProcessID;
	byte ObjectTypeNumber;
	byte Flags; // 0x01 = PROTECT_FROM_CLOSE, 0x02 = INHERIT
	ushort Handle;
	int Object_Pointer;
	int GrantedAccess;
)"

EnablePrivilege()
obi := Struct(OBJECT_BASIC_INFORMATION), oti := Struct("OBJECT_TYPE_INFORMATION[100]"), oni := Struct("OBJECT_NAME_INFORMATION[65]")
ObjectBasicInformation:=0, ObjectNameInformation:=1,ObjectTypeInformation:=2
VarSetCapacity(p,A_PtrSize*n:=0x1000)
SystemHandleInformation:=16
While 0xc0000004 = DllCall("ntdll\ZwQuerySystemInformation","UInt", SystemHandleInformation, "PTR", &p, "UInt", n*A_PtrSize, "UInt*", sz,"CDecl UInt") ;STATUS_INFO_LENGTH_MISMATCH:=0xc0000004
	VarSetCapacity(p, A_PtrSize * (n := n * 2))
h:=Struct("SYSTEM_HANDLE_INFORMATION", (&p) + A_PtrSize)
hCurrentProc:=GetCurrentProcess()
VarSetCapacity(procName,520)
i:=0
Loop % NumGet(&p,"UInt"){
	if !hProc:=OpenProcess(0x40|0x400, false, h[A_Index].ProcessID) ;PROCESS_DUP_HANDLE:=0x40, PROCESS_QUERY_INFORMATION:=0x400
		continue
	if (ZwDuplicateObject(hProc, h[A_Index].Handle, hCurrentProc, getvar(hObject:=0), 0, 0, 0x4)){ ;DUPLICATE_SAME_ATTRIBUTES := 0x4
		CloseHandle(hProc)
		continue
	}
	ZwQueryObject(hObject, ObjectBasicInformation, obi[], sizeof(obi))
	ZwQueryObject(hObject, ObjectTypeInformation, oti[], obi.TypeInformationLength + 2)
	if ("File" !=StrGet(oti.Name.Buffer["",""],oti.Name.Length // 2,"UTF-16") || GetFileType(hObject)!=1)
	{
		CloseHandle(hObject),CloseHandle(hProc)
		continue
	}
	DllCall("QueryFullProcessImageName","Ptr", hProc, "Uint", 0, "Str", procName, "UInt*", sz:=520)
	data:= [procName,h[A_Index].ProcessID, h[A_Index].Handle, h[A_Index].GrantedAccess, h[A_Index].flags, obi.Attributes, (obi.HandleCount - 1)]
	i++
	if ZwQueryObject(hObject, ObjectNameInformation, oni[], obi.NameInformationLength = 0 ? 520 : obi.NameInformationLength)<0x7FFFFFFF ; MAX_PATH*2:=520
		data.Push(StrGet(oni.Name.Buffer["",""],oni.Name.Length // 2,"UTF-16"))
	CloseHandle(hObject),CloseHandle(hProc)
	LV_Add("",data*)
}
Gui,Show, , % "Showing " i " File Handles"
return
GuiClose:
ExitApp
#DllImport, ZwDuplicateObject,ntdll\ZwDuplicateObject,PTR,,PTR,,PTR,,PTR,,UInt,,UInt,,UInt,,UInt
#DllImport, ZwQuerySystemInformation, ntdll\ZwQuerySystemInformation,UInt,,PTR,,UInt,,UInt,,UInt
#DllImport, ZwQueryObject, ntdll\ZwQueryObject,PTR,,UInt,,PTR,,UInt,,UInt,,UInt
#DllImport, ZwQueryInformationFile, ntdll\ZwQueryInformationFile,PTR,,PTR,,PTR,,UInt,,UInt,
RunAsAdmin() {
    If !A_IsAdmin {
        Run *runAs %A_AhkPath% "%A_ScriptFullPath%"
        ExitApp
    }
}
EnablePrivilege(name:="SeDebugPrivilege"){
	static LUID:="DWORD LowPart;LONG  HighPart",LUID_AND_ATTRIBUTES:="EnablePrivilege(LUID) Luid;DWORD Attributes;",TOKEN_PRIVILEGES:="DWORD PrivilegeCount;EnablePrivilege(LUID_AND_ATTRIBUTES) Privileges[1]"
	,priv:=Struct(TOKEN_PRIVILEGES,{PrivilegeCount:1}),init:=priv.Privileges.Attributes:=2 ;SE_PRIVILEGE_ENABLED
    LookupPrivilegeValue(0, name, priv.Privileges.Luid[""])
	OpenProcessToken(GetCurrentProcess(), 32, getvar(hToken:=0)) ; TOKEN_ADJUST_PRIVILEGES:=32
    AdjustTokenPrivileges(hToken, FALSE, priv[], sizeof(priv), 0, 0)
    rv := !A_LastError
    CloseHandle(hToken)
    return rv
}
Image
;) great job
Bruttosozialprodukt
Posts: 463
Joined: 24 Jan 2014, 22:28

Re: Find the process that is locking a file/folder

11 Apr 2016, 04:07

Hey, thank you so much! Man AHK_H is so cool!! I'll have to translate it into AHK_L code for now though.
Bruttosozialprodukt
Posts: 463
Joined: 24 Jan 2014, 22:28

Re: Find the process that is locking a file/folder

12 Apr 2016, 02:25

Is there a way to resolve `\Device\HarddiskVolumeX` to the drive letter?
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: Find the process that is locking a file/folder

12 Apr 2016, 02:36

Can you show what you got so far?

Just found a way to convert C: DriveLetters to "\Device\HardDiskVolumeX"

Code: Select all

MsgBox % QueryDosDevice("C:")
 
QueryDosDevice(DeviceName := "C:")
{
    size := VarSetCapacity(TargetPath, 260 * (A_IsUnicode ? 2 : 1)) + 1
    DllCall("QueryDosDevice", "str", DeviceName, "str", TargetPath, "uint", size)
    return TargetPath
}
Ref: Displaying Volume Paths
FindFirst / NextVolume gives you Volume names GUID \?\\...
QueryDosDevice gives you a Device name \Device\HarddiskVolume from a GUID or DriveLetter
GetVolumePathNamesForVolumeName gives you the Drive letter C: from a GUID

So what you can do is loop all drives with DriveGet and QueryDosDevice and store them in an array and later you can recheck them.
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
Bruttosozialprodukt
Posts: 463
Joined: 24 Jan 2014, 22:28

Re: Find the process that is locking a file/folder

12 Apr 2016, 09:32

Well, I don't really have anything so far. I found that the Diskpart could be parsed for that.

Code: Select all

DISKPART> list volume

  Volume ###  Ltr  Label        Fs     Type        Size     Status     Info
  ----------  ---  -----------  -----  ----------  -------  ---------  --------
  Volume 0     H                       DVD-ROM         0 B  No Media
  Volume 1                      NTFS   Partition    350 MB  Healthy    System
  Volume 2     C                NTFS   Partition    232 GB  Healthy    Boot
  Volume 3     D   Samsung USB  NTFS   Removable     59 GB  Healthy
But that wouldn't be very elegant.

I really like your way and I think it's good enough. :)

Code: Select all

devicePathObj := GetDevicePaths() 

For devicePath, filePath in devicePathObj
    MsgBox, %devicePath% - %filePath%

MsgBox % devicePathObj["\Device\HarddiskVolume2"]

GetDevicePaths() {
    DriveGet, driveLetters, List
    driveLetters := StrSplit(driveLetters)
    devicePaths := {}
    Loop % driveLetters.MaxIndex()
        devicePaths[QueryDosDevice(driveLetters[A_Index] ":")] := driveLetters[A_Index] ":"
    Return devicePaths
}
QueryDosDevice(DeviceName := "C:") {
    size := VarSetCapacity(TargetPath, 1023) + 1
    DllCall("QueryDosDevice", "str", DeviceName, "str", TargetPath, "uint", size)
    return TargetPath
}
I just tried it on HotkeyIt's example and it works like a charm:

Code: Select all

; http://forums.codeguru.com/showthread.php?176997.html
RunAsAdmin()
SetGlobals()
EnablePrivilege()

Gui, +Resize
Gui, Add, ListView, w640 r30 aw ah, Process Name|Process ID|File Handle|GrantedAccess|Flags|Attributes|HandleCount|Device Path|File Path|Drive
Gui, Show,, % "Showing " i " File Handles"

obi := Struct(OBJECT_BASIC_INFORMATION), oti := Struct("OBJECT_TYPE_INFORMATION[100]"), oni := Struct("OBJECT_NAME_INFORMATION[65]")
ObjectBasicInformation:=0, ObjectNameInformation:=1,ObjectTypeInformation:=2
VarSetCapacity(p,A_PtrSize*n:=0x1000)
SystemHandleInformation:=16
While 0xc0000004 = DllCall("ntdll\ZwQuerySystemInformation","UInt", SystemHandleInformation, "PTR", &p, "UInt", n*A_PtrSize, "UInt*", sz,"CDecl UInt") ;STATUS_INFO_LENGTH_MISMATCH:=0xc0000004
	VarSetCapacity(p, A_PtrSize * (n := n * 2))
h:=Struct("SYSTEM_HANDLE_INFORMATION", (&p) + A_PtrSize)
hCurrentProc:=GetCurrentProcess()
VarSetCapacity(procName,520)
i:=0

devicePathsObj := GetDevicePaths()

Loop % NumGet(&p,"UInt") {
	If !hProc:=OpenProcess(0x40|0x400, false, h[A_Index].ProcessID) ;PROCESS_DUP_HANDLE:=0x40, PROCESS_QUERY_INFORMATION:=0x400
		Continue
	If (ZwDuplicateObject(hProc, h[A_Index].Handle, hCurrentProc, getvar(hObject:=0), 0, 0, 0x4)){ ;DUPLICATE_SAME_ATTRIBUTES := 0x4
		CloseHandle(hProc)
		Continue
	}
	ZwQueryObject(hObject, ObjectBasicInformation, obi[], sizeof(obi))
	ZwQueryObject(hObject, ObjectTypeInformation, oti[], obi.TypeInformationLength + 2)
	If ("File" !=StrGet(oti.Name.Buffer["",""],oti.Name.Length // 2,"UTF-16") || GetFileType(hObject)!=1) {
		CloseHandle(hObject),CloseHandle(hProc)
		Continue
	}
	DllCall("QueryFullProcessImageName","Ptr", hProc, "Uint", 0, "Str", procName, "UInt*", sz:=520)
	data:= [procName,h[A_Index].ProcessID, h[A_Index].Handle, h[A_Index].GrantedAccess, h[A_Index].flags, obi.Attributes, (obi.HandleCount - 1)]
	i++
	If ZwQueryObject(hObject, ObjectNameInformation, oni[], obi.NameInformationLength = 0 ? 520 : obi.NameInformationLength)<0x7FFFFFFF { ; MAX_PATH*2:=520
        devicePath := StrGet(oni.Name.Buffer["",""],oni.Name.Length // 2,"UTF-16")
        RegexMatch(devicePath, "S)(^\\.+?\\.+?)(\\|$)", matches)
        filePath := StrReplace(devicePath,matches[1],devicePathsObj[matches[1]],o,1)
        drive := SubStr(filePath,1,1)
		data.Push(devicePath, filePath, drive)
    }
	CloseHandle(hObject),CloseHandle(hProc)
    
	LV_Add("",data*)
}

GuiClose(){
    ExitApp
}
#DllImport, ZwDuplicateObject,ntdll\ZwDuplicateObject,PTR,,PTR,,PTR,,PTR,,UInt,,UInt,,UInt,,UInt
#DllImport, ZwQuerySystemInformation, ntdll\ZwQuerySystemInformation,UInt,,PTR,,UInt,,UInt,,UInt
#DllImport, ZwQueryObject, ntdll\ZwQueryObject,PTR,,UInt,,PTR,,UInt,,UInt,,UInt
#DllImport, ZwQueryInformationFile, ntdll\ZwQueryInformationFile,PTR,,PTR,,PTR,,UInt,,UInt,
RunAsAdmin() {
    If !A_IsAdmin {
        Run % "*runAs " (A_IsCompiled?"":A_AhkPath " ") "`"" A_ScriptFullPath "`""
        ExitApp
    }
}
EnablePrivilege(name:="SeDebugPrivilege"){
	static LUID:="DWORD LowPart;LONG  HighPart",LUID_AND_ATTRIBUTES:="EnablePrivilege(LUID) Luid;DWORD Attributes;",TOKEN_PRIVILEGES:="DWORD PrivilegeCount;EnablePrivilege(LUID_AND_ATTRIBUTES) Privileges[1]"
	,priv:=Struct(TOKEN_PRIVILEGES,{PrivilegeCount:1}),init:=priv.Privileges.Attributes:=2 ;SE_PRIVILEGE_ENABLED
    LookupPrivilegeValue(0, name, priv.Privileges.Luid[""])
	OpenProcessToken(GetCurrentProcess(), 32, getvar(hToken:=0)) ; TOKEN_ADJUST_PRIVILEGES:=32
    AdjustTokenPrivileges(hToken, FALSE, priv[], sizeof(priv), 0, 0)
    rv := !A_LastError
    CloseHandle(hToken)
    return rv
}

GetDevicePaths() {
    DriveGet, driveLetters, List
    driveLetters := StrSplit(driveLetters)
    devicePaths := {}
    Loop % driveLetters.MaxIndex()
        devicePaths[QueryDosDevice(driveLetters[A_Index] ":")] := driveLetters[A_Index] ":"
    Return devicePaths
}
QueryDosDevice(DeviceName := "C:") {
    size := VarSetCapacity(TargetPath, 1023) + 1
    DllCall("QueryDosDevice", "str", DeviceName, "str", TargetPath, "uint", size)
    return TargetPath
}

SetGlobals() {
    Global
    FILETIME :="DWORD dwLowDateTime;DWORD dwHighDateTime"
    OBJECT_BASIC_INFORMATION:="
    (
        // Information Class 0
        int Attributes;
        int GrantedAccess;
        int HandleCount;
        int PointerCount;
        int PagedPoolUsage;
        int NonPagedPoolUsage;
        int Reserved1;
        int Reserved2;
        int Reserved3;
        int NameInformationLength;
        int TypeInformationLength;
        int SecurityDescriptorLength;
        FILETIME CreateTime;
    )"
    GENERIC_MAPPING :="
    (
      DWORD GenericRead;
      DWORD GenericWrite;
      DWORD GenericExecute;
      DWORD GenericAll;
    )"
    OBJECT_TYPE_INFORMATION:="
    (
        // Information Class 2
        UNICODE_STRING Name;
        int ObjectCount;
        int HandleCount;
        int Reserved1;
        int Reserved2;
        int Reserved3;
        int Reserved4;
        int PeakObjectCount;
        int PeakHandleCount;
        int Reserved5;
        int Reserved6;
        int Reserved7;
        int Reserved8;
        int InvalidAttributes;
        GENERIC_MAPPING GenericMapping;
        int ValidAccess;
        byte Unknown;
        byte MaintainHandleDatabase;
        int PoolType;
        int PagedPoolUsage;
        int NonPagedPoolUsage;
    )"
    UNICODE_STRING := "WORD Length;WORD MaximumLength;WORD *Buffer"
    OBJECT_NAME_INFORMATION:="UNICODE_STRING Name"
    SYSTEM_HANDLE_INFORMATION :="
    (
        // Information Class 16
        int ProcessID;
        byte ObjectTypeNumber;
        byte Flags; // 0x01 = PROTECT_FROM_CLOSE, 0x02 = INHERIT
        ushort Handle;
        int Object_Pointer;
        int GrantedAccess;
    )"
}
Bruttosozialprodukt
Posts: 463
Joined: 24 Jan 2014, 22:28

Re: Find the process that is locking a file/folder

12 Apr 2016, 15:49

Okay this is getting really nice and usable. I'm still on AHK_H for now, but I still think it's worth sharing.
It's much nicer and way faster than the resource monitor garbage. I even got icons to work, which makes it way easier to spot certain files and it just looks nice. :D

Code: Select all

; http://forums.codeguru.com/showthread.php?176997.html
; credits to HotkeyIt for the super complicated handle retrievement!
; credits to jNizM for the neat QueryDosDevice function!

RunAsAdmin()
SetGlobals()
EnablePrivilege()

Gui, +Resize
Gui, Add, Edit, w640 r1 aw gGui_FilterList vGui_Filter
Gui, Add, ListView, w640 r30 aw ah, Potentially locked|By
LV_ModifyCol(1,300)
LV_ModifyCol(2,300)
Gui, Add, Button, w640 aw ay gGui_Reload, Reload
Gui, Add, Progress, w640 aw ay vGui_Progress
Gui, Show,, % "Showing " i " File Handles"

Gui_Reload()

Gui_FilterList(ctrlHwnd:="", guiEvent:="", eventInfo:="", errLvl:=0) {
    Global
    GuiControlGet, Gui_Filter
    LV_Delete()
    Loop % dataArray.MaxIndex() {
        data := dataArray[A_Index]
        If (InStr(data.filePath,Gui_Filter))
            LV_Add("Icon" A_Index, data.filePath, data.procFullPath)
    }
}

Gui_Reload() {
    Global ;let's make life a bit more easy
    obi := Struct(OBJECT_BASIC_INFORMATION), oti := Struct("OBJECT_TYPE_INFORMATION[100]"), oni := Struct("OBJECT_NAME_INFORMATION[65]")
    ObjectBasicInformation:=0, ObjectNameInformation:=1,ObjectTypeInformation:=2
    VarSetCapacity(p,A_PtrSize*n:=0x1000)
    SystemHandleInformation:=16
    While 0xc0000004 = DllCall("ntdll\ZwQuerySystemInformation","UInt", SystemHandleInformation, "PTR", &p, "UInt", n*A_PtrSize, "UInt*", sz,"CDecl UInt") ;STATUS_INFO_LENGTH_MISMATCH:=0xc0000004
        VarSetCapacity(p, A_PtrSize * (n := n * 2))
    h:=Struct("SYSTEM_HANDLE_INFORMATION", (&p) + A_PtrSize)
    hCurrentProc:=GetCurrentProcess()
    VarSetCapacity(procName,520)
    dataArray := []
    i:=0
    numgetRes := NumGet(&p,"UInt")
    
    LV_Delete()
    ImageList := IL_Create(numgetRes)
    LV_SetImageList(ImageList)
    GuiControl,, Gui_Progress, 0 
    
    devicePathsObj := GetDevicePaths()

    Loop % numgetRes {
        GuiControl,, Gui_Progress, % A_Index/numgetRes*100
        If !hProc:=OpenProcess(0x40|0x400, false, h[A_Index].ProcessID) ;PROCESS_DUP_HANDLE:=0x40, PROCESS_QUERY_INFORMATION:=0x400
            Continue
        If (ZwDuplicateObject(hProc, h[A_Index].Handle, hCurrentProc, getvar(hObject:=0), 0, 0, 0x4)){ ;DUPLICATE_SAME_ATTRIBUTES := 0x4
            CloseHandle(hProc)
            Continue
        }
        ZwQueryObject(hObject, ObjectBasicInformation, obi[], sizeof(obi))
        ZwQueryObject(hObject, ObjectTypeInformation, oti[], obi.TypeInformationLength + 2)
        If ("File" !=StrGet(oti.Name.Buffer["",""],oti.Name.Length // 2,"UTF-16") || GetFileType(hObject)!=1) {
            CloseHandle(hObject),CloseHandle(hProc)
            Continue
        }
        DllCall("QueryFullProcessImageName","Ptr", hProc, "Uint", 0, "Str", procName, "UInt*", sz:=520)
        data := {}
        data.procFullPath := procName
        data.pid := h[A_Index].ProcessID
        data.handle := h[A_Index].Handle
        data.grantedAccess := h[A_Index].GrantedAccess
        data.flags := h[A_Index].flags
        data.attributes := obi.Attributes
        data.handleCount := (obi.HandleCount - 1)
        i++
        If ZwQueryObject(hObject, ObjectNameInformation, oni[], obi.NameInformationLength = 0 ? 520 : obi.NameInformationLength)<0x7FFFFFFF { ; MAX_PATH*2:=520
            devicePath := StrGet(oni.Name.Buffer["",""],oni.Name.Length // 2,"UTF-16")
            RegexMatch(devicePath, "S)(^\\.+?\\.+?)(\\|$)", matches)
            filePath := StrReplace(devicePath,matches[1],devicePathsObj[matches[1]],o,1)
            drive := SubStr(filePath,1,1)
            data.devicePath := devicePath
            data.filePath := filePath
            data.drive := drive
            data.isfolder := InStr(FileExist(data.filePath), "D") ? True : False
            
            SplitPath, % data.filePath,,, fileExt
            If (data.isfolder)
                IL_Add(ImageList, "shell32.dll", 5)
            Else If (fileExt = "exe")
                IL_Add(ImageList, data.filePath, 0)
            Else {
                icon := GetIconByExt(fileExt)
                If (icon.file)
                    IL_Add(ImageList, icon.file, icon.index)
                Else
                    IL_Add(ImageList, "imageres.dll", 3) 
            }
            dataArray.Push(data)
        }
        
        CloseHandle(hObject),CloseHandle(hProc)
    }
    
    Gui_FilterList()
}

GuiClose(){
    ExitApp
}
#DllImport, ZwDuplicateObject,ntdll\ZwDuplicateObject,PTR,,PTR,,PTR,,PTR,,UInt,,UInt,,UInt,,UInt
#DllImport, ZwQuerySystemInformation, ntdll\ZwQuerySystemInformation,UInt,,PTR,,UInt,,UInt,,UInt
#DllImport, ZwQueryObject, ntdll\ZwQueryObject,PTR,,UInt,,PTR,,UInt,,UInt,,UInt
#DllImport, ZwQueryInformationFile, ntdll\ZwQueryInformationFile,PTR,,PTR,,PTR,,UInt,,UInt,
RunAsAdmin() {
    If !A_IsAdmin {
        Run % "*runAs " (A_IsCompiled?"":A_AhkPath " ") "`"" A_ScriptFullPath "`""
        ExitApp
    }
}
EnablePrivilege(name:="SeDebugPrivilege"){
	static LUID:="DWORD LowPart;LONG  HighPart",LUID_AND_ATTRIBUTES:="EnablePrivilege(LUID) Luid;DWORD Attributes;",TOKEN_PRIVILEGES:="DWORD PrivilegeCount;EnablePrivilege(LUID_AND_ATTRIBUTES) Privileges[1]"
	,priv:=Struct(TOKEN_PRIVILEGES,{PrivilegeCount:1}),init:=priv.Privileges.Attributes:=2 ;SE_PRIVILEGE_ENABLED
    LookupPrivilegeValue(0, name, priv.Privileges.Luid[""])
	OpenProcessToken(GetCurrentProcess(), 32, getvar(hToken:=0)) ; TOKEN_ADJUST_PRIVILEGES:=32
    AdjustTokenPrivileges(hToken, FALSE, priv[], sizeof(priv), 0, 0)
    rv := !A_LastError
    CloseHandle(hToken)
    return rv
}

GetIconByExt(ext) {
   from := RegRead("HKEY_CLASSES_ROOT\." ext)
   defaultIcon := RegRead("HKEY_CLASSES_ROOT\" from "\DefaultIcon")
   defaultIcon := StrReplace(defaultIcon, "`"", "") ;"
   defaultIcon := StrReplace(defaultIcon, "`%SystemRoot`%", A_WinDir)
   defaultIcon := StrReplace(defaultIcon, "`%ProgramFiles`%", A_ProgramFiles)
   defaultIcon := StrReplace(defaultIcon, "`%windir`%", A_WinDir)
   defaultIconSplit := StrSplit(defaultIcon,",")
   resFile := defaultIconSplit[1]
   index := defaultIconSplit[2]
   ;index := (index < 0 ? abs(index)-4 : index)
   Return {file: resFile, index: index}
}


GetDevicePaths() {
    DriveGet, driveLetters, List
    driveLetters := StrSplit(driveLetters)
    devicePaths := {}
    Loop % driveLetters.MaxIndex()
        devicePaths[QueryDosDevice(driveLetters[A_Index] ":")] := driveLetters[A_Index] ":"
    Return devicePaths
}
QueryDosDevice(DeviceName := "C:") {
    size := VarSetCapacity(TargetPath, 1023) + 1
    DllCall("QueryDosDevice", "str", DeviceName, "str", TargetPath, "uint", size)
    return TargetPath
}

SetGlobals() {
    Global
    FILETIME :="DWORD dwLowDateTime;DWORD dwHighDateTime"
    OBJECT_BASIC_INFORMATION:="
    (
        // Information Class 0
        int Attributes;
        int GrantedAccess;
        int HandleCount;
        int PointerCount;
        int PagedPoolUsage;
        int NonPagedPoolUsage;
        int Reserved1;
        int Reserved2;
        int Reserved3;
        int NameInformationLength;
        int TypeInformationLength;
        int SecurityDescriptorLength;
        FILETIME CreateTime;
    )"
    GENERIC_MAPPING :="
    (
      DWORD GenericRead;
      DWORD GenericWrite;
      DWORD GenericExecute;
      DWORD GenericAll;
    )"
    OBJECT_TYPE_INFORMATION:="
    (
        // Information Class 2
        UNICODE_STRING Name;
        int ObjectCount;
        int HandleCount;
        int Reserved1;
        int Reserved2;
        int Reserved3;
        int Reserved4;
        int PeakObjectCount;
        int PeakHandleCount;
        int Reserved5;
        int Reserved6;
        int Reserved7;
        int Reserved8;
        int InvalidAttributes;
        GENERIC_MAPPING GenericMapping;
        int ValidAccess;
        byte Unknown;
        byte MaintainHandleDatabase;
        int PoolType;
        int PagedPoolUsage;
        int NonPagedPoolUsage;
    )"
    UNICODE_STRING := "WORD Length;WORD MaximumLength;WORD *Buffer"
    OBJECT_NAME_INFORMATION:="UNICODE_STRING Name"
    SYSTEM_HANDLE_INFORMATION :="
    (
        // Information Class 16
        int ProcessID;
        byte ObjectTypeNumber;
        byte Flags; // 0x01 = PROTECT_FROM_CLOSE, 0x02 = INHERIT
        ushort Handle;
        int Object_Pointer;
        int GrantedAccess;
    )"
}
Image
Image
Image

It might also be a cool idea to show which process is using/blocking which port.
I'm also wondering if we could close the handles that are locking a file without closing the process responsible. That would be pretty neat. :D
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: Find the process that is locking a file/folder

13 Apr 2016, 02:04

Looks nice, thanks for sharing =)
Maybe someone (like just me) can assist you to translate it into pure ahk_l.

Do you want to create a "Scripts and Functions" Topic? So I can add a Process Explorer like tool into your Topic too.
(or creatinging 1 big one together - maybe on github or something like this)
Image
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: Find the process that is locking a file/folder

13 Apr 2016, 03:22

For closing handles you can maybe use CloseHandle function (Ref: Kernel Objects)
The CloseHandle function closes handles to the following objects:
Access token
Communications device
Console input
Console screen buffer
Event
File
File mapping
I/O completion port
Job
Mailslot
Memory resource notification
Mutex
Named pipe
Pipe
Process <======
Semaphore
Thread <======
Transaction
Waitable timer

Code: Select all

CloseHandle(handle)
{
    if !(DllCall("CloseHandle", "ptr", handle))
        return A_LastError
    return 1
]
And with GetHandleInformation function you can check if the handle is protected from closing

Code: Select all

GetHandleInformation(handle)
{
    static FLAG := {1: "INHERIT", 2: "PROTECT_FROM_CLOSE"}
    if !(DllCall("GetHandleInformation", "ptr", handle, "uint*", flags))
        return A_LastError
    return flags
}
with SetHandleInformation function you can (maybe) set a handle protection or remove a protection

Code: Select all

SetHandleInformation(handle, mask := 1, flags := 1)
{
    if !(DllCall("SetHandleInformation", "ptr", handle, "uint", mask, "uint", flags))
        return A_LastError
    return 1
}
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
Bruttosozialprodukt
Posts: 463
Joined: 24 Jan 2014, 22:28

Re: Find the process that is locking a file/folder

13 Apr 2016, 03:41

https://autohotkey.com/boards/viewtopic.php?f=6&t=15857
That process explorer looks pretty cool. But your tool shows a list of processes that can be filtered, while my tool shows a list of handles that can be filtered. I guess we could add a new window that opens when you doubleclick a process to show all of its handles. The use-case would be a different one though.
I will, however, try to abstract the code into useful functions or classes so that you can easily add it to your process explorer.

EDIT:
Thanks, the handle functions look promising. :)
just me
Posts: 9424
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Find the process that is locking a file/folder

13 Apr 2016, 03:43

Hello Bruttosozialprodukt,

I'm wondering about some structure definitions and offsets. Did you run it on x64 with AHK 64?
Last edited by just me on 13 Apr 2016, 03:44, edited 1 time in total.
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: Find the process that is locking a file/folder

13 Apr 2016, 03:43

If you finished the translation to ahk_l i will pm you the code from the process explorer (rewrite it a bit atm) and we can create a big process / handles / ... tool.

Welcome just me =)

[ot]
since we are german speaking guys we should move the follow communication for this into german help subforum, so its easier for all.
[/ot]
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
Bruttosozialprodukt
Posts: 463
Joined: 24 Jan 2014, 22:28

Re: Find the process that is locking a file/folder

13 Apr 2016, 03:47

I ran it on Window 8.1 x64. I think I used AHK_H v2 32bit version, but I can't remember.
Edit:
Yes, I used AHK_H v2 Win32w. Using the x64w version it doesn't work, no errors, but the list stays empty. So you're probably right with your suspicion.
just me
Posts: 9424
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Find the process that is locking a file/folder

13 Apr 2016, 04:23

If you have time, please try this:

Code: Select all

    SYSTEM_HANDLE_INFORMATION :="
    (
        // Information Class 16
        int ProcessID;
        byte ObjectTypeNumber;
        byte Flags; // 0x01 = PROTECT_FROM_CLOSE, 0x02 = INHERIT
        ushort Handle;
        pvoid Object_Pointer; // <<<<< changed int to pvoid
        int GrantedAccess;
    )"
Bruttosozialprodukt
Posts: 463
Joined: 24 Jan 2014, 22:28

Re: Find the process that is locking a file/folder

13 Apr 2016, 04:31

Just the same. It still works with AHK_H 32 bit and the list is empty using the 64 bit version.
just me
Posts: 9424
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Find the process that is locking a file/folder

13 Apr 2016, 04:50

Hmmm, seems to need more. See you later!
HotKeyIt
Posts: 2364
Joined: 29 Sep 2013, 18:35
Contact:

Re: Find the process that is locking a file/folder

13 Apr 2016, 05:11

jNizM wrote:And with GetHandleInformation function you can check if the handle is protected from closing

Code: Select all

GetHandleInformation(handle)
{
    static FLAG := {1: "INHERIT", 2: "PROTECT_FROM_CLOSE"}
    if !(DllCall("GetHandleInformation", "ptr", handle, "uint*", flags))
        return A_LastError
    return flags
}
This is already available in Flags, see SYSTEM_HANDLE_INFORMATION.

For 64-bit change A_PtrSize to 4: h:=Struct("SYSTEM_HANDLE_INFORMATION", (&p) + 4) ;)

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: FanaticGuru and 149 guests