I'm trying to use following function with ctypes and I do have troubles how declare all the parameters and variables right.
The Documentation of the C code is following,
/* global variables */
int main ()
char sDeviceSerialNumber[32];
FEUSB_GetScanListPara( 0, "Device-ID", sDeviceSerialNumber ) ;
sDeviceSerialNumber is supposed to be a return value of the function which I need in Python for further use.
Python code:
def FEUSB_GetScanListPara(iIndex, cPara):
libfeusb.FEUSB_GetScanListPara.argtypes = [ctypes.c_int,
ctypes.c_wchar_p,
ctypes.POINTER(ctypes.c_char_p)
]
libfeusb.FEUSB_GetScanListPara.restype = ctypes.c_int
iIndex = ctypes.c_int(iIndex)
cValue_buffer = ctypes.create_string_buffer(32)
cValue = ctypes.c_char_p(ctypes.addressof(cValue_buffer))
value = libfeusb.FEUSB_GetScanListPara(iIndex,
cPara,
ctypes.byref(cValue)
)
if __name__ == "__main__":
i = 0
RFID.FEUSB_GetScanListPara(i, "Device-ID")
When I call the function with the code above, I get an error code, FEUSB_ERR_UNKNOWN_PARAMETER, therefore I assume that I do not declare the parameters correctly.
Any input is appreciated!
EDIT 1
def FEUSB_GetScanListPara(iIndex, cPara):
libfeusb.FEUSB_GetScanListPara.argtypes = [ctypes.c_int,
ctypes.c_char_p,
ctypes.c_char_p
]
libfeusb.FEUSB_GetScanListPara.restype = ctypes.c_int
cValue = ctypes.create_string_buffer(32)
value = libfeusb.FEUSB_GetScanListPara(iIndex, cPara,
ctypes.byref(cValue))
print("1.0", cPara, "back value", " = ", value)
print("1.1", cPara, " = ", cValue.value)
print("######")
if __name__ == "__main__":
data = RFID.FEUSB_GetScanListPara(i, b"Device-ID")
Python Console:
FEUSB_ClearScanList = 0
FEUSB_Scan = 0
FEUSB_GetScanListSize = 1
Traceback (most recent call last):
File "C:\xxxxx\3.1_ObidRFID_test\OBID_RFID_06.py", line 265, in <module>
data = RFID.FEUSB_GetScanListPara(i, b"Device-ID")
File "C:\xxxxx\3.1_ObidRFID_test\OBID_RFID_06.py", line 89, in FEUSB_GetScanListPara
value = libfeusb.FEUSB_GetScanListPara(iIndex, cPara, ctypes.byref(cValue))
ArgumentError: argument 3: <class 'TypeError'>: wrong type
EDIT 2
working code
def FEUSB_GetScanListPara(iIndex, cPara):
libfeusb.FEUSB_GetScanListPara.argtypes = [ctypes.c_int,
ctypes.c_char_p,
ctypes.c_char_p
]
libfeusb.FEUSB_GetScanListPara.restype = ctypes.c_int
cValue = ctypes.create_string_buffer(32)
return_value = libfeusb.FEUSB_GetScanListPara(0, b'Device-ID',
cValue)
Your declaration of .argtypes would match the C prototype of:
int FEUSB_GetScanListPara(int, wchar_t*, char**)
You haven't provided the exact C prototype, but from your example of:
FEUSB_GetScanListPara( 0, "Device-ID", sDeviceSerialNumber ) ;
and knowing wchar_t* is not a common interface parameter, you probably actually have simple char* declarations like:
int FEUSB_GetScanListPara(int, const char*, char*);
I'm assuming the 2nd parameter is an input parameter and 3rd parameter is an output parameter. Note that c_char_p corresponds to a byte string so use b'DeviceID' for cPara. Also if you have to allocate the buffer, the 3rd parameter is unlikely to be char**. If the API itself is not returning a pointer, but filling out an already allocated buffer, char* and hence ctypes.c_char_p is appropriate. You correctly use create_string_buffer() for an output parameter.
Note you don't need to wrap iIndex in a c_int. From .argtypes, ctypes knows the 1st parameter is a c_int and converts it for you. That's also the default if no .argtypes is provided, but better to be explicit and provide .argtypes.
This code should work. I don't have the DLL to verify:
import ctypes as ct
libfeusb = CDLL('./FESUB') # assuming in same directory
libfeusb.FEUSB_GetScanListPara.argtypes = ct.c_int, ct.c_char_p, ct.c_char_p
libfeusb.FEUSB_GetScanListPara.restype = ct.c_int
cValue = ct.create_string_buffer(32)
ret = libfeusb.FEUSB_GetScanListPara(0, b'Device-ID', cValue)
if ret == 0:
print(cValue.value)
else:
print('error:',ret)
If you still have issues, edit your question with a minimal, reproducible example. Make sure to provide the real C prototype.
Related
I am new to both Python and ctypes the module. Trying to call C++ a function by loading a shared library. Here is the prototype of the function I want to call.
void foo_func(const char *binary, size_t binsz, size_t memsz, void *params, size_t paramssz, void *settings);
And here is the code I have written to call this function.
import ctypes
import pathlib
class virt_buff(ctypes.Structure):
_fields_ = [("x", ctypes.c_int), ("y", ctypes.c_int)]
if __name__ == "__main__":
libname = pathlib.Path().absolute() / "build/libfoo.so"
c_lib = ctypes.CDLL(libname)
func_param = virt_buff(7, 0)
with open("build/fib.bin", mode='rb') as file: # b is important -> binary
binary = file.read()
c_lib.foo_func(ctypes.c_char(binary), file.tell(), 0x9000 + (file.tell() & ~0xFFF), func_param, 4, NULL)
But when I run this code, it gives me the following output.
Traceback (most recent call last):
File "ct_test.py", line 32, in <module>
c_lib.foo_func(ctypes.c_char(binary), file.tell(), 0x9000 + (file.tell() & ~0xFFF), virt_param, 4, NULL)
TypeError: one character bytes, bytearray or integer expected
I tried a lot of things and nothing seems to work. Can anyone help me find out what the actual problem is?
The first error message is due to trying to pass a single character (c_char) and initializing it with a multiple character data blob. You want c_char_p for C char* instead. But there are other issues after that. Declaring .argtypes and .restype for your function correctly will help ctypes catch errors.
Make the following additional commented fixes:
import ctypes as ct
import pathlib
class virt_buff(ct.Structure):
_fields_ = [("x", ct.c_int), ("y", ct.c_int)]
if __name__ == "__main__":
libname = pathlib.Path().absolute() / "build/libfoo.so"
# Declare argument types and result type that match the C call for ctypes to error check
# void foo_func(const char *binary, size_t binsz, size_t memsz, void *params, size_t paramssz, void *settings);
c_lib = ct.CDLL(str(libname)) # on windows, CDLL(libname) failed to accept WindwowsPath.
c_lib.foo_func.argtypes = ct.c_char_p, ct.c_size_t, ct.c_size_t, ct.c_void_p, ct.c_size_t, ct.c_void_p
c_lib.foo_func.restype = None
func_param = virt_buff(7, 0)
with open("build/fib.bin", mode='rb') as file:
binary = file.read()
# Indent under with, so file.tell() is in scope,
# Pass address of structure using ct.byref() and size with ct.sizeof()
# Use Python None instead of C NULL.
c_lib.foo_func(binary, file.tell(), 0x9000 + (file.tell() & ~0xFFF), ct.byref(func_param), ct.sizeof(func_param), None)
Your parameter is almost certainly supposed to a POINTER to char, and not just a single char:
c_lib.foo_func(ctypes.c_char_p(binary), ...
I am trying to call the following function in a DLL, which (to my limited understanding), takes a callback as a parameter.
C# definitions:
//Error handler
public delegate int FPtrErrorHandler(int ErrorType, int MessageType, int WhomToInform, String ErrorMessage);
//Desired function to call
[ DllImport ( Globals.LibDLLPath, CallingConvention = CallingConvention.Cdecl ) ]
public static extern int OpenLib(String TempDirectory, Bool IfCleanTempDir, FPtrErrorHandler ErrorHandler);
Per this SO entry, I created the Python code below:
import ctypes as c
from ctypes import *
#c.WINFUNCTYPE(c.c_int, c.c_int, c.c_int, c.c_int, c.c_char_p)
def FPtrErrorHandler(ErrorIdentifier, ErrorMessageType, WhomToInform, ErrorMessage):
print(f'Error ID={ErrorIdentifier}, Type={ErrorMessageType}, WhomToInform={WhomToInform}, msg={ErrorMessage}')
return 0
lib = windll.LoadLibrary('path_to_dll')
_OpenLib = lib[163]
_OpenLib.restype = c_int
_OpenLib.argtypes = [c_char_p, c_bool, c_void_p]
def OpenLib(TempDirectory,IfCleanTempDir):
cb1 = FPtrErrorHandler
return _OpenLib(TempDirectory, IfCleanTempDir, cb1)
n = OpenLib(r:'c:\temp',c_bool(True))
Unfortunately, I get the dreaded message
ArgumentError: argument 1: <class 'TypeError'>: wrong type
Thanks in advance for helping me with this. I'm really happy to learn ctypes and how to use Python to interact with the amazing DLLs that are out there.
OMG, I think this might be an easy one.
If I change the call to the function from
n = OpenLib(r:'c:\temp',c_bool(True))
to
n = OpenLib(rb:'c:\temp',c_bool(True))
it seems to be working. I just added the b modifier in the string prefix.
I'v done researched a lot about this problem.. But there's no where and I couldn't find it. I'm trying to call double c structure by calling c dll.
My question is, did i right way to declare "Class Structure" in python? I couldn't think that i'm right on my way. Because even though the Functions that I want to call from dll, It didn't come output anything.
[Visual C++/C]
I did try to C Syntax code,
typedef sturct {
int nBoardNum;
struct{
char pBoardName[16];
int nBoardID;
}BOARDINDEX[8];
}AAPBOARDINFO, *PAAPBOARDINFO;
HANDLE AcapOpen(char* cpBoardName, int nBoardNo, int nCh)
[Python]
I changed Python Syntax like this.
import ctypes as c
class BOARDINDEX(c.Structure):
_field_ = [("nBoardName", c.c_char_p * 16),("nBoardID", c.c_int)]
class AAPBOARDINFO(c.Structure):
_field_ = [("nBoardNum", c.c_int), ("BOARDINDEX", BOARDINDEX * 8)]
AapLib2 = c.WinDLL("AapLib2.dll")
BoardName = ["ABC","FWD","HGW"]
BoardNo = 0
ch = 1
output = Open(BoardName, BoardNo, ch)
def Open(BoardName, BoardNo, ch)
func = AapLib2.AcapOpen
func.argtypes = [c.POINTER(BOARDINDEX),c.c_int, c.c_int]
func.restype = c.c_int
ref = BOARDINDEX()
res = func(c.byref(ref.nBoardName),BoardNo, ch)
return res
Nothing outcomes when call Open() function...
please consider my request and any answer would be great...
Everything you need to know, can be found in the [Python.Docs]: ctypes - A foreign function library for Python.
There are a couple of problems with the code:
Structure members are specified in the _fields_ (not _field_) attribute
char pBoardName[16] maps to ctypes.c_char * 16 (not c_char_p)
HANDLE should be mapped to wintypes.HANDLE
Function prototype differs between C and Python
Using globals like AapLib2 is best to be be avoided, but I left them unchanged as they are outside the question scope
#1. and #3. will generate Undefined Behavior! Check [SO]: C function called from Python via ctypes returns incorrect value (#CristiFati's answer) for more details.
Here's a modified version of your code. Needless to say that I didn't actually test it, as I don't have the .dll:
#!/usr/bin/env python
import ctypes as cts
import sys
from ctypes import wintypes as wts
class BOARDINDEX(cts.Structure):
_fields_ = [
("nBoardName", cts.c_char * 16),
("nBoardID", cts.c_int),
]
class AAPBOARDINFO(cts.Structure):
_fields_ = [
("nBoardNum", cts.c_int),
("BOARDINDEX", BOARDINDEX * 8),
]
def open_board(board_name, board_no, ch):
AcapOpen = aaplib2.AcapOpen
AcapOpen.argtypes = (cts.c_char_p, cts.c_int, cts.c_int)
AcapOpen.restype = wts.HANDLE
ref = BOARDINDEX(board_name, board_no) # Probably this line should be replaced by the 3 (commented) ones below (AcapGetBoardInfo prototype would have to be specified as well)
#abi = AAPBOARDINFO()
#AcapGetBoardInfo(cts.byref(abi))
#ref = abi.BOARDINDEX[0]
res = AcapOpen(ref.nBoardName, ref.nBoardID, ch)
return res
def main(*argv):
board_names = (
"ABC",
"FWD",
"HGW",
)
board_no = 0
ch = 1
aaplib2 = cts.WinDLL("AapLib2.dll")
output = open_board(board_names[0], board_no, ch)
print(output)
if __name__ == "__main__":
print("Python {:s} {:03d}bit on {:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")),
64 if sys.maxsize > 0x100000000 else 32, sys.platform))
rc = main(*sys.argv[1:])
print("\nDone.\n")
sys.exit(rc)
Let me know how this works out.
My DLL has the following function:
extern "C" __declspec(dllexport) bool __cdecl PcapOpen(unsigned long inSize, void * inData, unsigned long *outSize, void * outData, Function_Modes eMode)
The function will write data out through outSize and outData (meaning these need to be passed to the function with the expectation that my function inside the DLL will modify them)
In addition, Function_Modes is:
typedef enum Function_Modes { Mode_Unknown = -1, Mode_Preview = 0, Mode_Execute = 1, Mode_Query } Function_Modes;
For reference in Perl I referenced the function like so:
my %Data;
$Data{Interface} = $interface;
my ($inData, $inSize) = buildData(\%Data);
$$outSize = pack("L", 65536);
$outData = "\x00"x65536;
my $pPcapOpen = Win32::API->new($dllName, "PcapOpen", "NPPPI", "I", "_cdecl");
my $return = $pPcapOpen->Call($inSize, $inData, $$outSize, $outData, $eMode);
I created a small Python code that should run the function and show the data it returns, but the call while "appears to access the DLL", does not return (the python code just after the call is never called)
pPcapDLL = ctypes.WinDLL("Pcap Interface.dll")
LP_c_char = ctypes.POINTER(ctypes.create_string_buffer(65536).__class__)
obuf = ctypes.create_string_buffer(65536)
obuflen = ctypes.c_ulong(65536)
optr = LP_c_char(obuf)
PcapOpen = pPcapDLL["PcapOpen"]
PcapOpen.restype = ctypes.c_bool
PcapOpen.argtypes = [ctypes.c_ulong, ctypes.c_void_p, ctypes.POINTER(LP_c_char), ctypes.POINTER(ctypes.c_size_t), ctypes.c_int]
outSize = ctypes.c_ulong(65536)
LP_c_char = ctypes.POINTER(ctypes.create_string_buffer(65536).__class__)
outData = ctypes.create_string_buffer(65536)
outData_ptr = LP_c_char(outData)
print("Calling PcapClose")
returnValue = PcapClose(inSize, inData, ctypes.byref(outSize), ctypes.byref(outData_ptr), eMode)
print("returnValue: {}".format(returnValue)) # <-- This doesn't get called
There appears to be very little documentation on how to handle DLL functions that return data - just that byref should be used, but its not 100% clear how to allocate the data for these function to use them - so that could be my issue
Thank you for assisting.
As I see it there are two changes you need to make:
The C function you are attempting to call uses the cdecl calling convention, so you must access the DLL using ctypes.cdll, not ctypes.windll. ctypes.windll is used for the stdcall calling convention. See also the Python ctypes documentation.
The fourth argument to your function is a pointer, but you appear to be passing it a pointer to a pointer. Try replacing ctypes.byref(outdata_ptr) with ctypes.byref(outdata).
I got the following problem :
I load via c types the nicaiu.dll to control a NI-USB6218 Data Acquisition Pad and i have to call several function to initialise it (DAQmxCreateTask(), DAQmxCreateAIVoltageChan() and DAQmxCfgSampClkTiming() ).
The first two calls work but DAQmxCfgSampClkTiming() raises this error
Traceback (most recent call last):
File "C:/*********/Voltage.py", line 68, in <module>
values)
ctypes.ArgumentError: argument 6: <type 'exceptions.TypeError'>: Don't know how to convert
parameter 6
Parameter 6 should be uint64 see Doc
This is my function call:
DAQmx_Val_Rising = 10280 #see NIDAQmx.h
DAQmx_Val_FiniteSamps = 10178 # see NIDAQmx.h
values = uint64(40000) #numpy function
dll.DAQmxCfgSampClkTiming(taskHandle, "OnboardClock", c_float(4000.0), DAQmx_Val_Rising, DAQmx_Val_FiniteSamps,
values)
I also tried values = c_uint64(40000) but it did not work.
Edit1:
The dll is located in System32 folder (Win7)
dll = cdll.nicaiu
E.g this function call works (returnvalue = 0)
DAQmx_Val_Diff = 10106
DAQmx_Val_RSE = 10083
DAQmx_Val_Volts = 10348
returnvalue = dll.DAQmxCreateAIVoltageChan(taskHandle, "Dev1/ai1", taskName, DAQmx_Val_RSE,
c_float(-1.0),c_float(1.0), DAQmx_Val_Volts, None)
Edit2:
Added argtypes line
dll.DAQmxCfgSampClkTiming.argtypes = [c_int, c_char_p, c_float, c_int32, c_int32, c_uint64]
returnvalue = dll.DAQmxCfgSampClkTiming(taskHandle, None, c_float(4000.0), DAQmx_Val_Rising,
DAQmx_Val_FiniteSamps,values)
Still get Error Code -200077
Definition of this code is :
nierror code = -200077
Requested value is not a supported value for this property. The property value may be invalid
because it conflicts with another property.
I can't test it as I don't have that instrument, but I would start with something like:
samp_clk_timing = dll.DAQmxCfgSampClkTiming
samp_clk_timing.restype = c_int32
samp_clk_timing.argtypes = (TaskHandle,
c_char_p,
c_double,
c_int32,
c_int32,
c_uint64)
return_val = samp_clk_timing(taskHandle,
"OnboardClock",
c_double(4000),
c_int32(10106),
c_int32(10178),
c_uint64(40000))