2013-09-19 4 views
-1

Я пытаюсь работать с ctypes, и я не могу получить вызов FormatMessage() для правильной работы.Python - ctypes и изменяемые буферы

Вот код, который у меня есть до сих пор; Я думаю, что единственная проблема заключается в передаче изменяемого буфера; Я получаю ArgumentError от ctypes о lpBuffer

import ctypes 
from ctypes.wintypes import DWORD 

def main(): 
    fm = ctypes.windll.kernel32.FormatMessageA 
    fm.argtypes = [DWORD,DWORD,DWORD,DWORD,ctypes.wintypes.LPWSTR(),DWORD] 

    dwFlags = DWORD(0x1000) # FORMAT_MESSAGE_ALLOCATE_BUFFER |FORMAT_MESSAGE_FROM_SYSTEM 
    lpSource = DWORD(0) 
    dwMessageId = DWORD(0x05) 
    dwLanguageId = DWORD(0) 
    #buf = ctypes.wintypes.LPWSTR() 
    #lpBuffer = ctypes.byref(buf) 
    lpBuffer = ctypes.create_string_buffer(512) 
    nSize = DWORD(512) 

    res = fm(dwFlags,lpSource,dwMessageId,dwLanguageId,lpBuffer,nSize) 
    print res 

Я получаю сообщение об ошибке на аргумент lpBuffer говоря, что это неправильный тип, но я пробовал так много вариантов прохождения в буфере, как я мог думать из. Я пробовал сделать это аналогично здесь: https://gist.github.com/CBWhiz/6135237 и установить FORMAT_MESSAGE_ALLOCATE_BUFFER, а затем передать в файле LPWSTR() byref, я также попытался изменить argtype, указатель и кастинг на различные файлы LPWSTR(), c_char_p и т. Д., Но неважно что я делаю, он продолжает жаловаться.

Каков правильный синтаксис для правильной работы функции? Я знаю, что ctypes может быть finnicky, но я ничего не нашел в документации, чтобы решить проблему (я знаю, что документация использует прототип(), но я хотел бы сделать это таким образом в настоящее время)

Благодаря

+0

Вы могли бы рассмотреть возможность использования 'win32api.FormatMessage' - это, наверное, проще, чем при использовании' ctypes', если вы можете вообще. –

+1

Просто угадайте, как использовать 'create_unicode_buffer' вместо' create_string_buffer', так как это WCHAR? – PaulMcG

+0

@PaulMcGuire: Гарантировано ли, что 'WCHAR' и' wchar_t' являются одним и тем же типом? В Windows я уверен, что 'WCHAR' всегда' unsigned short', несмотря ни на что, но я считаю, что 'wchar_t' может быть 32-битным в зависимости от настроек сборки. – abarnert

ответ

1

Вот argtypes определение FormatMessageW (примечание «W» для Unicode):

import ctypes 
from ctypes import wintypes 

fm = ctypes.windll.kernel32.FormatMessageW 
fm.argtypes = [ 
    wintypes.DWORD, # dwFlags 
    wintypes.LPCVOID, # lpSource 
    wintypes.DWORD, # dwMessageId 
    wintypes.DWORD, # dwLanguageId 
    wintypes.LPWSTR, # lpBuffer 
    wintypes.DWORD, # nSize 
    wintypes.LPVOID, # Arguments (va_list *) 
] 

FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x100 
FORMAT_MESSAGE_FROM_SYSTEM = 0x1000 

Если FormatMessage выделяет буфер, вы должны вместо этого передать ссылку на lpBuffer. Просто cast ссылка, чтобы обойти TypeError. Кроме того, не забудьте позвонить kernel32.LocalFree чтобы освободить буфер:

def main(): 
    dwFlags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER 
    lpSource = None 
    dwMessageId = 5 
    dwLanguageId = 0  
    lpBuffer = wintypes.LPWSTR() 
    nSize = 0 # minimum size 
    Arguments = None 

    if not fm(dwFlags, lpSource, dwMessageId, dwLanguageId, 
       ctypes.cast(ctypes.byref(lpBuffer), wintypes.LPWSTR), 
       nSize, Arguments): 
     raise ctypes.WinError() 

    msg = lpBuffer.value.rstrip() 
    ctypes.windll.kernel32.LocalFree(lpBuffer) 

    return msg 
+0

Этот код фактически совпадает с [the gist] (https://gist.github.com/CBWhiz/6135237), с которым он связан, и утверждает, что попробовал. Вы не исправили или не объяснили какие-либо ошибки в этом коде (поскольку, насколько я могу судить, этот код работает точно так же, как ваш). Итак, какова бы ни была проблема OP, я не уверен, что это действительно решает, если только план не будет бросать на него примеры, пока он не копирует и не вставляет один, не нарушая его. – abarnert

+0

@abarnert: Код в вопросе неверно использует 'FormatMessageA' с типами данных Unicode. Он также имеет неполные «argtypes» (это 'stdcall', поэтому вам нужны все аргументы). Указателю необходимо передать 'byref', чтобы заполнить адрес выделенного буфера, для которого требуется' cast', и без 'LocalFree' это утечка памяти. Да, этот ответ похож на суть, но не использует 'argtypes', поэтому у него нет проблемы с TypeError. – eryksun

+0

Дело в том, что код из сущности, с которой он связан, [здесь] (https://gist.github.com/CBWhiz/6135237), использует 'FormatMessageW' и использует' byref' правильно и так далее, и так далее если вы запустите этот код, он работает. Предоставление ему другой версии, которая также работает, хорошо, но я не думаю, что этого достаточно, чтобы помочь ему понять, что он сделал неправильно. (Это не критика вас - я не думаю, что есть какой-то способ помочь ему понять, что он сделал не так, если он не покажет нам, что он на самом деле сделал.) – abarnert