Python ctypes: WindowsError при использовании указателя на функцию
Я пытаюсь использовать ctypes Python для работы с DLL, но иногда я сталкиваюсь с проблемой, когда пытаюсь вызвать функцию, которая передается как указатель на другую функцию.
Немного предыстории... Я пытаюсь создать файловую систему пространства пользователя, используя Dokan (версия 0.6.0). В некотором смысле, Dokan - это FUSE для Windows. Я упаковал заголовочный файл dokan, используя ctypes (аналог 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, но ни один из них, похоже, не работает с Python 2.7 на 64-битной Windows.
Следующим моим лучшим предположением было то, что это проблема с использованием 64-битной ОС. Возможно, тип, используемый для указателя функции, недостаточен для правильной адресации. После некоторого поиска в Google, кажется, у других возникли проблемы с использованием ctypes в Win64. Я построил свою библиотеку Dokan для 64-битной архитектуры. Есть ли проблема с моим кодом на 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
будет разрушен. Это сценарий, описанный в моем первоначальном ответе, который может вызвать ошибку, которую вы видите. Я теоретизирую вашу проблему, поскольку не знаю, как выглядит остальная часть кода, но я думаю, как вы реализовали FindFilesWithPattern
в Python правильно, особенно так как вы говорите, что работает с перерывами. У меня была ошибка, с которой вы сталкивались раньше, и приведенный выше сценарий или что-то в этом роде приведет к ошибке, которую вы видите
Надеюсь это поможет...