I'm having a problem with my structure generation to allow the use of a c Library within Python - I'm hoping someone can point out the errors in my code.
C Prototype:
const char *DSpotterVerInfo(char *lpchLicenseFile, VerInfo *lpVerInfo, INT *lpnErr);
C Struct Definition:
typedef struct _VerInfo
{
const char *SDKName;
const char *SDKVersion;
const char *SDKType;
const char *Date;
const char *LType;
BOOL bTrialVersion;
} VerInfo;
My code:
class DSVerInfo(ctypes.Structure):
# This works (at least no ugly errors, but I can't get to the structure data..)
#_fields_ = [('Name',ctypes.c_char_p)]
# This defintion causes an error and a segdump
_fields_ = [ \
('Name', ctypes.c_char_p), \
('Version', ctypes.c_char_p), \
('SDKType', ctypes.c_char_p), \
('Date', ctypes.c_char_p), \
('LicType', ctypes.c_char_p), \
('Trial', ctypes.c_bool) \
]
def __init__(self):
self.Name = cast(ctypes.create_string_buffer(str.encode("")),ctypes.c_char_p)
self.Version = cast(ctypes.create_string_buffer(str.encode("")),ctypes.c_char_p)
self.SDKType = cast(ctypes.create_string_buffer(str.encode("")),ctypes.c_char_p)
self.Date = cast(ctypes.create_string_buffer(str.encode("")),ctypes.c_char_p)
self.LicType = cast(ctypes.create_string_buffer(str.encode("")),ctypes.c_char_p)
self.Trial = c_bool()
libc.MyLibVerInfo.argtypes = (ctypes.c_char_p,DSVerInfo,ctypes.POINTER(c_int))
err_no = ctypes.c_int()
Lic_ct = ctypes.create_string_buffer(str.encode(License_file))
VerInfo = DSVerInfo()
result = libc.DSpotterVerInfo(Lic_ct,VerInfo,err_no)
print("Result:\n",result.decode('utf-8'),"Error No:", err_no.value)
print("Version Size: ", sizeof(VerInfo))
print(VerInfo) #Not really any use.. just an object until I can use VerInfo.x
Here is a sample of the output when it fails (from the print of the errorNo):
Error No: 155394711
-155394711
Version Size: 48
<__main__.DSVerInfo object at 0x1093287c0>
Done
Segmentation fault: 11
f01898e9b5db0000:Test rpm$
Solution to Question:
From Mark's comment above the problem in the code is that I was not passing DSVerInfo as a pointer. Update the line calling the C funciton to the following and everything works as expected.
# Non-working code:
#libc.DSpotterVerInfo.argtypes = (ctypes.c_char_p,DSVerInfo,ctypes.POINTER(c_int))
#Working code (I had missed that I should be passsing a pointer to DSVerInfo)
libc.DSpotterVerInfo.argtypes = (ctypes.c_char_p,POINTER(DSVerInfo),ctypes.POINTER(c_int))
This is probably what you need. Here's a sample implementation of the function call:
test.c
#include <windows.h>
#include <stdio.h>
typedef struct _VerInfo
{
const char *SDKName;
const char *SDKVersion;
const char *SDKType;
const char *Date;
const char *LType;
BOOL bTrialVersion;
} VerInfo;
__declspec(dllexport)
const char *DSpotterVerInfo(char *lpchLicenseFile, VerInfo *lpVerInfo, INT *lpnErr) {
printf("LicenseFile = %s\n", lpchLicenseFile);
lpVerInfo->SDKName = "SDKName";
lpVerInfo->SDKVersion = "SDKVersion";
lpVerInfo->SDKType = "SDKType";
lpVerInfo->Date = "Date";
lpVerInfo->LType = "LType";
lpVerInfo->bTrialVersion = TRUE;
*lpnErr = 123;
return "something";
}
To call it:
test.py
import ctypes as ct
from ctypes import wintypes as w
class DSVerInfo(ct.Structure):
_fields_ = [('Name', ct.c_char_p),
('Version', ct.c_char_p),
('SDKType', ct.c_char_p),
('Date', ct.c_char_p),
('LicType', ct.c_char_p),
('Trial', w.BOOL)] # c_bool is only 1 byte, Windows BOOL is 4 bytes.
# Your __init__ is unnecessary. ctypes inits to null/zero by default
# and create_string_buffer is only needed if the C function needs
# pre-allocated buffer to write to. Since your structure had const
# char* parameters that wasn't required and pointers to null strings
# wouldn't be a very big buffer anyway :^)
# Helper function to print this structure.
def __repr__(self):
return f'DSVerInfo({self.Name!r}, {self.Version!r}, {self.SDKType!r}, {self.Date!r}, {self.LicType!r}, {self.Trial!r})'
dll = ct.CDLL('./test')
# Make sure to use the correct function name and declare both .argtypes and .restype.
dll.DSpotterVerInfo.argtypes = ct.c_char_p,ct.POINTER(DSVerInfo),ct.POINTER(ct.c_int)
dll.DSpotterVerInfo.restype = ct.c_char_p
err_no = ct.c_int()
Lic_ct = b'licensefile'
VerInfo = DSVerInfo()
result = dll.DSpotterVerInfo(Lic_ct, ct.byref(VerInfo), ct.byref(err_no))
print('Result:', result.decode())
print('Error No:', err_no.value)
print('Version Size: ', ct.sizeof(VerInfo))
print(VerInfo)
Output:
LicenseFile = licensefile
Result: something
Error No: 123
Version Size: 48
DSVerInfo(b'SDKName', b'SDKVersion', b'SDKType', b'Date', b'LType', 1)