I have a vendor-provided DLL for connecting to a piece of lab equipment. The DLL has a function
long search_devices(char **serial_number_list, uint32_t *dev_count)
that I'm trying to access from Python. What I've done is
import ctypes
usb_dllpath = r"path_to_dll"
usb_dll = ctypes.CDLL(usb_dllpath)
usb_dll.search_devices.argtypes = [ctypes.POINTER(ctypes.c_char_p), ctypes.POINTER(ctypes.c_uint32)]
usb_dll.search_devices.restype = ctypes.c_long
serial_list = ctypes.c_char_p()
num_devices = ctypes.c_uint32(99)
status_search = usb_dll.search_devices(ctypes.byref(serial_list), ctypes.byref(num_devices))
The result is that the first run gives an error OSError: exception: access violation writing 0x0000000000000000
, doesn't change the number of devices found from 99, and leaves the status of the search as 0 (failures should return negative numbers per the DLL's source code), while also apparently locking up the instrument and making the device not appear in the example GUI. Subsequent runs of the script throw no errors but set the value of num_devices to 0 while still leaving the status as 0.
Is this a problem in my python script due to, for example, a null pointer as indicated by the error? Or should I assume there is something going wrong in the DLL?
The relevant C function in the DLL is below, with #defines replaced by their values (search_devices
is just a wrapper for UsbSearchDevices
). The function returns a long to describe whether an error occurred and modifies the list of serial numbers and number of found devices in memory using the pointers I pass in.
long search_devices(char **serial_number_list, uint32_t *dev_count)
{
return UsbSearchDevices(serial_number_list, dev_count);
}
long UsbSearchDevicesLV(char *serialNumberList, int *numberDevices)
{
libusb_device_handle *deviceHandle = NULL;
int deviceCount = 0;
uint8_t string_usb[9];
char *list;
libusb_device **devList;
libusb_context *ctx = NULL;
struct libusb_device_descriptor desc;
int r, i;
int status = 0;
ssize_t count;
list = (char*)malloc(160);
if (list == NULL) return 0;
list[0] = '\0';
r = libusb_init(&ctx);
if (r < 0) return -12;
count = libusb_get_device_list(ctx, &devList);
if (count < 0)
{
libusb_free_device_list(devList, 1);
return -2;
}
for (i = 0; i < count; i )
{
status = libusb_get_device_descriptor(devList[i], &desc);
if (desc.idVendor == 0x277C && desc.idProduct == 0x0026)
{
status = libusb_open(devList[i], &deviceHandle);
if (status == 0)
{
libusb_get_string_descriptor_ascii(deviceHandle, desc.iSerialNumber, string_usb, sizeof(string_usb));
strncat(list, (char *)string_usb, 8 1);
libusb_close(deviceHandle);
deviceCount ;
}
}
}
strcpy(serialNumberList, list);
libusb_free_device_list(devList, 1);
libusb_exit(ctx);
free(list);
*numberDevices = deviceCount;
return 0;
}
The provided example C code that uses these functions has its relevant part below:
device_list = (char**)malloc(sizeof(char*)*128); // 128ports to search
for (i=0;i<128; i )
device_list[i] = (char*)malloc(sizeof(char)*8);
status = search_devices(device_list, &num_of_devices);
CodePudding user response:
The C usage example shows how to call the function. Below is a test function I used that fills out data as described:
test.c
#include <stdint.h>
#include <stdio.h>
__declspec(dllexport)
long search_devices(char **serial_number_list, uint32_t *dev_count) {
for(int i = 0; i < 5; i)
sprintf_s(serial_number_list[i], 8, "SNd", i);
*dev_count = 5;
return 0;
}
Here is the code to call it that should work for your DLL as well:
test.py
import ctypes as ct
dll = ct.CDLL('./test')
dll.search_devices.argtypes = ct.POINTER(ct.c_char_p), ct.POINTER(ct.c_uint32)
dll.search_devices.restype = ct.c_long
serial_list = (ct.c_char_p * 128)()
for i in range(128):
serial_list[i] = ct.cast(ct.create_string_buffer(8), ct.c_char_p)
num_devices = ct.c_uint32()
status_search = dll.search_devices(serial_list, ct.byref(num_devices))
devices = serial_list[:num_devices.value]
print(devices)
Output:
[b'SN00000', b'SN00001', b'SN00002', b'SN00003', b'SN00004']