0、前言

ctypes 是 Python 的外部函数库。它提供了与 C 兼容的数据类型,并允许调用 DLL 或共享库中的函数。可使用该模块以纯 Python 形式对这些库进行封装。

官方提供了详尽的文档:https://docs.python.org/zh-cn/3.9/library/ctypes.html ,配合网友的示例可以很快上手。本文主要是记录一些基本操作。

import platform
from ctypes import *if platform.system() == 'Windows':libc = cdll.LoadLibrary('msvcrt.dll')
elif platform.system() =='Linux':libc = cdll.LoadLibrary('libc.so.6')libc.printf(b'Hello ctypes!\n')

1、数据类型

ctypes 提供了一些基本数据类型用来映射 C 语言和 Python 的类型,对照表可见文档,常用的几个:

对一个 ctypes 类型乘以一个正数可以创建一个数组类型。

# int数组
int_arr = ctypes.c_int*5  # 相当于C的 int[5]
# 可以指定数据来初始话数组
my_arr = int_arr(1, 3, 5, 7, 9)
# <__main__.c_long_Array_5 object at 0x0000023A3FF4F8C0>
print(my_arr)

ctypes 预定义的指针类型只提供了几个, 可以使用 ctypes.POINTER 来定义新的指针类型,ctypes.pointer() 函数构造一个指针对象, ctypes.byref() 函数相当于对对象取地址。无参调用指针类型可以创建一个 NULL 指针, NULL 指针的布尔值是 False。

# int类型
num = ctypes.c_int(1992)
# int指针
ctypes.POINTER(ctypes.c_int)
# 对实例取地址,可作为指针参数传递
ctypes.byref(num)
# 生成一个指针对象
ctypes.pointer(num)# 空指针
null_ptr = ctypes.POINTER(ctypes.c_int)()
print(bool(null_ptr)) #打印 False

结构体和联合必须继承自 ctypes 模块中的 Structure 和 Union 。子类必须定义 _fields_ 属性。 _fields_ 是一个二元组列表( tuple ),二元组中包含 field name 和 field type 。

type 字段必须是一个 ctypes 类型,比如 c_int,或者其他 ctypes 类型: 结构体、联合、数组、指针。

默认情况下,结构体和联合的字段与 C 的字节对齐是一样的。也可以在定义子类的时候指定类的 _pack_ 属性指定字节对齐大小。

# 如果指定大小端,可使用基类BigEndianStructure或LittleEndianStructure
class MyStruct(ctypes.Structure):_pack_ = 1  # 指定为1字节对齐_fields_ = [("a", ctypes.c_char),("b", ctypes.c_int),("c", ctypes.c_double)]# 打印字节大小,测试pack设置是否有效
print("sizeof MyStruct:", ctypes.sizeof(MyStruct))

2、变量访问与函数调用

使用 ctypes.cdll.LoadLibrary(path) 加载对应的 dll 后,可以访问 C/C++ 导出的变量/函数符号。如果是 C++ 编译器,需要使用 extern "C"。此外,编译时选择的位数应和使用的 Python 位数一致,比如都是 x86 或者 x64 的。

在 Windows MSVC 中,可以这样写:

#ifdef MYDLL_EXPORTS
#define MYDLL_API __declspec(dllexport)
#else
#define MYDLL_API __declspec(dllimport)
#endifextern "C"
{//导出变量extern MYDLL_API int my_number;//导出函数MYDLL_API int my_func(int a, int b);
}

对于上面导出的变量和函数,访问方式如下:(可以看到函数可以直接访问,变量通过 type.in_dll() 函数访问)

import ctypesdll = ctypes.cdll.LoadLibrary("D:/TestSpace/TestCpp_mydll/x64/Release/mydll.dll")
#访问变量
print(dll.my_number)  # 打印<_FuncPtr object at 0x0000022537F1B450>,默认应该是当作函数访问的
print(ctypes.c_int.in_dll(dll, 'my_number').value)
#访问函数
print(dll.my_func(12, 34))

ctypes 默认假定函数返回 int 类型,可以设置函数对象的 restype 属性来指定具体类型。对于参数类型也可以通过设置函数对象的 argtypes 来指定具体类型,防止不合理的参数传递。

my_func = dll.my_func #函数对象
my_func.argtypes = [ctypes.c_double, ctypes.c_double] #指定参数类型
my_func.restype = ctypes.c_double #指定返回值类型
print(my_func(12, 34.5))

对于指针参数或者结构体参数的构造参见上一节,或者下面测试代码的内容。

3、测试代码

(C/C++ 代码在 Windows VS 环境编写)

