【问题标题】:Python ctypes: WindowsError when using function pointerPython ctypes:使用函数指针时出现WindowsError
【发布时间】:2011-06-13 16:32:14
【问题描述】:

我正在尝试使用 Python 的 ctypes 来处理 DLL,但是当我尝试调用作为指向另一个函数的指针传递的函数时偶尔会遇到问题。

一点背景...我正在尝试使用Dokan(版本0.6.0)构建用户空间文件系统。有点松散地说,Dokan 基本上是 Windows 的FUSE。我已经使用 ctypes 包装了 dokan 头文件(类似于pydokan)。该头文件包含一个函数指针的定义,如下所示

typedef int (WINAPI *PFillFindData) (PWIN32_FIND_DATAW, PDOKAN_FILE_INFO);

它还包含另一个函数的原型

int (DOKAN_CALLBACK *FindFilesWithPattern) (
     LPCWSTR,
     LPCWSTR,
     PFillFindData,
     PDOKAN_FILE_INFO);

对应的ctypes定义如下所示

PFillFindData = ctypes.WINFUNCTYPE(ctypes.c_int,
                                   PWIN32_FIND_DATAW, 
                                   PDOKAN_FILE_INFO)

FindFilesWithPattern = ctypes.WINFUNCTYPE(ctypes.c_int,
                                          ctypes.c_wchar_p,
                                          ctypes.c_wchar_p,
                                          PFillFindData,
                                          PDOKAN_FILE_INFO)

后一个函数 (FindFilesWithPattern) 的实现必须调用它所传递的 FillFindData 函数。基本实现如下所示

def FindFilesWithPattern(self,
                         FileName,
                         SearchPattern,
                         FillFindData,
                         DokanFileInfo):
    if FileName == '\\':
        File = WIN32_FIND_DATAW(FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_READONLY, 
                                FILETIME(1, 1),
                                FILETIME(1, 1), 
                                FILETIME(1, 1), 
                                0, 
                                len(self.HelloWorldText), 
                                0, 
                                0, 
                                'Hello_World.txt',
                                'Hello_~1.txt')
        pFile = PWIN32_FIND_DATAW(File)
        FillFindData(pFile, DokanFileInfo)
        return 0
    else:
        return -ERROR_FILE_NOT_FOUND

当这个函数被调用时,我偶尔得到以下错误:

Traceback (most recent call last):
    File "_ctypes/callbacks.c", line 313, in 'calling callback function'
    File "src/test.py", line 385, in FindFilesWithPattern
        FillFindData(pFile, DokanFileInfo)
WindowsError: exception: access violation reading 0x0000000000000008

我很生气。乍一看,这看起来像是我正在尝试访问超出范围的内存。但是这个错误只是偶尔发生。有时一切正常,结果按预期返回。 (澄清一下,当我偶尔说的时候,我的意思是错误发生在程序的某些运行中,而不是在其他运行中。错误似乎在一次运行中发生或不一致发生。)

然后我想知道我是否得到了错误代码而不是内存地址。我发现here if 这是一个错误代码,那么它可能表示“内存不足”。当我查看系统监视器时,这似乎不是问题。我尝试过运行各种内存分析器,例如 Heapy 和 Meliae,但它们似乎都不适用于 Windows 64 位上的 Python 2.7。

我的下一个最佳猜测是,这是使用 64 位操作系统的问题。也许用于函数指针的类型不足以正确处理它。在进行了一些谷歌搜索之后,似乎其他人在 Win64 中使用 ctypes 时遇到了问题。我已经为 64 位架构构建了我的 Dokan 库。那我的python代码有问题吗?

任何帮助将不胜感激。我已经为此苦苦挣扎了一段时间。

可以在here 找到类似的帖子。不过,这似乎并不是非常相似。

注意:在 python 代码中,您会看到一些此处未定义的类型(例如 PDOKAN_FILE_INFO)。这些要么是结构,要么是指向结构的指针,为了简洁起见,我没有包括在内。

