2

I am trying to work with a 3rd party DLL file to control a hardware (translation stage in my case). I have windows 10, 64 bit python (with Jupyter notebook).

I have imported the correct version (64-bit) of dll file like this:

import ctypes, sys
PiUsbDll = ctypes.WinDLL("PiUsb")

I have a copy of C++ header file for the DLL. Therefore, I know what is the list of functions and what arguments they take. I have defined the restype and argtype for each of the functions in the DLL file like this (# lines show the function def as per C++ header file)

# void * __stdcall piConnectMotor(int * ErrorNum, int SlideSerialNum);
PiUsbDll.piConnectMotor.restype = ctypes.c_void_p
PiUsbDll.piConnectMotor.argtypes = (ctypes.POINTER(ctypes.c_int),ctypes.c_int)

# void __stdcall piDisconnectMotor(void * devicePtr);
PiUsbDll.piDisconnectMotor.restype = None
PiUsbDll.piDisconnectMotor.argtypes = ctypes.c_void_p, #, defines a tuple

# int __stdcall piHomeMotor(int Velocity, void * devicePtr);
PiUsbDll.piHomeMotor.restype = ctypes.c_int
PiUsbDll.piHomeMotor.argtypes = (ctypes.c_int, ctypes.c_void_p)

# int __stdcall piRunMotorToPosition( int Position, int Velocity, void * devicePtr);
PiUsbDll.piRunMotorToPosition.restype = ctypes.c_int
PiUsbDll.piRunMotorToPosition.argtype = (ctypes.c_int, ctypes.c_int, ctypes.c_void_p)


.
.
.

I have defined/initialized variables with ctypes:

SlideSerialNum = ctypes.c_int(123) #serial number provided from manufacturer
Velocity = ctypes.c_int(10) # Allowed range 1-12
Position = ctypes.c_int(500) #Allowed range 0-1900
devicePtr = ctypes.c_void_p()
ErrorNum = ctypes.c_int() 
.
.
.

And then I call the functions like this (have omitted few intermediate lines of codes)

# Connect to motor
devicePtr = PiUsbDll.piConnectMotor(ctypes.byref(ErrorNum),SlideSerialNum)

# Homing the motor
ErrorNum.value = PiUsbDll.piHomeMotor(Velocity,devicePtr)

#  Run to a position
ErrorNum.value = PiUsbDll.piRunMotorToPosition(Position,Velocity,devicePtr)
.
.
.

Here, connecting and homing work fine all the times but run to a position fails with the following error:

ArgumentError: argument 3: <class 'OverflowError'>: int too long to convert

So, looks like devicePtr (a void*) is having inconsistent conversion between python type and ctypes. I hoped that defining argtype would take care of this but does not look so. One solution would be to fall back to 32-bit python version (alongwith 32-bit DLL files) where this problem does not appear but I would love to stick to 64 bit if possible.

  • 1
    `So, looks like devicePtr (a void*) is having inconsistent conversion between python type and ctypes` -- If python assumes that `sizeof(void *) == sizeof(int)`, that assumption is probably wrong for 64-bit programs. A 64-bit program has pointers that are (usually) 8-bytes in size, and an `int` (usually) stays as 4 bytes. Even in C++, pointers and `int` are treated as different things, and good C++ programs have `static_assert`'s in the code to ensure that pointers and integers have the same size for a program to work properly. – PaulMcKenzie Aug 16 '20 at 00:20
  • I thought I am running in same problem as pointed [here](https://stackoverflow.com/questions/23522055/error-when-unload-a-64bit-dll-using-ctypes-windll). I am very new to programming so not 100% sure but likely there is a better way than `argtypes` to ensure a consistent type conversion without `overflow`. – optomanishk Aug 16 '20 at 00:31
  • @PaulMcKenzie, regarding `static_assert`, I am not sure. The library is in binary format and I can only access header file and an example C++ client file which makes use of the DLL. I just want to be sure that I am not doing something terribly wrong with how I am using the DLL in python example shown in my question. If the issue is not fixable from python side, I shall just move on as I have no control over the 3rd party DLL. – optomanishk Aug 16 '20 at 00:50
  • The `static_assert` is used by C++ programmers to ensure that the program won't even build if the pointer size is different than the `int` size. I am no Python expert, but I don't think such a utility exists for Python, where you can say "don't even attempt to produce a program if this condition is not true". Lines in programs such as `static_assert(sizeof(void *) == sizeof(int), "pointer size is different than int size. Can't build")` will appear in C++ programs, to make sure that the program, library, whatever will fail to build if that condition is not met. – PaulMcKenzie Aug 16 '20 at 00:53
  • Thanks @PaulMcKenzie for explaining the C++ side of story. This is a very old and not so nicely written DLL file (from a 3rd party) so I am inclined to believe that `static_assert` was not used in here. I shall likely move to 32-bit version of DLL and Python where this issue does not occur. – optomanishk Aug 16 '20 at 01:12
  • `PiUsbDll.piRunMotorToPosition.argtype = (ctypes.c_int, ctypes.c_int, ctypes.c_void_p)` it's *argtype**s***. If it's the only error, this is a duplicate of https://stackoverflow.com/questions/58610333/c-function-called-from-python-via-ctypes-returns-incorrect-value. – CristiFati Aug 18 '20 at 07:36
  • If no reply in the next day, I'm going to mark the question as so. – CristiFati Aug 18 '20 at 20:51

2 Answers2

1

I figured the problem was related to the 64 bit 3rd party DLL files in my case. I have switched to 32 bit DLL files where the same python code works.

1

When you call the function, try "casting" it to c_void_p, a la

devicePtr = c_void_p(PiUsbDll.piConnectMotor(ctypes.byref(ErrorNum),SlideSerialNum))

That's what worked for me, facing a similar problem on x64 Windows with x64 python. (You can do a type(devicePtr) afterwards, to check that the type is correct). I'm not sure why explicitly setting the restype isn't "good enough" in this situation...

R. Li
  • 56
  • 3