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 правильно, особенно так как вы говорите, что работает с перерывами. У меня была ошибка, с которой вы сталкивались раньше, и приведенный выше сценарий или что-то в этом роде приведет к ошибке, которую вы видите

Надеюсь это поможет...

Другие вопросы по тегам