2

I am working on a project where I have to build a GUI for a development board with python with which I am new to as well. I am given the DLL which has required functions to communicate with the development board. I have LabVIEW equivalent function prototype which looks something like this:

int32_t WriteFPGARegsC(int16_t *USBdev, int32_t *DUTSelect, int32_t *Array_RegsIn, int32_t *Array_RegsOut, int32_t *Array_RegEnable);

And the same prototype also looks like this for Visual Basic:

Public Declare Function WriteFPGARegsC Lib "USB_IO_for_VB6.dll" (ByRef USBdev As Short, ByVal DUTSelect As Integer, ByRef Array_RegsIn As Integer, ByRef Array_RegsOut As Integer, ByRef Array_RegEnable As Integer) As Integer

I am trying to access this function with python instead of LabVIEW because of a lot of issues.

Last three parameters to be passed to the function needs to be an address to an array of 255 elements.

I have no clue how to pass pointers in function in python!!

I wrote the following shortcode to access this function in python:

USBdev = [0]
DUTSelect = [0]
RegsIn = [0] * 255
RegsOut = [0] * 255
RegEnable = [0] * 255

from ctypes import*

mydll = cdll.LoadLibrary("USB_IO_for_VB6.dll")
retmydll = mydll.WriteFPGARegsC(USBdev, DUTSelect, RegsIn, RegsOut, RegEnable)

After executing this code I get following error message:

Traceback (most recent call last):
  File "D:\Kushal\ATLASS\Source_code\Atlass.py", line 12, in <module>
    retmydll = mydll.WriteFPGARegsC(id(USBdev), id(DUTSelect), id(RegsIn),    id(RegsOut), id(RegEnable))
ValueError: Procedure called with not enough arguments (20 bytes missing) or wrong calling convention

Any help will be appreciated!! Thanks a lot!

1 Answer 1

3

