What process is using a given file?

Question:

I’m having trouble with one of my scripts, where it erratically seems to have trouble writing to its own log, throwing the error “This file is being used by another process.”

I know there are ways to handle this with try excepts, but I’d like to find out why this is happening rather than just papering over it. Nothing else should be accessing that file at all. So in order to confirm the source of the bug, I’d like to find out what service is using that file.

Is there a way in Python on Windows to check what process is using a given file?

Asked By: SuperBiasedMan

||

Answers:

You can use Microsoft’s handle.exe command-line utility. For example:

import re
import subprocess

_handle_pat = re.compile(r'(.*?)s+pid:s+(d+).*[0-9a-fA-F]+:s+(.*)')

def open_files(name):
    """return a list of (process_name, pid, filename) tuples for
       open files matching the given name."""
    lines = subprocess.check_output('handle.exe "%s"' % name).splitlines()
    results = (_handle_pat.match(line.decode('mbcs')) for line in lines)
    return [m.groups() for m in results if m]

Note that this has limitations regarding Unicode filenames. In Python 2 subprocess passes name as an ANSI string because it calls CreateProcessA instead of CreateProcessW. In Python 3 the name gets passed as Unicode. In either case, handle.exe writes its output using a lossy ANSI encoding, so the matched filename in the result tuple may contain best-fit characters and “?” replacements.

Answered By: Eryk Sun

Please don’t delete this answer in case I did anything wrong but give me a chance to correct it by leaving a comment. Thanks!

There is a better way than iterating through all PIDs (as was suggested in the comments) that involves performing a Windows API call to determine all handles on a given file. Please find below a code example which I have already posted for another question (however, cannot flag as duplicate since it does not have any accepted answers). Note that this only works for Windows.

import ctypes
from ctypes import wintypes

path = r"C:tempstackoverflow39570207.txt"

# -----------------------------------------------------------------------------
# generic strings and constants
# -----------------------------------------------------------------------------

ntdll = ctypes.WinDLL('ntdll')
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)

NTSTATUS = wintypes.LONG

INVALID_HANDLE_VALUE = wintypes.HANDLE(-1).value
FILE_READ_ATTRIBUTES = 0x80
FILE_SHARE_READ = 1
OPEN_EXISTING = 3
FILE_FLAG_BACKUP_SEMANTICS = 0x02000000

FILE_INFORMATION_CLASS = wintypes.ULONG
FileProcessIdsUsingFileInformation = 47

LPSECURITY_ATTRIBUTES = wintypes.LPVOID
ULONG_PTR = wintypes.WPARAM


# -----------------------------------------------------------------------------
# create handle on concerned file with dwDesiredAccess == FILE_READ_ATTRIBUTES
# -----------------------------------------------------------------------------

kernel32.CreateFileW.restype = wintypes.HANDLE
kernel32.CreateFileW.argtypes = (
    wintypes.LPCWSTR,      # In     lpFileName
    wintypes.DWORD,        # In     dwDesiredAccess
    wintypes.DWORD,        # In     dwShareMode
    LPSECURITY_ATTRIBUTES,  # In_opt lpSecurityAttributes
    wintypes.DWORD,        # In     dwCreationDisposition
    wintypes.DWORD,        # In     dwFlagsAndAttributes
    wintypes.HANDLE)       # In_opt hTemplateFile
hFile = kernel32.CreateFileW(
    path, FILE_READ_ATTRIBUTES, FILE_SHARE_READ, None, OPEN_EXISTING,
    FILE_FLAG_BACKUP_SEMANTICS, None)
if hFile == INVALID_HANDLE_VALUE:
    raise ctypes.WinError(ctypes.get_last_error())


# -----------------------------------------------------------------------------
# prepare data types for system call
# -----------------------------------------------------------------------------

class IO_STATUS_BLOCK(ctypes.Structure):
    class _STATUS(ctypes.Union):
        _fields_ = (('Status', NTSTATUS),
                    ('Pointer', wintypes.LPVOID))
    _anonymous_ = '_Status',
    _fields_ = (('_Status', _STATUS),
                ('Information', ULONG_PTR))


iosb = IO_STATUS_BLOCK()


class FILE_PROCESS_IDS_USING_FILE_INFORMATION(ctypes.Structure):
    _fields_ = (('NumberOfProcessIdsInList', wintypes.LARGE_INTEGER),
                ('ProcessIdList', wintypes.LARGE_INTEGER * 64))


info = FILE_PROCESS_IDS_USING_FILE_INFORMATION()

PIO_STATUS_BLOCK = ctypes.POINTER(IO_STATUS_BLOCK)
ntdll.NtQueryInformationFile.restype = NTSTATUS
ntdll.NtQueryInformationFile.argtypes = (
    wintypes.HANDLE,        # In  FileHandle
    PIO_STATUS_BLOCK,       # Out IoStatusBlock
    wintypes.LPVOID,        # Out FileInformation
    wintypes.ULONG,         # In  Length
    FILE_INFORMATION_CLASS)  # In  FileInformationClass

# -----------------------------------------------------------------------------
# system call to retrieve list of PIDs currently using the file
# -----------------------------------------------------------------------------
status = ntdll.NtQueryInformationFile(hFile, ctypes.byref(iosb),
                                      ctypes.byref(info),
                                      ctypes.sizeof(info),
                                      FileProcessIdsUsingFileInformation)
pidList = info.ProcessIdList[0:info.NumberOfProcessIdsInList]
print(pidList)
Answered By: Robert
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.