Python - ctypes and mutable buffers - python

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

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

Related

How to produce a Python ctypes instance

I am attempting to use the pyueye library to run ML cameras, and am running into problems with ctypes. One function requires an argument of type 'ctypes instance,' and despite trying every possible variation I cannot figure out how to produce this with the ctypes library. There is no documentation for python with this library, but the C documentation of the function I am trying to use is:
Syntax
INT is_SetAutoParameter (HIDS hCam, INT param, double* pval1, double* pval2)
Example 1
//Enable auto gain control:
double dEnable = 1;
int ret = is_SetAutoParameter (hCam, IS_SET_ENABLE_AUTO_GAIN, &dEnable, 0);
The code and subsequent error I am recieving in python is:
nRet = ueye.is_SetAutoParameter(hCam, ueye.IS_SET_ENABLE_AUTO_GAIN, ctypes.byref(ctypes.c_long(1)), ctypes.byref(ctypes.c_long(0)))
Error:
ret = _is_SetAutoParameter(_hCam, _param, ctypes.byref(pval1), ctypes.byref(pval2))
TypeError: byref() argument must be a ctypes instance, not 'CArgObject'
Any advice on ctypes instances? Thanks
EDIT: Minimal reproducible example
from pyueye import ueye
import ctypes
class Turfcam:
def main(self):
turfcam.take_photo()
def take_photo(self):
hCam = ueye.HIDS(0)
pval1 = ctypes.c_double(1)
pval2 = ctypes.c_double(0)
nRet = ueye.is_SetAutoParameter(hCam, ueye.IS_SET_ENABLE_AUTO_GAIN, ctypes.byref(pval1), ctypes.byref(pval2))
# Camera Init
nRet = ueye.is_InitCamera(hCam, None)
if __name__ == "__main__":
turfcam = Turfcam()
turfcam.main()
The pyueye library has some minimal documentation:
_is_SetAutoParameter = _bind("is_SetAutoParameter", [ctypes.c_uint, ctypes.c_int, ctypes.POINTER(ctypes.c_double), ctypes.POINTER(ctypes.c_double)], ctypes.c_int)
def is_SetAutoParameter(hCam, param, pval1, pval2):
"""
:param hCam: c_uint (aka c-type: HIDS)
:param param: c_int (aka c-type: INT)
:param pval1: c_double (aka c-type: double \*)
:param pval2: c_double (aka c-type: double \*)
:returns: success, or no success, that is the answer
:raises NotImplementedError: if function could not be loaded
"""
if _is_SetAutoParameter is None:
raise NotImplementedError()
_hCam = _value_cast(hCam, ctypes.c_uint)
_param = _value_cast(param, ctypes.c_int)
ret = _is_SetAutoParameter(_hCam, _param, ctypes.byref(pval1), ctypes.byref(pval2))
return ret
The wrapper is doing the byref, so only a ctypes object needs to be passed. The uEye docs say the parameters can be input or output parameters, so always create c_double objects and initialize them as needed for the function passed. Note that for an output parameter you must assign the object a name so it exists after the call to be queried. Input parameters can get away with passing ctypes.c_double(value) directly to the function, but for consistency I'd always assign names to the objects:
Example:
pval1 = ctypes.c_double()
pval2 = ctypes.c_double() # not used for output on IS_GET_ENABLE_AUTO_GAIN
nRet = ueye.is_SetAutoParameter(hCam, ueye.IS_GET_ENABLE_AUTO_GAIN, pval1, pval2)
print(pval1.value) # query the returned value
To complete the answer given by Mark Tolonen: the following code works OK (but probably you also want to check the error code nRet):
def take_photo(self):
hCam = ueye.HIDS(0)
# Camera Init (you must do this before using hCam !!)
nRet = ueye.is_InitCamera(hCam, None)
pval1 = ctypes.c_double(1)
pval2 = ctypes.c_double(0)
nRet = ueye.is_SetAutoParameter(hCam, ueye.IS_SET_ENABLE_AUTO_GAIN, pval1, pval2)

Python ctypes, dll function arguments

