Translate C header file in Python - python

I have this C-Headerfile:
#pragma pack (push,1)
typedef struct {
uint16_t DLLFailureCode;
uint8_t ConnectionStatus;
uint32_t SystemFailureCode;
} TConnectionResult;
typedef void (__cdecl *TOnConnectionEvent)(uint32_t Handle,
TConnectionResult ConnectionStatus);
DLLIMPORT uint16_t __cdecl OpenConnection(uint8_t PortType,
char * PortName,
TOnConnectionEvent OnConnectSuccess,
uint32_t * Handle);
And I implemented it as below, but for some reason I can't get any connection. Did I make a mistake or is it the DLL?
class Program:
def __init__(self):
self.DLL = CDLL(os.path.join(r"C:\Users\...\PycharmProjects\Program\epMCOMLib.dll"))
self.Handle = c_uint32(0)
self.Handle_p = pointer(self.Handle)
def OPEN(self):
PortType = c_uint8(0)
PortName = c_char_p(b'COM1')
OnConnectSuccess = c_uint32(0)
self.DLL.OpenConnection(PortType, PortName, OnConnectSuccess, self.Handle_p)

Here's a working example given your C declarations. I recommend using .argtypes and .restype to declare your function parameters and return value so ctypes can do argument checking. Passing a c_uint32(0) for Handle is incorrect on a 64-bit system but might work on a 32-bit system. Use None for a NULL pointer or you can make a callback as demonstrated below using CFUNCTYPE as the callback type.
test.c (MSVC compile: cl /LD /W4 test.c)
#include <stdint.h>
#pragma pack(push,1)
typedef struct {
uint16_t DLLFailureCode;
uint8_t ConnectionStatus;
uint32_t SystemFailureCode;
} TConnectionResult;
#pragma pack(pop)
typedef void (__cdecl *TOnConnectionEvent)(uint32_t Handle,
TConnectionResult ConnectionStatus);
__declspec(dllexport)
uint16_t __cdecl OpenConnection(uint8_t PortType,
char * PortName,
TOnConnectionEvent OnConnectSuccess,
uint32_t * Handle)
{
TConnectionResult result = {1, 2, 3};
*Handle = 123;
OnConnectSuccess(*Handle, result);
return 1;
}
test.py
import ctypes as ct
class TConnectionResult(ct.Structure):
_pack_ = 1
_fields_ = (('DLLFailureCode', ct.c_uint16),
('ConnectionStatus', ct.c_uint8),
('SystemFailureCode', ct.c_uint32))
def __repr__(self):
return f'TConnectionResult(DLLFailureCode={self.DLLFailureCode}, ' \
f'ConnectionStatus={self.ConnectionStatus}, ' \
f'SystemFailureCode={self.SystemFailureCode})'
TOnConnectionEvent = ct.CFUNCTYPE(None, ct.c_uint32, TConnectionResult)
#TOnConnectionEvent
def callback(handle, status):
print(f'{handle=}, {status=}')
dll = ct.CDLL('./test')
dll.OpenConnection.argtypes = ct.c_uint8, ct.c_char_p, TOnConnectionEvent, ct.POINTER(ct.c_uint32)
dll.OpenConnection.restype = ct.c_uint16
port_type = 0
port_name = b'COM1'
handle = ct.c_uint32()
result = dll.OpenConnection(port_type, port_name, callback, ct.byref(handle))
print(f'{result=} handle={handle.value}')
Output:
handle=123, status=TConnectionResult(DLLFailureCode=1, ConnectionStatus=2, SystemFailureCode=3)
result=1 handle=123

Related

C typedef struct with pragma translated to Python in python

