Python调用 dll 文件

9 篇文章 0 订阅

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
我就被这个折腾了半天

  • 11
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值