// 下列 ifdef 块是创建使从 DLL 导出更简单的宏的标准方法。
// 在预处理器中定义了 MYDLL_EXPORTS 符号,而调用方不包含此符号。
// 源文件中包含此文件的任何其他项目都会将 MYDLL_API 视为是从 DLL 导入的,
// 而此 DLL 则将用此宏定义的符号视为是被导出的。
#ifdef MYDLL_EXPORTS
#define MYDLL_API __declspec(dllexport)
#else
#define MYDLL_API __declspec(dllimport)
#endif//ctypes需要提供c接口,主要是目前cpp还没有统一的abi
extern "C"
{//导出一个全局变量extern MYDLL_API int my_number;//函数MYDLL_API int my_func(int a, int b);MYDLL_API double my_func2(double a, double b);MYDLL_API int * my_func3(char *a, double *b, const char *str);//指针extern MYDLL_API char *char_ptr;extern MYDLL_API int *int_ptr;extern MYDLL_API int *null_ptr;//打印dll中的变量MYDLL_API void print_var();//结构体struct MYDLL_API MyStruct{int a;double b;};MYDLL_API MyStruct my_func4(const MyStruct &arg);MYDLL_API MyStruct * my_func5(MyStruct *arg);
}
// MSVC模板默认带pch,设置为不使用预编译头
// #include "pch.h"
#include "mydll.h"
#include <iostream>MYDLL_API int my_number = 1992;MYDLL_API int my_func(int a, int b)
{return a + b;
}MYDLL_API double my_func2(double a, double b)
{return a + b;
}MYDLL_API int * my_func3(char * a, double * b, const char *str)
{std::cout << "dll myfunc3:" << str << std::endl;my_number = *a + int(*b);return &my_number;
}MYDLL_API char *char_ptr = new char(65);
MYDLL_API int *int_ptr = new int(250);
MYDLL_API int *null_ptr = nullptr;MYDLL_API void print_var()
{std::cout << "dll print var:"<< "\n\tmy number:" << my_number<< "\n\tchar ptr:" << *char_ptr<< "\n\tint ptr:" << *int_ptr << std::endl;
}MYDLL_API MyStruct my_func4(const MyStruct & arg)
{MyStruct ret{ 12,34.5 };std::cout << "dll myfunc4:"<< "in:" << arg.a << " " << arg.b<< "ret:" << ret.a << " " << ret.b << std::endl;return ret;
}MYDLL_API MyStruct * my_func5(MyStruct * arg)
{if (arg) {arg->a = 67;arg->b = 89.5;}return arg;
}
import ctypes#dll = ctypes.cdll.LoadLibrary("msvcrt.dll")
#dll.printf(b"hello ctypes\n")dll = ctypes.cdll.LoadLibrary("D:/TestSpace/TestCpp_20210617_mydll/x64/Release/mydll.dll")
#访问变量
print(dll.my_number)  # 打印<_FuncPtr object at 0x0000022537F1B450>,默认应该是当作函数访问的
print(ctypes.c_int.in_dll(dll, 'my_number').value)
#访问函数
print(dll.my_func)
print(dll.my_func(12, 34))
#ctypes 默认假定函数返回 int 类型,可以设置函数对象的 restype 属性来指定具体类型。
#对于参数类型也可以通过设置函数对象的 argtypes 来指定具体类型,防止不合理的参数传递。
my_func2 = dll.my_func2
my_func2.argtypes = [ctypes.c_double, ctypes.c_double]
my_func2.restype = ctypes.c_double
print(my_func2(12, 34.5))
my_func3 = dll.my_func3
my_func3.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.c_double)]
my_func3.restype = ctypes.POINTER(ctypes.c_int)
arg1 = ctypes.c_char(12)
arg2 = ctypes.c_double(1000)
#byref相当于取引用,c字符串传字节数组
ret = my_func3(ctypes.byref(arg1), ctypes.byref(arg2), b'hello ctypes.')
print(ret.contents.value)dll.print_var()
#指针变量
#ptr = ctypes.POINTER(ctypes.c_int)
# char(65)->print(b'A'),可能会把后面挨着的字节内容也打印
print(ctypes.c_char_p.in_dll(dll, 'char_ptr').value)
print(ctypes.POINTER(ctypes.c_int).in_dll(dll, 'int_ptr').contents.value)
ctypes.POINTER(ctypes.c_int).in_dll(dll, 'int_ptr').contents.value = 520
#空指针bool值为false
print(bool(ctypes.POINTER(ctypes.c_int).in_dll(dll, 'null_ptr')))
dll.print_var()#struct或union必须派生ctypes给的基类Structure和Union
#每个子类都必须定义一个_fields_属性,_fields_必须是一个2-tuples列表,包含一个字段名和一个字段类型。class MyStruct(ctypes.Structure):_fields_ = [("a", ctypes.c_int),("b", ctypes.c_double)]my_func4 = dll.my_func4
my_func4.restype = MyStruct
arg = MyStruct()
arg.a = 10
arg.b = 11
#print(ctypes.sizeof(MyStruct))
ret = my_func4(arg)
print('my_func4 ret:', ret.a, '  ', ret.b)
my_func5 = dll.my_func5
my_func5.restype = ctypes.POINTER(MyStruct)
ret = my_func5(ctypes.byref(arg)).contents
print('my_func5 ret:', ret.a, '  ', ret.b)

4、参考

参考文档:https://docs.python.org/zh-cn/3.9/library/ctypes.html

参考博客:https://zhuanlan.zhihu.com/p/23372572

