废话不多说, 直接上代码(忽略我写的c代码严谨性, 我刚学的c)
// hello.h
// Created by 86176 on 2022/12/14.
//
#ifndef UNTITLED1_SAMPLE_H
#define UNTITLED1_SAMPLE_H
typedef struct {
int x,y;
} Point;
typedef struct {
int data1;
float *data2;
double *data3;
int data4[3];
char *data5;
char data6[10];
} FuncResult;
FuncResult test_func(int data1, float *data2, double *data3, int data4[], char *data5, char data6[]);
void register_callback(int d, void (*func)(Point *p));
void struct_func(int d, Point *p);
#endif //UNTITLED1_SAMPLE_H
c代码编译成libhello.dll
# sample.py
import array
import ctypes
import os
_file = 'libhello.dll'
_path = os.path.join(*(os.path.split(__file__)[:-1] + (_file,)))
import cffi
import platform
ffi = cffi.FFI()
def load_library():
lib_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)))
# # 1. load header
with open(os.path.join(lib_dir, "library.h"), encoding='utf-8') as sdk_header:
sdk_header = sdk_header.read() \
.split("#define UNTITLED1_SAMPLE_H")[1] \
.replace("#endif", "")
ffi.cdef(sdk_header)
# 2. find library path and load
arch = platform.architecture()[0]
if platform.system() == "Darwin":
return ffi.dlopen(os.path.join(lib_dir, "libhello.dll"))
elif platform.system() == "Windows" and arch == "64bit":
# add path 'python/libemgp/' to environment variable 'PATH' to load the dependent DLLs.
os.environ["PATH"] += os.pathsep + os.path.join(lib_dir, "win")
# return ffi.dlopen(os.path.join(lib_dir, "win", "emgp.dll"))
return ffi.dlopen(os.path.join(lib_dir, "libhello.dll"))
else:
raise Exception("Unsupported platform: " + platform.system() + ", arch: " + arch)
# load EMGPlusSDK library
libemgp = load_library()
# FuncResult test_func(int x, float *y, const double z[10], char c, char *data1, const char data2[10], Point *p);
data1 = 1
data2 = ffi.new('float *', 1)
data3 = ffi.new('double *', 1)
data4 = [1, 2, 3]
data5 = ffi.new('char *', 'a'.encode())
data6 = ffi.new('char []', 'hello world'.encode('utf-8'))
res = libemgp.test_func(data1, data2, data3, data4, data5, data6)
print('------------------函数传参-----------------------------')
print(res.data1)
print(res.data2[0]) # print(ffi.unpack(res.data2, 1)[0])
print(res.data3[0]) # print(ffi.unpack(res.data3, 1)[0])
print(ffi.unpack(res.data4, 3))
print(ffi.unpack(res.data5, 1).decode('utf-8')) # print(ffi.string(res.data5).decode('utf-8'))
print(ffi.unpack(res.data6, len("hello world")).decode('utf-8')) # print(ffi.string(res.data6).decode('utf-8'))
print('------------------函数回调-----------------------------')
@ffi.callback("void(Point *p)")
def func_callback(point):
print(point.x)
print(point.y)
libemgp.register_callback(3, func_callback)
print('------------------结构体参数-----------------------------')
libemgp.struct_func(2, ffi.new("Point *", (3, 4)))
对比ctypes可以看到cffi的代码更简洁一点, 更加靠近python的语法, 缺点就是教程太少了, 包括官方的教程也是错误一大堆(好像教程是老版本的, cffi==1.15.1运行例子不少报错的)