python ctypes issue on different OSes
In 99% of the cases, inconsistencies between arguments (and / or return) type inconsistencies are the cause (check [SO]: C function called from Python via ctypes returns incorrect value (@CristiFati's answer) for more details).
Always have [Python.Docs]: ctypes - A foreign function library for Python open when working with CTypes.
I found [GitHub]: erikssm/futronics-fingerprint-reader - (master) futronics-fingerprint-reader/ftrScanAPI.h (I don't know how different it's from what you currently have, but things you posted so far seem to match), and I did some changes to your code:
Define argtypes and restype for functions
Define missing types (for clarity only)
Some other insignificant changes (renames)
One other thing that I noticed in the above file, is a
#pragma pack(push, 1)
macro (check [MS.Docs]: pack for more details). For this structure it makes no difference (thanks @AnttiHaapala for the hint), as the 3 int (4 byte) members alignment doesn't change, but for other structures (with "smaller" member types (e.g. char, short)) you might want to add:_pack_ = 1
Your modified code (needless to say, I didn't run it as I don't have the .dll):
#!/usr/bin/env python
import ctypes as ct
from ctypes import wintypes as wt
# ...
class FTRSCAN_IMAGE_SIZE(ct.Structure):
# _pack_ = 1
_fields_ = (
("nWidth", ct.c_int),
("nHeight", ct.c_int),
("nImageSize", ct.c_int),
)
PFTRSCAN_IMAGE_SIZE = ct.POINTER(FTRSCAN_IMAGE_SIZE)
FTRHANDLE = ct.c_void_p
lib = ct.WinDLL("ftrScanAPI.dll") # provided by fingerprint scanner
ftrScanOpenDevice = lib.ftrScanOpenDevice
ftrScanOpenDevice.argtypes = ()
ftrScanOpenDevice.restype = FTRHANDLE
ftrScanGetImageSize = lib.ftrScanGetImageSize
ftrScanGetImageSize.argtypes = (FTRHANDLE, PFTRSCAN_IMAGE_SIZE)
ftrScanGetImageSize.restype = wt.BOOL
print("Open device and get device handle...")
h_device = ftrScanOpenDevice()
print("Handle is", h_device)
print("Get image size...")
image_size = FTRSCAN_IMAGE_SIZE(0, 0, 0)
if ftrScanGetImageSize(h_device, ct.byref(image_size)):
print("Get image size succeed...")
print(" W", image_size.nWidth)
print(" H", image_size.nHeight)
print(" Size", image_size.nImageSize)
else:
print("Get image size failed...")
Python load library from different platform (Windows, Linux or OS X)
you can use the ctypes.cdll
module to load the DLL/SO/DYLIB and the platform
module to detect the system you are running on.
a minimal working example would be like this:
import platform
from ctypes import *
# get the right filename
if platform.uname()[0] == "Windows":
name = "win.dll"
elif platform.uname()[0] == "Linux":
name = "linux.so"
else:
name = "osx.dylib"
# load the library
lib = cdll.LoadLibrary(name)
please note that you will need an 64 bit python interpreter to load 64 bit libraries and an 32 bit python interpreter to load 32 bit libraries
Wrong ctypes assignation
[Python 3.Docs]: ctypes - A foreign function library for Python.
You misspelled restypes (it should be restype). By doing so, restype is not initialized, and defaults to int (this wouldn't be a problem on 32bit), and you ran into:
- [SO]: Python ctypes cdll.LoadLibrary, instantiate an object, execute its method, private variable address truncated (@CristiFati's answer)
- [SO]: python ctypes issue on different OSes (@CristiFati's answer)
Besides that, there are several problems in the code:
- If the C function specifies a pointer (
double*
in this case), don't usectypes.c_void_p
(in argtypes or restype) to map it, as it might be too wide, use (for this case)ctypes.POINTER(ctypes.c_double)
instead For me this doesn't even compile (I wonder how were you able to run that code). I'm going to exemplify on XTrain only, but applies to YTrain and Xpredict as well. ctypes doesn't know to convert a Python list to a
ctypes.POINTER(ctypes.c_double)
(orctypes.c_void_p
), and the conversion must be made manually (to actypes.c_double
array):XTrain = [1.0, 1.0, 1.0, 3.0, 3.0, 3.0]
xtrain_ctypes = (ctypes.c_double * len(XTrain))(*XTrain)and pass xtrain_ctypes to the functions.
how to solve the os errors
Windows uses .dll and .exe for applications. The .so is compiled for a different system (like Linux).
Python ctypes uses regular Windows functions by the looks of it, and thus also only support .dll.
You need a version of gcc that produce Windows binaries to compile your C source code before you can load it on Windows.
There's a lot of different distributions of GCC for Windows, like for example Mingw. Just google "GCC for Windows".
Once you have a Windows based GCC installed, use that in place of your current gcc, but use libfun.dll for in place of libfun.so.
Python/ctypes file handle difference between Mac OS X and Ubuntu
Are you trying on a 32-bit Ubuntu versus a 64-bit OS/X? I think the issue is that your version of libc.fopen() returns a C "int", which is almost always a 32-bit value --- but the real fopen() returns a pointer. So on a 64-bit operating system, the c_file that you get is truncated to a 32-bit integer. On a 32-bit operating system, it works anyway because the 32-bit integer can be passed back to the fread() and fclose(), which will interpret it again as a pointer. To fix it, you need to declare the restype of libc.fopen().
(I can only recommend CFFI as an alternative to ctypes with saner defaults, but of course I'm partial there, being one of the authors :-)
C function called from Python via ctypes returns incorrect value
Listing [Python.Docs]: ctypes - A foreign function library for Python.
In order for everything to be properly converted (Python <=> C) when calling the function (residing in a .dll (.so)), 2 things need to be specified (leaving x86 (pc032) calling convention (Win) aside):
Argument types
Return type
In CTypes, this is achieved by specifying:
argtypes - a sequence (tuple, list) containing each function's argument (CTypes) type, in the order they appear in the function declaration (if the function only has one argument, there should be one element sequence)
restype - a single CTypes type (function's return type)
Side note: an alternative to the above, is prototyping foreign functions (CFUNCTYPE, WINFUNCTYPE, PYFUNCTYPE - check the Function prototypes section (in the URL at the beginning)).
Anyway:
Failing to specify
Misspelling (which basically is same thing as previous bullet)
any of them (where required (1)), will result in defaults being applied: all are treated (C89 style) as ints, which (on most systems) are 32 bits long.
This generates Undefined Behavior (2) (also applies when incorrectly specifying them), especially on 064bit CPU / OS (and Python process), where values of larger types (e.g. pointers) could be truncated (check [SO]: Maximum and minimum value of C types integers from Python (@CristiFati's answer) for details (limits -> size) regarding C integral types).
The displayed errors can be numerous and sometimes misleading.
You misspelled argtype (lacking an s at the end).
Correct that, and you should be fine
Example:
For a function func exported by libdll.dll (libdll.so) with the following header:
double func(uint32_t ui32,
float f,
long long vll[8],
void *pv,
char *pc,
uint8_t *pui8);
The Python equivalent would be:
import ctypes as cts
func = libdll.func
func.argtypes = (cts.c_uint32,
cts.c_float,
cts.c_longlong * 8,
cts.c_void_p,
cts.c_char_p,
cts.POINTER(cts.c_ubyte))
'''
Last 2 argument types are almost the same (1st is signed while 2nd is unsigned).
- 1st is used for NUL terminated strings (that work with strcpy, strlen, ...).
Python automatically converts them to bytes
- 2nd is used for buffers in general (which may contain NUL (0x00) characters)
'''
func.restype = cts.c_double
Some (more destructive) outcomes of the same (or very similar) scenario (there are many others):
[SO]: Python ctypes cdll.LoadLibrary, instantiate an object, execute its method, private variable address truncated (@CristiFati's answer)
[SO]: python ctypes issue on different OSes (@CristiFati's answer)
Footnotes
#1: Technically, there are situations where it's not required to specify them. But even then, it's best to have them specified in order to eliminate any possible confusion:
Function with no arguments:
function_from_dll.argtypes = ()
Function returning void:
function_from_dll.restype = None
#2: Undefined Behavior ([Wikipedia]: Undefined behavior) as the name suggests, is a situation when the outcome of a piece of code can't be "predicted" (or guaranteed by the C (C++) standard). The main cases:
Works as expected
Doesn't work as expected
Has some funny outputs / side effects
Crashes
The "beauty" of it is that sometimes it seems totally random, sometimes it "only reproduces" under some specific circumstances (different machines, different OSes, different environments, ...). Bottom line is that all of them are purely coincidental! The problem lies in the code (can be the current code (highest chances) or other code it uses (libraries, compilers)).
Access Violation when using Ctypes to Interface with Fortran DLL
I see 2 problems with the code (and a potential 3rd one):
argtypes (and restype) not being specified. Check [SO]: C function called from Python via ctypes returns incorrect value (@CristiFati's answer) for more details
This may be a consequence (or at least it's closely related to) the previous. I can only guess without the Fortran (or better: C) function prototype, but anyway there is certainly something wrong. I assume that for the input data things should be same as for the output data, so the function would take 2 arrays (same size), and the input one's elements would be
void *
s (since their type is not consistent). Then, you'd need something like (although I can't imagine how would Fortran know which element contains an int and which a double):in_data (ctypes.c_void_p * len(self.data))()
for idx, item in enumerate(self.data):
item.convert_to_ctypes()
in_data[index] = ctypes.addressof(item.c_val)Since you're on 032bit, you should also take calling convention into account (ctypes.CDLL vs ctypes.WinDLL)
But again, without the function prototype, everything is just a speculation.
Also, why "MyFunction_".lower()
instead of "myfunction_"
?
Getting segmentation fault when using ctypes on xlib
The error is because argtypes (and restype) not being specified, as described in [Python 3.Docs]: ctypes - Specifying the required argument types (function prototypes). There are many examples of what happens in this case, here are 2 of them:
- [SO]: python ctypes issue on different OSes (@CristiFati's answer)
- [SO]: Python ctypes cdll.LoadLibrary, instantiate an object, execute its method, private variable address truncated (@CristiFati's answer)
Declare them for each function, before calling it:
xlib.XOpenDisplay.argtypes = [ctypes.c_char_p]
xlib.XOpenDisplay.restype = ctypes.c_void_p # Actually, it's a Display pointer, but since the Display structure definition is not known (nor do we care about it), make it a void pointer
xlib.XDefaultRootWindow.argtypes = [ctypes.c_void_p]
xlib.XDefaultRootWindow.restype = ctypes.c_uint32
xss.XScreenSaverQueryInfo.argtypes = [ctypes.c_void_p, ctypes.c_uint32, ctypes.POINTER(XScreenSaverInfo)]
xss.XScreenSaverQueryInfo.restype = ctypes.c_int
Related Topics
Importerror: No Module Named Crypto.Cipher
How to Access Pandas Groupby Dataframe by Key
Matplotlib Savefig() Plots Different from Show()
Override a Method at Instance Level
Gradient Descent Using Python and Numpy
How to Convert a File to Utf-8 in Python
How to Set Default Python Version to Python3 in Ubuntu
How to Set Env Variable in Jupyter Notebook
How to Get JSON from Webpage into Python Script
Get Raw Post Body in Python Flask Regardless of Content-Type Header
Making an Asynchronous Task in Flask
List() Uses Slightly More Memory Than List Comprehension
How to Uninstall a Package Installed with Pip Install --User
What Is the Most Pythonic Way to Check If an Object Is a Number