I have a DLL with a function
EXPORT long Util_funct( char *intext, char *outtext, int *outlen )
Looks like it expects char *intext, char *outtext, int *outlen.
I wass trying to define differnt data types in python so i can pass an argument, but no success so far.
from ctypes import *
string1 = "testrr"
#b_string1 = string1.encode('utf-8')
dll = WinDLL('util.dll')
funct = dll.Util_funct
funct.argtypes = [c_wchar_p,c_char_p, POINTER(c_int)]
funct.restype = c_char_p
p = c_int()
buf = create_string_buffer(1024)
retval = funct(string1, buf, byref(p))
print(retval)
The output is None, but I see some changes in p.
Could you please help me to define proper data types for the function.
This should work:
from ctypes import *
string1 = b'testrr' # byte string for char*
dll = CDLL('util.dll') # CDLL unless function declared __stdcall
funct = dll.Util_funct
funct.argtypes = c_char_p,c_char_p,POINTER(c_int) # c_char_p for char*
funct.restype = c_long # return value is long
p = c_int()
buf = create_string_buffer(1024) # assume this is big enough???
retval = funct(string1, buf, byref(p))
print(retval)
Thanks for all your answers!
I think i figured it out. Using not the smartest way but just trying/experimenting with different data types.
As this is not a common library and i had no information for it, maybe the sulution wont be very useful for others , but anyway.
Looks like the function process only one character at a time, because if i pass a word it returns only one encoded character.
So here it is :
from ctypes import *
buf = create_unicode_buffer(1024)
string1 = "a"
c_s = c_wchar_p(string1)
dll = CDLL('util.dll')
enc = dll.Util_funct
enc.argtypes = c_wchar_p, c_wchar_p, POINTER(c_int)
enc.restype = c_long # i don't think this type matters at all
p = c_int()
enc(c_s, buf, byref(p))
print(p.value)
print(buf.value)
the output is 1 and the simbol ^
Thanks again

python ctypes, call user32 and kernel32 func with arguments

I need to know how many arguments to pass in user32 and kernel32 functions
for example :
windll.kernel32.GetConsoleTitle()
I get Error :
:ValueError: Procedure probably called with not enough arguments (4 bytes missing)
Tiny update to above code:
>>> import ctypes
>>> MAX_LEN = 256
>>> buffer_ = ctypes.create_unicode_buffer(MAX_LEN)
>>> ctypes.windll.kernel32.GetConsoleTitleW(buffer_, MAX_LEN)
5
>>> buffer_.value
'Command Prompt - python'
You should read the API decscription. Here's the link for GetConsoleTitle:
https://learn.microsoft.com/en-us/windows/console/getconsoletitle
DWORD WINAPI GetConsoleTitle(
_Out_ LPTSTR lpConsoleTitle,
_In_ DWORD nSize
);
Update:
Here's a short demo of getting console window title:
import ctypes
MAX_BUFFER = 260
title_text_buffer = (ctypes.c_char * MAX_BUFFER)()
res = ctypes.windll.kernel32.GetConsoleTitleA(title_text_buffer, MAX_BUFFER)
title_text = title_text_buffer.value
print title_text

What process is using a given file?

