Python ctypes, dll function arguments - python

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

Related

Python ctypes how to deal with malloc/free?

I'm trying to convert a .cpp file to .py using the ctypes library.
Some basic operations are working, but I'm struggling when using typedef structs as well as malloc/free.
Here what I got:
module.h:
typedef struct
{
CHAR8 name[M_MODNAMELEN_A]; /**< Name of the module. */
} MODULE_NAME;
typedef struct
{
UINT16 countModules; /**< Count of module names.*/
MODULE_NAME * names; /**< Array of module names. */
} MODULE_LIST;
program.cpp:
UINT16 countModules;
SINT32 ret = GetCountModules(targetHandle, &countModules);
if(ret!=M1C_OK)
return ret;
MODULE_LIST modList;
modList.names = (MODULE_NAME*)malloc(countModules * sizeof(MODULE_NAME));
// do some calculations
free( modList.names );
program.py:
from ctypes import *
c_lib = CDLL('libXXYY.so')
class MODULE_NAME(Structure):
_fields_ = [
('name', c_byte)
]
def __repr__(self):
return '({0})'.format(self.name)
class MODULE_LIST(Structure):
_fields_ = [
('countModules', c_uint),
('names', POINTER(MODULE_NAME)) ### Is this conversion correct?
]
def __repr__(self):
return '({0}, {1})'.format(self.x, self.y)
count_modules = c_uint()
ret = c_long(c_lib.GetCountModules(target_handle, byref(count_modules)))
if ret.value != M1C_OK:
return ret
# Up to here everything works fine..
# Allocating memory for the list of module names
modList = MODULE_LIST()
modList.names = ### How to convert the malloc line?
## Some calcs
### How to convert the free( modList.names ); line?
The python runs ok up to the "modList.names = " line.. There I've tried several things (for example: modList.names = (MODULE_NAME)count_modules.value * sizeof(MODULE_NAME()) ) but all my tries have failed.
How should I translate the malloc line from cpp to python?
How should I translate the free line from cpp to python?
Are the translations of the typedef struct to class correct? Specially the one of "MODULE_LIST".
How should I translate the malloc line from cpp to python?
Usually, a DLL that returns something like MODULE_LIST does the malloc for you,
and provides a function to free it later, but if you have to fill one out yourself
an array type is created by multiplying a type by a size, then calling it to create an instance:
m = MODULE_LIST()
m.countModules = countModules
m.names = (MODULE_NAME * countModules)() # create instance of array
How should I translate the free line from cpp to python?
If allocated as above, Python will free it when there are no more references to the memory, such as when m above goes out of scope. You can call del m to explicitly dereference the memory. If m is the only reference, it will be freed.
Are the translations of the typedef struct to class correct? Specially the one of "MODULE_LIST".
No, CHAR8 name[M_MODNAMELEN_A]; is an array, so multiply a type by a length to make the correct array type:
M_MODNAMELEN_A = 100 # some constant that wasn't defined by OP
class MODULE_NAME(Structure):
_fields_ = ('name',c_char * M_MODNAMELEN_A),
MODULE_LIST has a 16-bit integer, so use the correct 16-bit type:
class MODULE_LIST(Structure):
_fields_ = (('countModules',c_uint16),
('names',POINTER(MODULE_NAME)))
To call the function correctly, best practice is to define .argtypes and .restype for the function to improve error checking:
# Assume "int GetCountModules(HANDLE handle, UINT16* pCount)" exists in dll.
dll = CDLL('./somedll')
dll.GetCountModules.argtypes = c_void_p, POINTER(c_uint16)
dll.GetCountModules.restype = c_int
# Make an instance to the output parameter and pass it by reference:
countModules = c_uint16()
ret = dll.GetCountModules(targetHandle, byref(countModules))

How to use ctypes argtypes c_ubytes for unsigned char *?

I'm new to Python and I'm trying to use C++ module as AES library: http://spaniakos.github.io/AES/classAES.html : AES.h.
It contains a module with byte defined as unsigned char:
void AES::set_IV(unsigned long long int IVCl){
memcpy(iv,&IVCl,8);
memcpy(iv+8,&IVCl,8);
IVC = IVCl;
}
void AES::get_IV(byte *out){
memcpy(out,&IVC,8);
memcpy(out+8,&IVC,8);
}
void AES::iv_inc(){
IVC += 1;
memcpy(iv,&IVC,8);
memcpy(iv+8,&IVC,8);
}
I've tried some code to use it in Python:
from ctypes import *
import ctypes
import numpy
import glob
libfile = glob.glob('build/*/AES.so')[0]
mylib = ctypes.cdll.LoadLibrary(libfile)
x = c_ulonglong(22222222)
y = numpy.ctypeslib.as_array(x)
iv = (c_ubyte * 16)()
my_iv = (ctypes.c_ubyte * 16)(0)
pointer_type =ctypes.POINTER(ctypes.c_ubyte)
pointer = ctypes.cast(my_iv, pointer_type)
mylib._ZN3AES6set_IVEy.argtypes = [numpy.ctypeslib.ndpointer(dtype = ctypes.c_ulonglong)]
mylib._ZN3AES6set_IVEy.restype = None
mylib._ZN3AES6set_IVEy(y) # the set_IV C++ func
mylib._ZN3AES6iv_incEv.restypes = None
mylib._ZN3AES6iv_incEv() # the iv_inc C++ func
print("OK")
mylib._ZN3AES6get_IVEPh.argtypes =[(pointer_type)]
mylib._ZN3AES6get_IVEPh(pointer) # the get_IV C++ func
print("OK")
and the result is: [enter image description here][1]
It means that the first two functions have run completely but the last Segmentation Fault means something wrong occurred. I've searched this online but still doesnt have any idea about this.
In addition, I do code with python3 on raspberry pi.
Could you tell me about this problem?
Thanks.
[1]:
https://i.stack.imgur.com/Jb3e3.png

Is there a way to receive the return value in Python through the OUT parameter in C++?

I have a C++ dll, one of the export function is defined as follows:
OPERATEMYSQLSTDCALL_API int __stdcall CheckMac(char * pcMac, OUT LPSTR errorInfo);
I use it in python, use the ctypes library,i read some of the information and call it as follws:
from ctypes import *
lib = WinDLL('OperateMysqlStdcall.dll')
CheckMac = lib.CheckMac
CheckMac.argtypes = [c_char_p, POINTER(c_wchar_p)]
CheckMac.restype = c_int
p1=c_wchar_p()
value = CheckMac('88888888',byref(p1));
print p1.value
but when i execute it,it return None,i'm sure the value "OUT LPSTR errorInfo" in C++ is not NULL,i print it in console and it shows Correctly.could anyone tells me why it can't work in python.Thank U very much!
The type of LPSTR is char* so you should use c_char_p for its type as well. As an output parameter, though, you need a writable string buffer. Ideally, the API should indicate the size of the buffer passed so a buffer overrun could be checked.
Here's some test DLL code:
#include <windows.h>
extern "C" __declspec(dllexport)
int __stdcall CheckMac(char* pcMac, LPSTR errorInfo)
{
strcpy(errorInfo, pcMac);
return 1;
}
And Python:
from ctypes import *
lib = WinDLL('test.dll')
CheckMac = lib.CheckMac
CheckMac.argtypes = [c_char_p, c_char_p]
CheckMac.restype = c_int
errorInfo = create_string_buffer(1024)
value = CheckMac('88888888',errorInfo);
print errorInfo.value
Output:
88888888

Python - ctypes and mutable buffers

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

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