I have this C-Headerfile:
#pragma pack (push,1)
typedef struct {
uint16_t DLLFailureCode;
uint8_t ConnectionStatus;
uint32_t SystemFailureCode;
} TConnectionResult;
How would you implement the equivalent to this in Python (if that is even possible)?
I tried the following way, but it doesn't seem right: Why?
from ctypes import *
class Program:
def __init__(self):
self.DLL = CDLL(os.path.join(r"C:\Users\...\PycharmProjects\Program\epMCOMLib.dll"))
self.Handle = c_uint32(0)
self.Handle_p = pointer(self.Handle)
def QueryOpenConnectionStatus(self):
p = POINT(0, 0, 0)
p_p = pointer(p)
print("OpenQuery", self.DLL.QueryOpenConnectionStatus(self.Handle, p_p))
print(p.DLLFailureCode, p.ConnectionStatus, p.SystemFailureCode)
class POINT(Structure):
_pack_ = 1
_fields_ = [
('DLLFailureCode', c_uint16),
('ConnectionStatus', c_uint8),
('SystemFailureCode', c_uint32)]
My goal is to be able to run the following method successfully in Python:
DLLIMPORT void __cdecl QueryOpenConnectionStatus(uint32_t Handle,
TConnectionResult * Result);

Python error TypeError: __init__() missing 2 required positional arguments: and ctypes

I have a DLL library in C, I managed to initialize the functions in python a receive and send data to it. Now I'm trying to reorganize the python code to have all my DLL python functions in one big class.
This is what I have in Visual Studio.
This is Hello.h
#pragma once
#include<stdio.h>
#include<stdint.h>
#ifdef SOME_API_EXPORTS
#define SOME_API __declspec(dllexport)
#else
#define SOME_API __declspec(dllimport)
#endif
SOME_API int sum(int a, int b);
SOME_API int mul(int a, int b);
SOME_API void message(void);
#define max_length 63U
typedef struct
{
uint8_t param1;
uint32_t param2;
uint8_t param3;
uint8_t param4[max_length];
} struct_1;
SOME_API void message_2(struct_1* request);
SOME_API int function_1(uint8_t param_1, uint8_t param_2);
SOME_API int function_2(uint8_t param_1, uint8_t param_2);
this is Hello.C
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include "Hello.h"
#include <inttypes.h>
int sum(int a, int b) {
return a + b;
}
int mul(int a, int b) {
return a * b;
}
void message(void)
{
puts("hello from dll\n");
}
SOME_API void message_2(struct_1* request)
{
printf("data: request=[%d %d %d %s]\n",
request->param1, request->param2, request->param3, request->param4);
//do some processing
}
SOME_API int function_1(uint8_t param_1, uint8_t param_2)
{
int status;
//do some processing
printf("%u , %u \n", param_1, param_2);
status = 1;
return status;
}
SOME_API int function_2(uint8_t param_1, uint8_t param_2)
{
int status;
//do some processing
printf("%u , %u \n", param_1, param_2);
status = 0;
return status;
}
this is my original python code without the class.
import ctypes
test = ctypes.WinDLL('Project.dll')
max_length = 63
class struct_1(ctypes.Structure):
_fields_ = [("param1", ctypes.c_ubyte),
("param2", ctypes.c_uint32),
("param3", ctypes.c_ubyte),
("param4", ctypes.c_char * max_length)]
test.message()
##req = struct_1(1, 2 , 3,"test".encode('utf-8'))
##result = test.message_2(ctypes.byref(req))
py_function_1 = test.function_1
py_function_1.argtype = (ctypes.c_uint8,ctypes.c_uint8 )
py_function_1.restype = ctypes.c_int
py_function_1(255,255)
return_val = py_function_1()
print(return_val)
py_function_2 = test.function_2
py_function_2.argtype = (ctypes.c_uint8,ctypes.c_uint8 )
py_function_2.restype = ctypes.c_int
py_function_2(255,255)
return_val = py_function_2()
print(return_val)
and this is my python test code trying to implement the class that will contain all my dll function inits. I'm just testing with function_1 and function_2 for now. I will add other functions in the future.
import ctypes
test = ctypes.WinDLL('Project.dll')
max_length = 63
class struct_1(ctypes.Structure):
_fields_ = [("param1", ctypes.c_ubyte),
("param2", ctypes.c_uint32),
("param3", ctypes.c_ubyte),
("param4", ctypes.c_char * max_length)]
class my_outer_class:
def __init__(self,py_function_1,py_function_2):
self.py_function_1 = test.function_1
self.py_function_1.argtype = (ctypes.c_uint8,ctypes.c_uint8 )
self.py_function_1.restype = ctypes.c_int
self.py_function_2 = test.function_2
self.py_function_2.argtype = (ctypes.c_uint8,ctypes.c_uint8 )
self.py_function_2.restype = ctypes.c_int
myapi = my_outer_class()
result = myapi.py_function_1(255,255)
print(result)
result = myapi.py_function_2(255,255)
print(result)
The error I get is
Traceback (most recent call last):
File "C:\Users\Dude\OneDrive\Desktop\Test.py", line 25, in <module>
myapi = my_outer_class()
TypeError: __init__() missing 2 required positional arguments: 'py_function_1' and 'py_function_2'
I would appreciate any suggestions. Thanks.
It is because you define the class's init to have py_function_1 and py_function_2 as the constructors. Thus, you should pass the test.function_1 and test.function_2 when initializing the object, i.e.
myapi = my_outer_class(test.function_1, test.function_2)
and then use it correctly in class' init, i.e.
class my_outer_class:
def __init__(self, py_function_1, py_function_2):
self.py_function_1 = py_function_1 # change this to use the constructor
... # other lines
self.py_function_2 = py_function_2 # change this to use the constructor
... # other lines
The solution above means that the user has the ability to supply other callable in the constructor.
Another alternative is to restrict the user to use only the one from .dll. A possible solution would be the one mentioned in the comments section. However, I don't think this is the best practice because it uses the global scope to find the function. This will be an issue when you tried to scale your script, say, by separating the class in another file. You will need to include non-class code there.
Better solution if you want restrict the user to not altering the function_1 and function_2 is to load the .dll in the class' init instead of in the global scope.
class my_outer_class:
def __init__(self):
test = ctypes.WinDLL('Project.dll')
self.py_function_1 = test.function_1

