DLL 样例
- VC 编译器
// func.c
#ifdef _MSC_VER
#define DLL_EXPORT __declspec( dllexport )
#else
#define DLL_EXPORT
#endif
DLL_EXPORT int add(int a, int b)
{
return a + b;
}
DLL_EXPORT void update(double a[], int size)
{
for (int i = 0; i < size; i++)
*a++ *= 0.0098;
}
DLL_EXPORT void print_s(const char* s)
{
printf("Hello %s", s);
}
编译:cl /LD func.c /o func.dll
- gcc 编译器
// func.c
int add(int a, int b)
{
return a + b;
}
void update(double a[], int size)
{
for (int i = 0; i < size; i++)
*a++ *= 0.0098;
}
void print_s(const char* s)
{
printf("Hello %s", s);
}
编译:gcc func.c -shared -o func.dll
调用方法
from ctypes import *
dll = CDLL("func.dll")
dll.add(23, 33)
length = dll.print_s(c_char_p(b"Andy"))
print(length)
arr = (c_double*5)()
arr[0] = 100
arr[1] = 200
arr[2] = 300
arr[3] = 200
arr[4] = 200
dll.update(byref(arr), len(arr))
for item in arr:
print(item)
out:
56
Hello Andy
10
0.98
1.96
2.94
1.96
1.96
带结构体
// func.h
// 如果 dll 文件中的函数用到了 C 结构体,比如
typedef struct Header {
unsigned short Version;
char Manufacturer[16];
char Type[16];
unsigned int Timestamp;
unsigned int _;
unsigned short TaskID;
} Header;
int r_header(struct Header *header, char *binfile);
# python
from ctypes import *
# 定义一个与 C 结构体相同的类,在python中使用
class Header(Structure): # 继承 Structure 类
_fields_ = [
("ver", c_ushort),
("manuf", (c_char*16)),
("typ", (c_char*16)),
("ts", c_uint),
("_", c_uint),
("tid", c_ushort)
]
rbs = CDLL("func.dll")
h = Header() # 声明一个python 版的结构体
rbs.r_header.restype = c_int # 指定 dll 内函数 r_header 返回类型
code = rbs.r_header(byref(h), c_char_p(b"./binfile.bin")) # 传入 h 地址,会被写入读取到的值
if code != 0:
print("error")
print(h.ver, h.manuf, h.typ, h.ts, h.tid)
进阶: 回调函数, 类型指针, 传递Python类型
将 Python 函数做为回调函数传给dll
// func.h
// 数据结构体
typedef struct Data {
int Msec;
long long Timestamp;
double Volume;
double Amount;
} Data;
// 回调函数签名
typedef void(*cb) (void* arr, const Data* d, void* opt);
/*
* 生成 Data 数据, 传给回调函数
* @param: func 回调函数
* @param: arr 任意指针, 将传递给回调函数
* @param: opt 任意指针, 将传递给回调函数
*/
void create_data(cb func, void* arr, void* opt);
// func.c
#include "func.h"
void create_data(cb func, void* arr, void* opt)
{
Data d {500, 1601548587, 55555, 88888888};
func(arr, &d, opt);
}
# test.py
from ctypes import *
from copy import deepcopy
# 定义与 C 结构体相同的类
class Data(Structure):
_fields_ = [
("Msec", c_int),
("Timestamp", c_int64),
("Volume", c_double),
("Amount", c_double)
]
class Param(Structure):
_fields_ = [
("x", c_int),
("y", c_int)
]
# 定义 python 回调函数, 用来被 createData() 调用, 接收 Data 数据
# @param: pyList python 列表
# @param: data Data 结构体指针
# @param: opt 其他参数
def callback(pyList: list, data: POINTER(Data), opt: c_void_p):
p = cast(opt, POINTER(Param))[0] # 指针类型转换
print(p.x, p.y)
pyList.append(deepcopy(data[0])) # 拷贝指针指向内容
# 定义回调函数签名, 第一个参数是指代函数返回值, 后面三个才是实参
funcType = CFUNCTYPE(None, py_object, POINTER(Data), c_void_p)
cb = funcType(callback) # 实例化
mydll = CDLL("func.dll")
dataArr = [] # 用来保存 Data 数据
para = Param()
para.x = 11
para.y = 22
mydll.create_data(cb, c_void_p(id(dataArr), byref(para))
# 输出结果
for item in dataArr:
print(item.Msec, item.Timestamp, item.Volume, item.Amount)
更进阶: ctypes 改写获取到的指针所指向的内容
// func.h
//该回调函数向 buff 写入字符串
typedef void(*write_buff) (char* buff);
/*
* 处理由回调函数产生的字符串
* @param: f 回调函数
*/
void print_something(write_buff f);
// func.c
#include "func.h"
void print_something(write_buff f)
{
char buff[32]; // 字符数组作为buffer 接收字符串
f(buff); // 执行回调函数, 取得字符
printf("get str: [%s]", buff); // python调用可能不会打印出来, buff 内容确实被回调函数修改了
}
# test.py
from ctypes import *
import time
# 定义 python 回调函数, 用来被 print_something() 调用, 生成字符串
# @param: buff c_char 指针
def new_str(buff: POINTER(c_char)):
dt = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time())) # 当前日期时间字符串
memmove(buff, dt.encode(), len(dt)) # 拷贝 dt 字符到 buff 中, 这个 buff 空间是在 dll 中开辟的
funcType = CFUNCTYPE(None, POINTER(c_char))
cb = funcType(new_str) # 实例化
mydll = CDLL("func.dll")
mydll.print_something(cb)
#> get str: [2022-03-16 15:13:00]
释放dll(解除文件占用)
from ctypes import *
dll = CDLL("func.dll")
dll.add(23, 33)
cdll.kernel32.FreeLibrary(c_int64(dll._handle))
注意事项
一定要注意你用的Python 是多少位的(32/64位),还有你使用的编译器是多少位的,一定要对应。
否则会报错:不是有效的32位程序
,32位 python 用不了64位的 dll, 64位 python 也用不了32 位的dll
我就被这个折腾了半天