Python ctypes:使用函数指针时出现WindowsError

3 投票
1 回答
1817 浏览
提问于 2025-04-16 19:29

我正在尝试使用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

我很困惑。乍一看,这看起来像是我试图访问超出范围的内存。但这个错误只是偶尔发生。有时一切正常,结果也如预期返回。(为了澄清,当我说偶尔时,我是指这个错误在程序的某些运行中出现,而在其他运行中没有。这个错误似乎在单次运行中是有规律的。)

我想,也许我得到的是一个错误代码,而不是一个内存地址。我在这里发现,如果这是一个错误代码,可能表示“内存不足”。但当我查看系统监视器时,这似乎不是问题。我尝试运行各种内存分析工具,比如HeapyMeliae,但它们似乎都不支持Windows 64位上的Python 2.7。

我下一个猜测是,这可能是使用64位操作系统的问题。也许用于函数指针的类型不足以正确寻址。在网上搜索后,似乎其他人在Win64中使用ctypes时也遇到过问题。我已经为64位架构构建了我的Dokan库。那么我的Python代码有问题吗?

任何帮助都将非常感谢。我已经为此苦恼了一段时间。

可以在这里找到一个类似的帖子。但似乎并不是特别相似。

注意:在Python代码中,你会看到一些没有在这里定义的类型(例如PDOKAN_FILE_INFO)。那些要么是结构体,要么是指向结构体的指针,我为了简洁没有包含。

1 个回答

2

你是如何实例化和使用你的回调函数的?回调函数必须在它可能被调用的整个生命周期内保持有效。可以参考这个回答


编辑

我想澄清一下我认为的问题...

我看到你需要实现一个 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 的方式是正确的,尤其是你说它偶尔能工作。我之前也遇到过你看到的失败情况,上述场景或类似的情况导致你看到的错误。

希望这能帮到你...

撰写回答