ctypes, problem with output format function c++

I created a ctype function that calls a C ++ library that connects to a socket and sends it a 5 bytes hex packet. The function connects send the hex code I write I input the peripheral responds (I checked with Wireshark) in a correct way but the python code with ctype fails to interopet well the packet it receives as a response (always 5 bytes). Can anyone give me any suggestions? Where am I wrong? Attached I enclose the C ++ code with the code or python and the answer of the function.
I have already tried c_char_p, ctypes.POINTER (LP_c_char), ctypes.POINTER (LP_c_char), but nothing the return of the function is always 0
C++ Code connect_pe_func.cpp
#include <stdio.h>
#include <errno.h>
#include <string>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <cstring>
#include <unistd.h>
#include <iostream>
#include <unistd.h>
#include <sstream>
extern "C" char * connect_pe_func(int argc, char *argv[])
{
int sockfd, n;
int connected = 0;
struct sockaddr_in servaddr;
std::string serveraddr = argv[0];
sockfd = socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = inet_addr(serveraddr.c_str());
servaddr.sin_port = htons(9761);
connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
std::string pref_hex;
std::string hex("0x");
std::string test = argv[1];
size_t numbytes = test.size() / 2;
uint8_t command[numbytes];
for (size_t w = 0, x = 0; w < numbytes; ++w, x += 2)
{
pref_hex = hex + test.substr(x, 2);
//cout << pref_hex;
command[w] = stoi(pref_hex, nullptr, 16);
}
int bytes_to_send = sizeof(command);
send(sockfd, command, bytes_to_send, 0);
uint8_t output_command[numbytes];
recv(sockfd, output_command, bytes_to_send, 0);
close(sockfd);
char test_out[10];
for (size_t w = 0, x = 0; w < numbytes; ++w, x += 2)
{
test_out[x] = (char)output_command[w];
}
return test_out;
}
python code
import sys
import ctypes
lib = ctypes.CDLL('./connect_pe_func.so')
LP_c_char = ctypes.POINTER(ctypes.c_char)
LP_LP_c_char = ctypes.POINTER(LP_c_char)
lib.connect_pe_func.argtypes = (ctypes.c_int, LP_LP_c_char)
lib.connect_pe_func.restypes = ctypes.c_char_p
argv = ["192.168.2.170","2600000026"]
argc = len(argv)
p = (LP_c_char*len(argv))()
for i, arg in enumerate(argv): # not sys.argv, but argv!!!
enc_arg = arg.encode('utf-8')
p[i] = ctypes.create_string_buffer(enc_arg)
na = ctypes.cast(p, LP_LP_c_char)
lib.connect_pe_func(argc, na)
I expected the output of python code like 26810000a7, but the actual output is always 0