参考博客:https://blog.csdn.net/CLinuxF/article/details/107913199

参考博客:https://www.cnblogs.com/gaowengang/p/7919219.html

Python ctypes模块的基本使用相关推荐

  1. 聊聊Python ctypes 模块(转载)

    聊聊Python ctypes 模块(转载) https://zhuanlan.zhihu.com/p/20152309?columnSlug=python-dev 作者:Jerry Jho 链接:h ...

  2. ctypes python3_聊聊Python ctypes 模块

    摘要:模块ctypes是Python内建的用于调用动态链接库函数的功能模块,一定程度上可以用于Python与其他语言的混合编程.由于编写动态链接库,使用C/C++是最常见的方式,故ctypes最常用于 ...

  3. python ctypes模块安装_ctypes模块扩展python

    文章1 前言 朋友的公司是做GPS的,上周联系到我要帮做个程序把他们平台的车辆定位跟踪数据和省里的平台对接.看一下官方提供的三个文档,洋洋洒洒共一百多页,一大堆协议的定义甚是齐全,好在官方的文件中也带 ...

  4. 简单分析Python ctypes模块的WinDLL源码(我爱Python,吼吼~)

    又是一个寂寞的周末啊同学们,这几天天气变冷自己却没有赖床,好吧,表扬一次^^ 扯点八卦,今天是pycon2011在上海那边开了,我早上和刚才看了网上的直播,做的很不错,形式很像irongeek.com ...

  5. Python ctypes 模块

    原文:http://zhuanlan.zhihu.com/p/20152309 著作权归作者所有. 商业转载请联系作者获得授权,非商业转载请注明出处. 作者:Jerry Jho 链接:http://z ...

  6. Python ctypes模块

    1 简介 ctypes是一个自Python 2.5开始引入的,Python自带的函数库.其提供了一系列与C.C++语言兼容的数据结构类与方法,可基于由C源代码编译而来的DLL动态链接库文件,进行Pyt ...

  7. 聊聊Python ctypes 模块

    摘要:模块ctypes是Python内建的用于调用动态链接库函数的功能模块,一定程度上可以用于Python与其他语言的混合编程.由于编写动态链接库,使用C/C++是最常见的方式,故ctypes最常用于 ...

  8. Python ctypes 调用API函数模拟键盘鼠标事件

    在Python编程中, 有时需要模拟键盘或鼠标事件, 自动操作计算机, 比如玩游戏等. 本文介绍使用ctypes模块调用API函数, 模拟键盘鼠标事件的方法. 目录 1.导入ctypes模块 2.通过 ...

  9. python 空指针_Python ctypes模块:扩展指针数组时进行NULL指针访问

    我试图在项目中使用ctypes模块.我正在创建一个动态分配的" max_entries"对数组,一旦该数组用完,我将创建一个新的大小为(1.5 * max_entries)的数组, ...

最新文章

  1. 数据结构 - 有两个链表,第一个升序,第二个降序,合并为一个升序链表(C++)
  2. 数据结构-链表1-顺序存储
  3. 卷积神经网络中的池化是什么意思
  4. 霆智服务器安装步骤_阿里云服务器安装MySql数据库详细步骤
  5. 经典傅里叶算法小集合 附完整c代码
  6. PID公式的推导过程及实现代码
  7. DELL 笔记本 触摸板 驱动安装 与 禁用启动
  8. python爬虫之一:爬取网页小说(魂破九天)
  9. 自动化测试八宗罪- 读Test Automation Snake Oil的一点翻译和感想
  10. Cortex-M3技术参考手册 2022年3月1日
  11. tableView表格重写表头增加全选功能和实现翻页(读写excel和读ini)
  12. 威学一百_威学一百app下载-威学一百官网版下载v1.0.0_MDPDA手机网
  13. ant desigh of angular:让nz-tree-select与nz-tree的值保持一致
  14. go 并发编程 之 数据竞争 data race (三)
  15. 目前家用计算机的运算速度每秒,现代个人计算机运算速度最高可达每秒()
  16. python爬取豆瓣网即将上映的电影,数据信息存储到json文件
  17. Pinpoint作为链路追踪和报警(监控spring boot服务)
  18. 局域网联机_【进击的巨人21】【全DLCs整合】【局域网联机】【免安装解压即玩】 免费分享...
  19. 在外远程桌面控制家里的电脑
  20. 边沿捕获寄存器-Verilog

热门文章

  1. RMAN备份之备份多个备份集到带库(一)
  2. win10 安装office 2016 plus 备忘
  3. 数据结构实验大作业(将之前预测ACM获奖的模型搬到Vue和django上)
  4. 全球及中国元素铟行业市场规模预测及未来发展前景分析报告2022-2028年
  5. 开启智慧的10大网站--非常值得收藏!
  6. K8s安装遇见问题笔记
  7. 益普生携手海王星辰,布局大健康领域
  8. Al_challenger_2018_sentiment_analysis_top17_基于Aspect Level思路的解决方案
  9. HDU 1546 (最短路 Dijkstra算法)
  10. 计算机网络技术与应用的ppt,计算机网络技术与应用.ppt