【问题讨论】:

  • “错误似乎在一次运行中出现或不一致出现。”这向我表明错误取决于输入。因此,这是内存问题的可能性很小;更有可能是您的算法无法处理的未清理/意外输入。
  • 我在 FindFilesWithPattern 函数中添加了一条 print 语句(print "function pointer: {}".format(PFillFindData))作为函数的第一行,错误就消失了。当我注释掉打印语句时,错误又回来了。有人能理解吗?
  • 如果我将上面评论中的打印语句更改为其他内容(不包括 .format(PFillFindData) 的任何内容),我会收到错误消息。

标签: python windows ctypes dokan


【解决方案1】:

你如何实例化和使用你的回调?回调必须在它可能被调用的生命周期内保持在范围内。见this answer。这些


编辑

澄清我认为的问题是什么......

我看到你必须实现一个 DOKAN_OPERATIONS 结构来实现像 FindFilesWithPattern 这样的回调,然后使用该结构调用 DokanMain 来挂载文件系统。当您填写结构时,您应该为回调创建一个类型,然后使用 Python 函数实例化回调。类似(伪代码):

#Create the callback pointer type
PFINDFILESWITHPATTERN = ctypes.WINFUNCTYPE(ctypes.c_int,
                                          ctypes.c_wchar_p,
                                          ctypes.c_wchar_p,
                                          PFillFindData,
                                          PDOKAN_FILE_INFO) 

# Implement in Python
def FindFilesWithPatternImpl(...):
    # implementation

# Create a callback pointer object
FindFilesWithPattern = PFINDFILESIWTHPATTERN(FindFilesWithPatternImpl)

# Create the required structure listing the callback
dokan_op = DOKAN_OPERATIONS(...,FindFilesWithPattern,...)

# Register the callbacks
DokanMain(byref(dokan_op))

在可以使用回调的生命周期内,您必须保留对对象 dokan_op 的引用。如果你实现类似于下面的挂载 Dokan:

def mount():
    # Create structure locally
    dokan_op = DOKAN_OPERATIONS(...)
    # Spin off thread to mount Dokan
    threading.Thread(DokanMain,args=(byref(dokan_op),))

mount()

一旦mount() 返回dokan_op 将被破坏。这是我最初的答案所描述的场景,会导致您看到的错误。我正在对您的问题进行理论化,因为我不知道其余代码是什么样的,但我认为您在 Python 中实现 FindFilesWithPattern 的方式是正确的,尤其是。因为你说它间歇性地工作。我遇到过您之前看到的故障,上述情况或类似情况会导致您看到的错误。

希望这会有所帮助...

【讨论】:

  • 从某种意义上说,我可能不应该将其称为回调。 PFillFindData 实际上只是一个 typedef。 FindFilesWithPattern 由操作系统从 DLL 中调用(例如,在“dir”命令之后)。当它被调用时,它会传递一个指向函数 (FillFindData) 的指针。 FillFindData 的实现实际上存在于 DLL 中,而不是我的 python 代码中。如果这有点令人困惑,我深表歉意。
  • 由于 PFillFindData 只是特定函数指针的 typedef,我最好只使用 c_void_p 吗?
  • 更新:我尝试使用 c_void_p 而不是 PFillFindData,但它给了我一个错误(TypeError: 'long' object is not callable)。
  • 所以FindFilesWithPattern 是您在Python 中实现的操作系统将调用的回调?而PFillFindData 是一个 C 函数您需要从该回调中的 Python 代码调用吗?
  • 感谢您对此进行更多调查!最初,我没有像您提到的那样坚持 DokanOperations 结构。在您的第一篇文章之后,我将它作为一个类的成员,然后我将其子类化为我的实际文件系统。我的代码的结构与您在此处发布的内容非常相似。我现在没有看到错误,所以希望这是解决方案。再次感谢!
猜你喜欢
  • 1970-01-01
  • 2014-01-23
  • 2018-10-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-22
  • 2013-02-16
相关资源
最近更新 更多