Passing struct from C to Python with ctypes

I'm trying to pass a struct from C to Python but I've problems when some attribute is a char *
test.h
typedef struct _FOO
{
int field1;
char field2[5];
int whatever;
} FOO, *PFOO;
void CallPythonFunction(PFOO foo);
test.c
PyObject *module_name, *plugin, *PyTargetFunction, *ppresult, *pargs;
void CallPythonFunction(PFOO foo)
{
// Set PYTHONPATH TO working directory
setenv("PYTHONPATH",dir_python_modules,1);
// Initialize the Python Interpreter
Py_Initialize();
module_name = PyString_FromString((char*)"test");
// Load the module object
if ((plugin = PyImport_Import(module_name)) == NULL) {
PyErr_Print();
printf("Error: PyImport_Import\n");
return -1;
}
PyTargetFunction = PyObject_GetAttrString(plugin, (char*)"some_function");
pargs = PyTuple_Pack(1
//, PyLong_FromUnsignedLong((unsigned int) validacion)
, PyLong_FromVoidPtr(foo)
);
ppresult = PyObject_CallObject(PyTargetFunction, pargs);
}
test.py
import ctypes
POINTER = ctypes.POINTER
class _PyFoo(ctypes.Structure):
_fields_ = [
('field1', ctypes.c_int),
('field2', ctypes.c_char_p),
#('field2', POINTER(ctypes.c_char), # not work either
('whatever', ctypes.c_int)
]
def some_function(foo):
foo_view = _PyFoo.from_address(foo)
print("foo.field1: ", foo_view.field1)
print("foo.field2: ", foo_view.field2.value)
print("foo.whatever: ", foo_view.whatever)
pass
main.c
int main(int argc, char *argv[])
{
PFOO foo = malloc(sizeof(FOO));
foo->field1 = 5;
sprintf(foo->field2, "hello");
foo->whatever = 3;
CallPythonFunction(foo);
return 0;
}
I need get this output:
('foo.field1: ', 5)
('foo.field2: ', 'hello')
('foo.whatever: ', 3)
In test.py, the type of field2 is incorrect. ctypes.c_char * 5 is the correct ctypes syntax for a C char[5].
Also in test.py, change foo_view.field2.value to foo_view.field2 since it will not be a pointer. Without that change, the Python code will throw an exception that isn't currently handled by the test.c code and it will just stop after the first print.
In main.c, sprintf(foo->field2, "hello"); is going to have a buffer overflow because of the null terminator.

python ctypes use pointers to get results from a function

I am trying to call a C function form my python code, the C function is the following:
#include <stdio.h>
#include <stddef.h>
#include "picohttpparser.c"
#include "picohttpparser.h"
const char *path;
char buf[4096];
int pret, minor_version, i;
struct phr_header headers[100];
size_t buflen = 0, prevbuflen = 0, method_len, path_len, num_headers;
ssize_t rret;
void parse_http_request(char *request, const char *method) {
/* parse the request */
strncpy(buf, request, 4096);
num_headers = sizeof(headers) / sizeof(headers[0]);
phr_parse_request(buf, sizeof(buf) -1, &method, &method_len, &path, &path_len, &minor_version, headers, &num_headers, 0);
}
In python I am using ctypes:
import ctypes
_cparser = ctypes.CDLL('/tmp/serv.so')
_cparser.parse_http_request.argtypes = (ctypes.c_char_p, ctypes.POINTER(ctypes.c_char_p))
def parse(request):
method = ctypes.c_char_p()
_cparser.parse_http_request(ctypes.c_char_p(request.encode()), ctypes.byref(method))
print(method)
method is always None, but if I compile the C code, it prints out correctly the method variable. What am I missing here?

Categories

Resources