I'm having trouble with one of my scripts, where it erratically seems to have trouble writing to its own log, throwing the error "This file is being used by another process."
I know there are ways to handle this with try excepts, but I'd like to find out why this is happening rather than just papering over it. Nothing else should be accessing that file at all. So in order to confirm the source of the bug, I'd like to find out what service is using that file.
Is there a way in Python on Windows to check what process is using a given file?
You can use Microsoft's handle.exe command-line utility. For example:
import re
import subprocess
_handle_pat = re.compile(r'(.*?)\s+pid:\s+(\d+).*[0-9a-fA-F]+:\s+(.*)')
def open_files(name):
"""return a list of (process_name, pid, filename) tuples for
open files matching the given name."""
lines = subprocess.check_output('handle.exe "%s"' % name).splitlines()
results = (_handle_pat.match(line.decode('mbcs')) for line in lines)
return [m.groups() for m in results if m]
Note that this has limitations regarding Unicode filenames. In Python 2 subprocess passes name as an ANSI string because it calls CreateProcessA instead of CreateProcessW. In Python 3 the name gets passed as Unicode. In either case, handle.exe writes its output using a lossy ANSI encoding, so the matched filename in the result tuple may contain best-fit characters and "?" replacements.
Please don't delete this answer in case I did anything wrong but give me a chance to correct it by leaving a comment. Thanks!
There is a better way than iterating through all PIDs (as was suggested in the comments) that involves performing a Windows API call to determine all handles on a given file. Please find below a code example which I have already posted for another question (however, cannot flag as duplicate since it does not have any accepted answers). Note that this only works for Windows.
import ctypes
from ctypes import wintypes
path = r"C:\temp\stackoverflow39570207.txt"
# -----------------------------------------------------------------------------
# generic strings and constants
# -----------------------------------------------------------------------------
ntdll = ctypes.WinDLL('ntdll')
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
NTSTATUS = wintypes.LONG
INVALID_HANDLE_VALUE = wintypes.HANDLE(-1).value
FILE_READ_ATTRIBUTES = 0x80
FILE_SHARE_READ = 1
OPEN_EXISTING = 3
FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
FILE_INFORMATION_CLASS = wintypes.ULONG
FileProcessIdsUsingFileInformation = 47
LPSECURITY_ATTRIBUTES = wintypes.LPVOID
ULONG_PTR = wintypes.WPARAM
# -----------------------------------------------------------------------------
# create handle on concerned file with dwDesiredAccess == FILE_READ_ATTRIBUTES
# -----------------------------------------------------------------------------
kernel32.CreateFileW.restype = wintypes.HANDLE
kernel32.CreateFileW.argtypes = (
wintypes.LPCWSTR, # In lpFileName
wintypes.DWORD, # In dwDesiredAccess
wintypes.DWORD, # In dwShareMode
LPSECURITY_ATTRIBUTES, # In_opt lpSecurityAttributes
wintypes.DWORD, # In dwCreationDisposition
wintypes.DWORD, # In dwFlagsAndAttributes
wintypes.HANDLE) # In_opt hTemplateFile
hFile = kernel32.CreateFileW(
path, FILE_READ_ATTRIBUTES, FILE_SHARE_READ, None, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS, None)
if hFile == INVALID_HANDLE_VALUE:
raise ctypes.WinError(ctypes.get_last_error())
# -----------------------------------------------------------------------------
# prepare data types for system call
# -----------------------------------------------------------------------------
class IO_STATUS_BLOCK(ctypes.Structure):
class _STATUS(ctypes.Union):
_fields_ = (('Status', NTSTATUS),
('Pointer', wintypes.LPVOID))
_anonymous_ = '_Status',
_fields_ = (('_Status', _STATUS),
('Information', ULONG_PTR))
iosb = IO_STATUS_BLOCK()
class FILE_PROCESS_IDS_USING_FILE_INFORMATION(ctypes.Structure):
_fields_ = (('NumberOfProcessIdsInList', wintypes.LARGE_INTEGER),
('ProcessIdList', wintypes.LARGE_INTEGER * 64))
info = FILE_PROCESS_IDS_USING_FILE_INFORMATION()
PIO_STATUS_BLOCK = ctypes.POINTER(IO_STATUS_BLOCK)
ntdll.NtQueryInformationFile.restype = NTSTATUS
ntdll.NtQueryInformationFile.argtypes = (
wintypes.HANDLE, # In FileHandle
PIO_STATUS_BLOCK, # Out IoStatusBlock
wintypes.LPVOID, # Out FileInformation
wintypes.ULONG, # In Length
FILE_INFORMATION_CLASS) # In FileInformationClass
# -----------------------------------------------------------------------------
# system call to retrieve list of PIDs currently using the file
# -----------------------------------------------------------------------------
status = ntdll.NtQueryInformationFile(hFile, ctypes.byref(iosb),
ctypes.byref(info),
ctypes.sizeof(info),
FileProcessIdsUsingFileInformation)
pidList = info.ProcessIdList[0:info.NumberOfProcessIdsInList]
print(pidList)

How to pass a 'file' to cpp function that expects 'Buffer' in Python ctypes?

I want to add 'Open_Buffer_Continue' function to the MediaInfoDLL.py ctypes wrapper, the bindings are here and
MediaInfoDLL.cs C# binding already implements this function so it's possible.
How do i pass the following:
file = open('./file.avi', 'rb')
to the mediainfo cpp Open_Buffer_Continue which expects:
( const ZenLib::int8u * Buffer,
size_t Buffer_Size
)
?
this is what i have so far:
MediaInfo_Open_Buffer_Init = MediaInfoDLL_Handler.MediaInfo_Open_Buffer_Init
MediaInfo_Open_Buffer_Init.argtype = [c_size_t, c_size_t]
MediaInfo_Open_Buffer_Init.restype = None
MediaInfo_Open_Buffer_Continue = MediaInfoDLL_Handler.MediaInfo_Open_Buffer_Continue
MediaInfo_Open_Buffer_Continue.argtype = [c_size_t, c_size_t] # NOT SURE HERE var 1
MediaInfo_Open_Buffer_Continue.restype = c_size_t
MediaInfo_Open_Buffer_Finalize = MediaInfoDLL_Handler.MediaInfo_Open_Buffer_Finalize
MediaInfo_Open_Buffer_Finalize.argtype = [c_void_p]
MediaInfo_Open_Buffer_Finalize.restype = None
I know nothing about mediainfo, but it looks like ZenLib::int8u* is a pointer to bytes, so the best thing to use would be:
MediaInfo_Open_Buffer_Continue.argtype = [c_char_p, c_size_t]
Then read your file into a string and pass it:
with open('./file.avi','rb') as f:
data = f.read()
MediaInfo_Open_Buffer_Continue(data,len(data))

Categories

Resources