Reputation: 497
I'm trying to work with ctypes, and I can't get the call to FormatMessage() to work properly.
Here's the code I have so far; I think the only issue is passing in a mutable buffer; I'm getting an ArgumentError from ctypes about 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
I'm getting an error on the lpBuffer argument saying it's a wrong type, but I've tried as many variations of passing in the buffer as I could think of. I've tried doing it similar to here: https://gist.github.com/CBWhiz/6135237 and setting FORMAT_MESSAGE_ALLOCATE_BUFFER then passing in a LPWSTR() byref, I've also tried changing the argtype, pointer and casting to a variety of LPWSTR(), c_char_p, etc, but no matter what I do it keeps complaining.
What's the proper syntax to get the function to execute properly? I know ctypes can be finnicky but I haven't found anything in the documentation to resolve the issue (I know the documentation uses prototype() but I'd like to do it this way for now)
Thanks
Upvotes: 0
Views: 2200
Reputation: 34260
Here's the argtypes
definition for FormatMessageW
(note "W" for 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
If FormatMessage
allocates the buffer, you have to instead pass a reference to lpBuffer
. Just cast
the reference to get around the TypeError
. Also, remember to call kernel32.LocalFree
to free the buffer:
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
Upvotes: 3