不使用os.listdir快速判断目录是否为空的Python3 kernel32调用异常问题
Fixing Access Violation & Building an Efficient Directory Empty Check for Windows in Python
First, let's break down why you're hitting that access violation error, then fix the code to be reliable and optimized for your large-scale use case—handling thousands of directories with up to 100k files each.
What's Causing the Access Violation?
Your code has a few critical issues that lead to memory access errors and unreliable behavior:
- Missing Win32 API Type Annotations: You didn't define
argtypesandrestypefor the kernel32 functions. Ctypes guesses these types by default, which often leads to stack corruption or invalid memory writes (exactly the error you're seeing). - Incorrect Handle Validation:
INVALID_HANDLE_VALUEisn't just-1across all system architectures—using this hardcoded value can fail on 64-bit systems where handles are 64-bit integers. - Unsafe Resource Cleanup: If your loop breaks early (when finding a non-empty file),
FindClosemight never run, leaking file handles. - Incomplete Error Handling: When
FindNextFileWreturnsFalse, you need to check if it's because there are no more files, or if an actual error occurred.
Fixed & Optimized Code
Here's the revised implementation that addresses all these issues while keeping the efficiency of direct Win32 API calls:
import os import ctypes from ctypes import wintypes # Define critical Win32 constants INVALID_HANDLE_VALUE = wintypes.HANDLE(-1).value ERROR_NO_MORE_FILES = 18 # Load kernel32 with proper error handling kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) # Declare function types to avoid stack corruption kernel32.FindFirstFileW.argtypes = ( wintypes.LPCWSTR, ctypes.POINTER(wintypes.WIN32_FIND_DATAW) ) kernel32.FindFirstFileW.restype = wintypes.HANDLE kernel32.FindNextFileW.argtypes = ( wintypes.HANDLE, ctypes.POINTER(wintypes.WIN32_FIND_DATAW) ) kernel32.FindNextFileW.restype = wintypes.BOOL kernel32.FindClose.argtypes = (wintypes.HANDLE,) kernel32.FindClose.restype = wintypes.BOOL def is_empty(fpath): # Construct the search path with wildcard search_path = os.path.join(fpath, '*') wfd = wintypes.WIN32_FIND_DATAW() handle = kernel32.FindFirstFileW(search_path, ctypes.byref(wfd)) if handle == INVALID_HANDLE_VALUE: # Return True if directory is inaccessible or doesn't exist (matches your original logic) return True try: # Check the first found entry immediately if wfd.cFileName not in ('.', '..'): return False # Keep searching until we find a non-system entry or exhaust files while True: success = kernel32.FindNextFileW(handle, ctypes.byref(wfd)) if not success: last_error = ctypes.get_last_error() # No more files to check—directory is empty if last_error == ERROR_NO_MORE_FILES: return True # On unexpected error, default to your original return value return True # Found a real file/directory—return False immediately if wfd.cFileName not in ('.', '..'): return False finally: # Ensure we always close the handle to avoid leaks kernel32.FindClose(handle) # Test the function print(is_empty(r'C:\Users'))
Key Improvements
- Type Safety: By declaring
argtypesandrestype, we tell ctypes exactly how to marshal parameters between Python and the Win32 API, eliminating memory access errors. - Reliable Handle Management: The
try-finallyblock guaranteesFindCloseruns even if we exit early, preventing handle leaks that could cause issues with thousands of directories. - Early Termination: The function returns as soon as it finds any non-
./..entry, making it extremely fast even for directories with 100k+ files—no need to load all filenames into memory. - Cross-Architecture Support: Uses proper
wintypesdefinitions to work on both 32-bit and 64-bit Python.
内容的提问来源于stack exchange,提问作者Tavy
相关产品推荐
相关产品推荐

