Python基础库-ctypes
目录
- 用处
- 文档翻译
- windows加载dll
- linux加载so
- 声明函数
- 调用约定
- 数据类型
- 使用ctypes类型
- 传递指针
- 结构体
- 结构体 字段对齐和字节序
- 数组
- 类型强制转换
- 回调函数
- dll导出的值
- 例子
用处
ctypes主要还是用于调用dll和so里面的函数
文档翻译
windows加载dll
from ctypes import windll, cdll
# 加载Windows的kernel32.dll
print(windll.kernel32)
# 加载Windows的msvcrt.dll,和libc一样,里面是Windows的标准库函数
print(cdll.msvcrt)
linux加载so
from ctypes import cdll, CDLL
# 方法1
libc1 = cdll.LoadLibrary("libc.so.6")
# 方法2
libc2 = CDLL("libc.so.6")
声明函数
如果函数的参数和返回值是基础类型(字符串和数值),则可以直接调用函数,无需声明
from ctypes import windll
handle = windll.kernel32.GetModuleHandleA(None)
print(hex(handle ))
t = cdll.msvcrt.time(None)
print(t)
printf = cdll.msvcrt.printf
printf(b"Hello, %s\n", b"World!")
如果函数参数或返回值包含一些复杂数据类型,则需要先声明函数参数(argtypes )和返回值(restype)
from ctypes import *
from ctypes.wintypes import *GetModuleHandleA = windll.kernel32.GetModuleHandleA
GetModuleHandleA.argtypes = (LPCSTR,)
GetModuleHandleA.restype = HMODULE
handle = GetModuleHandleA(None)
print(hex(handle))
直接调用GetModuleHandleA和声明之后调用,返回值是不一样的,这是因为返回的指针类型没有被正确解释。所以调用dll的函数前最好先声明函数的参数和类型
比如调用kernel32.dll的GetModuleHandleA,可以先在微软的官方文档中搜一下函数原型
调用约定
ctypes只支持两种调用约定,cdll支持cdecl调用约定,windll支持stdcall调用约定。还有个oledll也是stdcall调用约定,没看到和windll有啥区别
数据类型
使用ctypes类型
from ctypes import *i = c_int(10)
print(i)
print(c_wchar_p("Hello, World"))
print(c_ushort(-3))i.value = 100
print(i)
传递指针
可以用byref函数来传递指针,当然也可以用pointer函数,一样的效果,但是byref效率更高,因为pointer需要构造一个真实的指针。
from ctypes import *i = c_int(100)
f = c_float(3.14)
s = create_string_buffer(b"address: ")
cdll.msvcrt.printf(b"%s %p %x", s, byref(f), pointer(i))
结构体
继承Structure,然后声明_fields_ 字段就可以定义一个结构体类型
from ctypes import *class POINT(Structure):_fields_ = [("x", c_int),("y", c_int)]point = POINT(10, 20)
print(point.x, point.y)
point = POINT(y=5)
print(point.x, point.y)
结构体嵌套
class RECT(Structure):_fields_ = [("upperleft", POINT),("lowerright", POINT)]
rc = RECT(POINT(1, 2), POINT(3, 4))
# rc = RECT((1, 2), (3, 4))
print(rc.upperleft.x, rc.upperleft.y)
print(rc.lowerright.x, rc.lowerright.y)
结构体 字段对齐和字节序
默认对齐方式和C一样。可以用_pack_ 属性来定义,值可以设置一个正整数,表示字段的最大对齐方式,和#pragma pack(n)
效果是一样的
ctypes 中的结构体使用的是本地字节序,要使用非本地字节序,可以使用 BigEndianStructure, LittleEndianStructure, BigEndianUnion, LittleEndianUnion 作为基类。这些类不能包含指针字段
数组
# 定义
a = c_char * 4
# 赋值
s = a(b'a', b'b', b'c', b'\x00')
等同于
char a[4] = "abc";
类型强制转换
比如将一个float类型指针强制转换为int类型指针
from ctypes import *a = pointer(c_float(3.14))print(cast(a, POINTER(c_int)).contents)
当然输出结果肯定不是3
回调函数
在Python中定义一个可以在dll里面被调用的函数
qsort是一个排序函数,第一个参数是排序的数组,第二个是数组长度,第三个是数组元素的大小,第四个是个回调函数,如果返回值小于0,a将放在b前面,如果大于0,a将放在b后面
from ctypes import *def py_cmp_func(a, b):print("py_cmp_func", a[0], b[0])return a[0]-b[0]IntArray5 = c_int * 5
ia = IntArray5(5, 1, 7, 33, 99)
qsort = cdll.msvcrt.qsort
qsort.restype = NoneCMPFUNC = CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
cmp_func = CMPFUNC(py_cmp_func)qsort(ia, len(ia), sizeof(c_int), cmp_func)
print(list(ia))
CFUNCTYPE定义cdecl调用约定的函数,WINFUNCTYPE定义stdcall 调用约定的函数。第一个参数为返回值类型,后面的则是参数类型
也可以通过装饰器的形式定义
@CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
def py_cmp_func(a, b):print("py_cmp_func", a[0], b[0])return a[0] - b[0]
dll导出的值
动态链接库不止可以导出函数,也可以导出变量。这里的pythonapi,其实就是加载的python.dll
from ctypes import *opt_flag = c_int.in_dll(pythonapi, "Py_OptimizeFlag")
print(opt_flag)
例子
枚举进程的所有模块信息
c语言大概代码
hModuleSnap = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE32 | TH32CS_SNAPMODULE, dwPID );
if( hModuleSnap == INVALID_HANDLE_VALUE ) { return( r_mi );
}
me32.dwSize = sizeof( MODULEENTRY32 );
if( !Module32First( hModuleSnap, &me32 ) ) { CloseHandle( hModuleSnap );return( r_mi );
}
do { } while( Module32Next( hModuleSnap, &me32 ) );
Python翻译
第一步:定义结构体 MODULEENTRY32
from ctypes import *
from ctypes.wintypes import *class MODULEENTRY32(Structure):_fields_ = [("dwSize", DWORD), # 结构的大小,以字节为单位,必须先初始化("th32ModuleID", DWORD), # 该成员不再使用,并且始终设置为 1("th32ProcessID", DWORD), # 进程pid("GlblcntUsage", DWORD), # 无意义, 一般等于0xFFFF("ProccntUsage", DWORD), # 无意义, 一般等于0xFFFF("modBaseAddr", POINTER(BYTE)), # 拥有进程上下文中模块的基地址("modBaseSize", DWORD), # 模块的大小,以字节为单位("hModule", HMODULE), # 拥有进程上下文中的模块句柄("szModule", c_char*256), # 模块名称("szExePath", c_char*260), # 模块路径]
第二步:定义函数
kernel32 = WinDLL('kernel32', use_last_error=True)def func_def(name, restype, *argtypes, dll=kernel32):def errcheck(result, func, args):if not result:raise WinError(get_last_error())return resultcfunc = getattr(dll, name)cfunc.argtypes = argtypescfunc.restype = restype#cfunc.errcheck = errcheckreturn cfuncCreateToolhelp32Snapshot = func_def("CreateToolhelp32Snapshot", HANDLE, *(DWORD, DWORD))
Module32First = func_def("Module32First", BOOL, *(HANDLE, POINTER(MODULEENTRY32)))
Module32Next = func_def("Module32Next", BOOL, *(HANDLE, POINTER(MODULEENTRY32)))
CloseHandle = func_def("CloseHandle", BOOL, *(HANDLE,))
第三步:
TH32CS_SNAPMODULE = 0x00000008
TH32CS_SNAPMODULE32 = 0x00000010def getModuleInfo(moduleName, pid):'''获取模块信息,返回模块信息的字典'''hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE|TH32CS_SNAPMODULE32, pid)me32 = MODULEENTRY32()me32.dwSize = sizeof(MODULEENTRY32)bRet = Module32First(hModuleSnap, pointer(me32))while bRet:szModule = me32.szModule.decode()if szModule.upper() == moduleName.upper():addr = cast(me32.modBaseAddr, c_void_p).value # hex(addressof(modBaseAddr.contents))CloseHandle(hModuleSnap)try:me32.szExePath.decode("gbk")except UnicodeDecodeError:print(me32.szExePath)module = {'modBaseSize': me32.modBaseSize, # 模块字节大小'th32ProcessID': me32.th32ProcessID, # 进程pid'modBaseAddr': addr, # 模块基址"hModule": me32.hModule, # 模块句柄'szModule': me32.szModule.decode("ansi"), # 模块名称'szExePath': me32.szExePath.decode("ansi") # 模块路径}return modulebRet = Module32Next(hModuleSnap, pointer(me32) )CloseHandle(hModuleSnap)import os
import syspy_version = str(sys.version_info[0]) + str(sys.version_info[1])
print(getModuleInfo(f"python{py_version}.dll", os.getpid()))
Python基础库-ctypes相关推荐
- Python基础库-正则表达式库
活动地址:CSDN21天学习挑战赛 文章目录 1.正则表达式 1.1正则表达概述 1.2正则表达式库 1.3一个简单的例子:检查用户输入的邮箱地址是否合法 2.正则表达式-原子 3.正则表达式 ...
- python基础代码库-CNN详解-基于python基础库实现的简单CNN
CNN,即卷积神经网络,主要用于图像识别,分类.由输入层,卷积层,池化层,全连接层(Affline层),Softmax层叠加而成.卷积神经网络中还有一个非常重要的结构:过滤器,它作用于层与层之间(卷积 ...
- python基础库-python基础库-Pandas
s1,s2,s3为3个Series,用其组成一个人dataframe: df_new = pd.DataFrame([s1,s2,s3],index=["A","B&qu ...
- 【Python基础库】保留重要缓存内容 dill 使用【案例】
保存重要变量数据 例子如下 import dill#保存变量 T='Hiya' val=[1,2,3] a = np.zeros([4,5]) #建立一个缓存的文件 filename= 'global ...
- 【Python基础库】-在dataframe中错位相减-使用shift()函数
shift 英文:偏移 DataFrame.shift(periods=1, freq=None, axis=0) 参数 periods:类型为int,表示移动的幅度,可以是正数,也可以是负数,默认值 ...
- Python基础模块:图像处理模块@PIL(批量分类处理图片及添加水印)
大家好,才哥又来了! 又要上班了,春节总是过的太匆匆. 最近接到一个需求,把一批照片按照分辨率进行分类存储,再将其中指定宽高比的照片设置为特定的分辨率且添加水印. 以下是简单的效果: 目录: 文章目录 ...
- python 基础命令-Python 命令行(CLI)基础库
在 CLI 下写 UI 应用 前阵子看了一下自己去年写的 Python-视频转字符动画,感觉好糗..所以几乎把整篇文章重写了一遍.并使用 curses 库实现字符动画的播放. 但是感觉,curses ...
- Python中运用的基础库
库名称简介 Chardet字符编码探测器,可以自动检测文本.网页.xml的编码. colorama主要用来给文本添加各种颜色,并且非常简单易用. Prettytable主要用于在终端或浏览器端构建格式 ...
- python基础代码库-Python基础数据处理库-NumPy
最近更新:2017-07-19 NumPy是Python做数据处理的底层库,是高性能科学计算和数据分析的基础,比如著名的Python机器学习库SKlearn就需要NumPy的支持.掌握NumPy的基础 ...
最新文章
- 3次握手中的最后一个ACK服务端收到了吗
- A folder failed to be moved——Android SDK的安装问题解决方案
- WIFI配置专项测试
- io操作是指什么_各种IO模型,一篇打尽
- 1732: 数花费(Kruscal)
- python参数默认值实例_Python中使用partial改变方法默认参数实例
- 微软服务器收费吗,了解 Azure 外部服务收费
- 配置 Sybase数据源
- TCP/IP笔记-Qt使用Win10pcap发送以太帧(理论与实践)
- 安卓actionbar上的搜索按钮点击没反应如何解决
- 论文笔记--跨媒体语义共享子空间学习理论与方法研究-2015
- Python selenium 延时的几种方法
- java 任务栏程序_如何为Java Swing程序动态启用或禁用任务栏图标
- 学会2种方法,小白也能快速产出标准的Axure原型
- 产品经理学习——Axure常用快捷键
- 树莓派搭建DLNA客户端,使用gmediarender,DLAN render。
- android高效ORM数据库框架greenDao使用
- Oracle视图(View)----------------数据库中虚拟的表
- Spring Boot整合websocket实现群聊,点对点聊天,图片发送,音频发送
- CSS之关于min-width、max-width、min-height和max-height的使用
热门文章
- java滚动条_java Swing界面优化JscrollPane滚动条教程
- 如何在html网页内引入css样式
- Ubuntu 中WPS乱码问题
- 计算机文化基础知识梳理,计算机文化基础知识梳理.doc
- SQL Server中row_number函数用法介绍
- 如果去掉数学前后的空格_如何取消excel表格中数据前的空格-Excel 如何去除单元格中数字前后的空格...
- 导出数据Excel打开乱码问题
- 关于Python中面向对象的理解
- HDUOJ 2087 剪花布条
- 常用的数字正则(严格匹配)