Listing [Python.Docs]: ctypes - A foreign function library for Python.
Before everything, check [SO]: C function called from Python via ctypes returns incorrect value (@CristiFati's answer) for a common pitfall when working with CTypes (calling functions).

Anyway, I prepared a dummy example (don't mind the error proneness), it's just for illustrating the mechanism.

  • dll00.c:

    #if defined(_WIN32)
    #  define DLL00_EXPORT_API __declspec(dllexport)
    #else
    #  define DLL00_EXPORT_API
    #endif
    
    #include <stdint.h>
    #include <stdio.h>
    
    #define DIM 10  // Must be the SAME (well, or lower) than the constant defined in the Python code
    
    
    DLL00_EXPORT_API int32_t __stdcall WriteFPGARegsC(int16_t *USBdev, int32_t *DUTSelect, int32_t *ArrayRegsIn, int32_t *ArrayRegsOut, int32_t *ArrayRegEnable)
    {
        int32_t ret = 99;
        printf("From C:\n\t*USBdev: %d\n\t*DUTSelect: %d\n\tArray_RegsIn[0]: %d\n\tArray_RegsOut[0]: %d\n\tArray_RegEnable[0]: %d\n\n\tReturning: %d\n",
        *USBdev, *DUTSelect, ArrayRegsIn[0], ArrayRegsOut[0], ArrayRegEnable[0], ret);
        for (int i = 0; i < DIM; i++) {
            ArrayRegsOut[i] *= *DUTSelect;
        }
        return ret;
    }
    
  • code00.py:

    #!/usr/bin/env python
    
    import ctypes as cts
    import sys
    
    
    DIM = 10  # Should be 255, but for demo purposes keep it smaller
    UIntArr = cts.c_uint * DIM
    UShortArr = cts.c_ushort * DIM
    UIntPtr = cts.POINTER(cts.c_uint)
    UShortPtr = cts.POINTER(cts.c_ushort)
    
    
    def main(*argv):
        dll = cts.WinDLL("./USB_IO_for_VB6.dll")
        dll_func = dll.WriteFPGARegsC
        dll_func.argtypes = (UShortPtr, UIntPtr, UIntPtr, UIntPtr, UIntPtr)
        dll_func.restype = cts.c_uint
    
        usb_dev = cts.c_ushort(25)
        dut_select = cts.c_uint(2)
        regs_in = UIntArr(*range(20, 20 + DIM))
        regs_out = UIntArr(*range(30, 30 + DIM))
        reg_enable = UIntArr(*range(40, 40 + DIM))
    
        nth = 5
        print("Output register array: {:s}".format(" ".join(["{:d}".format(item) for item in regs_out])))
        print("\tIts {:d}th element: {:d}\n".format(nth, regs_out[nth]))
    
        ret = dll_func(cts.byref(usb_dev), cts.byref(dut_select), regs_in, regs_out, reg_enable)
        print("\nFunction returned: {:d}".format(ret))
        print("Output register array: {:s}".format(" ".join(["{:d}".format(item) for item in regs_out])))
        print("\tIts {:d}th element: {:d}\n".format(nth, regs_out[nth]))
    
    
    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)
    
    

Output:

(py35x64_test) e:\Work\Dev\StackOverflow\q051289410> "c:\Install\x86\Microsoft\Visual Studio Community\2015\vc\vcvarsall.bat" amd64

(py35x64_test) e:\Work\Dev\StackOverflow\q051289410> dir /b
dll00.c
code00.py

(py35x64_test) e:\Work\Dev\StackOverflow\q051289410> cl /nologo dll00.c /link /DLL /OUT:USB_IO_for_VB6.dll
dll00.c
  Creating library USB_IO_for_VB6.lib and object USB_IO_for_VB6.exp

(py35x64_test) e:\Work\Dev\StackOverflow\q051289410> dir /b
dll00.c
dll00.obj
code00.py
USB_IO_for_VB6.dll
USB_IO_for_VB6.exp
USB_IO_for_VB6.lib

(py35x64_test) e:\Work\Dev\StackOverflow\q051289410> "e:\Work\Dev\VEnvs\py35x64_test\Scripts\python.exe" ./code00.py
Python 3.5.4 (v3.5.4:3f56838, Aug  8 2017, 02:17:05) [MSC v.1900 64 bit (AMD64)] 064BIT on win32

Output register array: 30 31 32 33 34 35 36 37 38 39
        Its 5th element: 35

From C:
        *USBdev: 25
        *DUTSelect: 2
        Array_RegsIn[0]: 20
        Array_RegsOut[0]: 30
        Array_RegEnable[0]: 40

        Returning: 99

Function returned: 99
Output register array: 60 62 64 66 68 70 72 74 76 78
        Its 5th element: 70


Update #0

  • Modified answer to be as close as possible to the real scenario, and also to address questions in the comments

    • To make it clear, a CTypes array can be accessed as any other Python sequence
Sign up to request clarification or add additional context in comments.

9 Comments

Thanks for the reply. That's quite helpful. Just one issue: First two arguments are just pointers but not array pointers. Also when i tried to execute your code i code error: ret = func(usb_dev, dut_select, regs_in, regs_out, reg_enable) ValueError: Procedure called with not enough arguments (20 bytes missing) or wrong calling convention
Although argument names support your statement, their type (uint32_t*) doesn't as they are all pointers. Are you sure you posted the correct function signature? Just the eliminate the other factor, in the Python code (as it is in my answer) replace the line dll = ctypes.CDLL("USB_IO_for_VB6.dll") by dll = ctypes.WinDLL("USB_IO_for_VB6.dll") and see if there is any difference.
Worked after i changed to WinDLL!! Thanks a lot!!
One short question....How do I access those arrays that I passed to the function!!
I edited the question shortly after you asked. Take a look a the prints (I am accessing regs_out elements). and the comments. You can access it as a list, e.g. array[index] or as an iterable: for item in array:.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.