异步调用ReadDirectoryChangesW的完成例程

时间:2014-09-08 09:23:22

标签: python python-2.7 winapi ctypes

我正在尝试使用python中的ReadDirectoryChangesW(带有完成例程的异步模式)。

MS documentation备注之后,我了解到必须提供OVERLAPPED结构和回调。

使用该文档描述回调:

VOID CALLBACK FileIOCompletionRoutine(
  _In_     DWORD dwErrorCode,
  _In_     DWORD dwNumberOfBytesTransfered,
  _Inout_  LPOVERLAPPED lpOverlapped
);

所以我将ctypes WINFUNCTYPE工厂实例化为:

RDCW_CALLBACK_F = ctypes.WINFUNCTYPE(None, ctypes.wintypes.DWORD, ctypes.wintypes.DWORD, ctypes.POINTER(OVERLAPPED))

并尝试访问FileIOCompletionRoutine来电:

ReadDirectoryChangesW = ctypes.windll.kernel32.ReadDirectoryChangesW

ReadDirectoryChangesW.restype = ctypes.wintypes.BOOL
ReadDirectoryChangesW.errcheck = _errcheck_bool
ReadDirectoryChangesW.argtypes = (
    ctypes.wintypes.HANDLE,  # hDirectory
    LPVOID,  # lpBuffer
    ctypes.wintypes.DWORD,  # nBufferLength
    ctypes.wintypes.BOOL,  # bWatchSubtree
    ctypes.wintypes.DWORD,  # dwNotifyFilter
    ctypes.POINTER(ctypes.wintypes.DWORD),  # lpBytesReturned
    ctypes.POINTER(OVERLAPPED),  # lpOverlapped
    RDCW_CALLBACK_F  # FileIOCompletionRoutine # lpCompletionRoutine
)

请注意,如果我尝试将FileIOCompletionRoutine用作同步调用(将None传递给其最后两个参数),则可以正常工作。

当我尝试传递完成例程时出现问题:

#Routine implementation
def dir_change_callback(dwErrorCode,dwNumberOfBytesTransfered,p):
    print("dir_change_callback!")

#Get WINFUNC from factory
call2pass = RDCW_CALLBACK_F(dir_change_callback)

try:
    event_buffer = ctypes.create_string_buffer(BUFFER_SIZE)
    nbytes = ctypes.wintypes.DWORD()
    overlapped_read_dir = OVERLAPPED()    
    ReadDirectoryChangesW(handle, ctypes.byref(event_buffer),
                          len(event_buffer), recursive,
                          WATCHDOG_FILE_NOTIFY_FLAGS,
                          ctypes.byref(nbytes), 
                          ctypes.byref(overlapped_read_dir), call2pass)

调用有效,如果在程序执行期间我没有在受监视目录中执行更改,则它会在没有崩溃的情况下运行。 但是,如果发生更改,程序将崩溃:

hand = get_directory_handle(os.path.abspath("/test/"))
read_directory_changes(hand, False)
time.sleep(10)

崩溃报告

Nombre del evento de problema (Problem event name): APPCRASH
Nombre de la aplicación (Application Name):         python.exe
Nombre del módulo con errores (Module name):        ntdll.dll
Versión del módulo con errores (Module version):    6.1.7601.18247
Código de excepción (Exception code):               c0000005
Desplazamiento de excepción (Exception offset):     000337a2
Versión del sistema operativo (OS Version):         6.1.7601.2.1.0.256.48

我认为问题出现在WinApi尝试调用回调代码时,我怀疑我没有正确构建传递给调用的WINFUNC

另一方面,我在python ctypes documentation site发现了以下警告:

  

注意确保只要保留对CFUNCTYPE()对象的引用   它们是从C代码中使用的。 ctypes没有,如果你不这样做,他们可能会   垃圾收集,在回调时崩溃您的程序。

但是我保留了对可调用语的全局引用,所以,除非我错了,否则我认为这不是问题。

你曾经调用kernel32 windows调用传递python中实现的回调吗?我试图做的正确吗?

修改

我只是想出了导致崩溃的原因:event_buffer并非全局,而是在回调开始之前收集的。 虽然使全局修复崩溃,但消息"dir_change_callback!"未在进程终端中打印。我试图从回调中创建一个新文件并向其写一些消息,但结果仍然相同:这就像回调没有被执行。这很奇怪,因为上面描述的崩溃应该与回调执行相关联。

ctypes文档声明:

  

另外,请注意,如果在创建的线程中调用回调函数   在Python的控制范围之外(例如通过调用的外部代码)   回调),ctypes在每个上创建一个新的虚拟Python线程   调用。对于大多数用途,此行为是正确的,但这意味着   使用threading.local存储的值将无法生存   不同的回调,即使这些调用是从同一个C进行的   线程。

作为ctypes的完全新手,我理解这个虚拟线程是执行系统调用的进程的线程,并且可能在执行调用之前终止此进程。 这个问题Using threading lock in ctypes callback function似乎支持这个想法。我试图在我的代码中重现那个问题锁定方案,但锁定仍然是解锁的,就像试图打印的版本一样:回调似乎没有执行:

lck = threading.Lock()
lck.acquire()

def dir_change_callback(dwErrorCode,dwNumberOfBytesTransfered,p):
    lck.release()

0 个答案:

没有答案