Python ctypes:使用函数指针时出现WindowsError
我正在尝试使用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
我很困惑。乍一看,这看起来像是我试图访问超出范围的内存。但这个错误只是偶尔发生。有时一切正常,结果也如预期返回。(为了澄清,当我说偶尔时,我是指这个错误在程序的某些运行中出现,而在其他运行中没有。这个错误似乎在单次运行中是有规律的。)
我想,也许我得到的是一个错误代码,而不是一个内存地址。我在这里发现,如果这是一个错误代码,可能表示“内存不足”。但当我查看系统监视器时,这似乎不是问题。我尝试运行各种内存分析工具,比如Heapy和Meliae,但它们似乎都不支持Windows 64位上的Python 2.7。
我下一个猜测是,这可能是使用64位操作系统的问题。也许用于函数指针的类型不足以正确寻址。在网上搜索后,似乎其他人在Win64中使用ctypes时也遇到过问题。我已经为64位架构构建了我的Dokan库。那么我的Python代码有问题吗?
任何帮助都将非常感谢。我已经为此苦恼了一段时间。
可以在这里找到一个类似的帖子。但似乎并不是特别相似。
注意:在Python代码中,你会看到一些没有在这里定义的类型(例如PDOKAN_FILE_INFO)。那些要么是结构体,要么是指向结构体的指针,我为了简洁没有包含。
1 个回答
你是如何实例化和使用你的回调函数的?回调函数必须在它可能被调用的整个生命周期内保持有效。可以参考这个回答。
编辑
我想澄清一下我认为的问题...
我看到你需要实现一个 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
的方式是正确的,尤其是你说它偶尔能工作。我之前也遇到过你看到的失败情况,上述场景或类似的情况会导致你看到的错误。
希望这能帮到你...