Python ctypes: WindowsError при использовании указателя на функцию - PullRequest
3 голосов
/ 13 июня 2011

Я пытаюсь использовать 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 Ответ

2 голосов
/ 14 июня 2011

Как вы создаете и используете свой обратный вызов? Обратный вызов должен оставаться в области действия в течение всего срока его вызова. См. этот ответ . Эти


Редактировать

Разъяснение того, что я думаю, проблема ...

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

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

...