Passing pointers to DLL function in Python

人走茶凉 提交于 2021-02-19 07:48:05

问题


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:


As [Python]: ctypes - A foreign function library for Python states, there are a few things to do, out of which the most important is setting argtypes and restype for the function object. SO is full of similar questions (I answered to several of them).

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

code.c:

#include <stdio.h>
#include <inttypes.h>

#define DIM 10  // Must be the SAME (well, or lower) than the constant defined in the Python code


__declspec(dllexport) int32_t __stdcall WriteFPGARegsC(int16_t *USBdev, int32_t *DUTSelect, int32_t *Array_RegsIn, int32_t *Array_RegsOut, int32_t *Array_RegEnable) {
    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, Array_RegsIn[0], Array_RegsOut[0], Array_RegEnable[0], ret);
    for (int i = 0; i < DIM; i++) {
        Array_RegsOut[i] *= *DUTSelect;
    }
    return ret;
}

code.py:

import sys
import ctypes


DIM = 10  # Should be 255, but for demo purposes keep it smaller
uint_arr = ctypes.c_uint * DIM
ushort_arr = ctypes.c_ushort * DIM
uint_p = ctypes.POINTER(ctypes.c_uint)
ushort_p = ctypes.POINTER(ctypes.c_ushort)


def main():
    dll = ctypes.WinDLL("USB_IO_for_VB6.dll")
    func = dll.WriteFPGARegsC
    func.argtypes = [ushort_p, uint_p, uint_p, uint_p, uint_p]
    func.restype = ctypes.c_uint

    usb_dev = ctypes.c_ushort(25)
    dut_select = ctypes.c_uint(2)
    regs_in = uint_arr(*range(20, 20 + DIM))
    regs_out = uint_arr(*range(30, 30 + DIM))
    reg_enable = uint_arr(*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 = func(ctypes.byref(usb_dev), ctypes.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} on {:s}\n".format(sys.version, sys.platform))
    main()

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
code.c
code.py

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

(py35x64_test) e:\Work\Dev\StackOverflow\q051289410>dir /b
code.c
code.obj
code.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" code.py
Python 3.5.4 (v3.5.4:3f56838, Aug  8 2017, 02:17:05) [MSC v.1900 64 bit (AMD64)] 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

@EDIT0:

  • 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


来源:https://stackoverflow.com/questions/51289410/passing-pointers-to-dll-function-in-python

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!