Condor Ruben
Condor Ruben

Reputation: 35

Python ctypes API call results in Access Violation

I am trying to list a directory with FindFirstFileW and FindNextFileW in python using ctypes. The FindFirstFileW is successful but FindNextFileW or FindClose results in OSError: exception: access violation writing 0xFFFFFFFFB8093F80

This is the code:

def assert_success(success):
    if not success:
        raise AssertionError(FormatError())

def assert_handle(handle):
    if handle == INVALID_HANDLE_VALUE:
        raise AssertionError(FormatError())
    

def FindFirstFileW(lpFileName):
    wfd = WIN32_FIND_DATAW()
    handle = windll.kernel32.FindFirstFileW(lpFileName, byref(wfd))
    assert_handle(handle)
    return handle, wfd

def FindNextFileW(handle):
    wfd = WIN32_FIND_DATAW()
    success = windll.kernel32.FindNextFileW(handle, byref(wfd))
    assert_success(success)
    return wfd

def FindClose(handle):
    success = windll.kernel32.FindClose(handle)
    assert_success(success)
    return success

handle, wfd = FindFirstFileW('C:\\Windows\\*')
print(wfd.cFileName)
wfd = FindNextFileW(handle)
print(wfd.cFileName)
FindClose(handle)

Upvotes: 0

Views: 530

Answers (1)

Mark Tolonen
Mark Tolonen

Reputation: 178399

You haven't set .argtypes and .restype appropriately for the functions being used. ctypes assumes return values are c_int for example, but handles are 64-bit on 64-bit Python and getting truncated to 32 bits. Plus you get the added benefit of type checking your calls since ctypes knows what the parameter types are supposed to be.

Also recommend .errcheck for automatically checking return values:

Try this:

from ctypes import *
from ctypes import wintypes as w

INVALID_HANDLE_VALUE = w.HANDLE(-1).value
ERROR_NO_MORE_FILES = 18

def boolcheck(result,func,args):
    if not result:
        raise WinError(get_last_error())
    return None

# return True if more files
# return False if no more files
# raise exception for other reasons
def nomorecheck(result,func,args):
    if not result and get_last_error() != ERROR_NO_MORE_FILES:
        raise WinError(get_last_error())
    return result

def handlecheck(result,func,args):
    if result == INVALID_HANDLE_VALUE:
        raise WinError(get_last_error())
    return result

dll = WinDLL('kernel32',use_last_error=True)
dll.FindFirstFileW.argtypes = w.LPCWSTR,w.LPWIN32_FIND_DATAW
dll.FindFirstFileW.restype = w.HANDLE
dll.FindFirstFileW.errcheck = handlecheck
dll.FindNextFileW.argtypes = w.HANDLE,w.LPWIN32_FIND_DATAW
dll.FindNextFileW.restype = w.BOOL
dll.FindClose.errcheck = nomorecheck
dll.FindClose.argtypes = w.HANDLE,
dll.FindClose.restype = w.BOOL
dll.FindClose.errcheck = boolcheck

def find_files(directory):
    wfd = w.WIN32_FIND_DATAW()
    handle = dll.FindFirstFileW(directory, byref(wfd))
    yield wfd.cFileName
    while dll.FindNextFileW(handle, byref(wfd)):
        yield wfd.cFileName
    dll.FindClose(handle)

for file in find_files(r'c:\windows\*'):
    print(file)

Upvotes: 1

Related Questions