首页 > 编程语言 >厉害了,Python也能使用动态链接库

厉害了,Python也能使用动态链接库

时间:2023-04-23 14:36:19浏览次数:47  
标签:aa Python 厉害 windll dll int ctypes 动态链接库 指针


未谙姑食性,先遣小姑尝。

大家好,我是Python进阶者。

前言

动态链接库(DLL)想必大家都不陌生了吧,C/C++编程经常会用到,那么,它跟我们的Python有什么关系?要说关系恐怕也就是Python是用C写的了,不过,还有一点更重要的关系,那就是Python可以调用C函数,这一点,在Pywin32中有所体现。下面我们就来详细了解下到底Python使用动态链接库是怎么回事吧。

一、神秘的模块

我们都知道C语言是比较靠近底层的语言,所以我们要想使用动态链接库就要给Python和C构建一座桥梁。今天我们要说的这座桥梁就是---Ctypes。

二、安装并导入Ctypes

系统自带有该模块,若没有,安装Pywin32也就有了。

from ctypes import *

三、认识动态链接库

动态链接库在linux 系统中表现为“.so”的后缀文件,而在Windows中表现为”.dll“的后缀文件。

四、初步了解Ctypes

安装好后我们需要对Ctypes做一个大致的了解,首先我们得先查看它的所有函数及其方法。

厉害了,Python也能使用动态链接库_c++

接下来我们就将对这些方法做一个简单的了解,并且配上一个小实例让大家看了之后更容易懂,让天底下不再有十分艰难的编程问题存在是我们的宗旨。

五、Ctypes的基本用法

1).数据类型

Ctypes很完美的契合了C的数据类型,丰富多样,下面我们来看看吧:

ctypes 类型

C 类型

Python 类型

c_bool

_Bool

bool (1)

c_char

char

单字符字节对象

c_wchar

wchar_t

单字符字符串

c_byte

char

整型

c_ubyte

unsigned char

整型

c_short

short

整型

c_ushort

unsigned short

整型

c_int

int

整型

c_uint

unsigned int

整型

c_long

long

整型

c_ulong

unsigned long

整型

c_longlong

__int64 或 long long

整型

c_ulonglong

unsigned __int64 或 unsigned long long

整型

c_size_t

size_t

整型

c_ssize_t

ssize_t 或 Py_ssize_t

整型

c_float

float

浮点数

c_double

double

浮点数

c_longdouble

long double

浮点数

c_char_p

char * (以 NUL 结尾)

字节串对象或 None

c_wchar_p

wchar_t * (以 NUL 结尾)

字符串或 None

c_void_p

void *

int 或 None

我们可以看到这些都是可以在Python中使用的C语言数据类型。我们一起来看看它的用处吧:

厉害了,Python也能使用动态链接库_编程语言_02

我们可以看到这些基本就是数据类型的使用方法了,也是挺简单的。

2).操作变量

刚刚我们定义了许多类型的变量,下面我们像使用C语言变量一样来使用它们。

1)).访问变量的值
it.value
2)).修改变量的值
it.value=43  #直接赋值,即可修改
3)).给变量添加指针
#强指针
pt=pointer(it)  pointer(c_int(10)) #定义指针,指向变量 pt,pt是指针内存地址
pt.contents     #指针所指的对象
#弱指针 比强指针速度快
byref(it,4) #it:c的实例  4:偏移量
#返回一个指针的图片来做一个C实例,只可用作为函数参数

厉害了,Python也能使用动态链接库_c++_03

上面的指针只是简单的创建了指针实例,还有一种方法可以指定指针类型,然后进行创建:

aa=POINTER(c_int) #创建指针
aa(c_int(43))     #创建指针实例
aa(c_int(43)).contents.value#获取指针的值

也可以创建一个空指针:

POINTER(c_int)() #创建空指针,空指针是一个bool值

厉害了,Python也能使用动态链接库_编程语言_04

可以看出空指针没有Contents属性。

也可以使用抽象基类“_Pointer”来完成指针的操作:

import ctypes
class ss(ctypes._Pointer): #这里必须带上ctypes,否则会报错
    _type_=c_int
    contents=c_float
aa=ss(c_int(10)) #指定对象类型为整形
print(aa.contents) #替换为浮点类型

厉害了,Python也能使用动态链接库_linux_05

3).创建修改缓冲

Ctypes定义的指针类型是不可以修改的,如果需要在C函数中被修改,需要使用一些函数来修改,下面来看看:

1)).字符缓冲
p=create_string_buffer(4)           #创建一个4字节缓冲区 初始化为空字节
create_string_buffer(b"Hello")      #创建一个包含空字符结尾字符串缓冲区
create_string_buffer(b"Hello", 10)   #创建一个10字节缓冲区
print(sizeof(p),repr(p.raw)) #内存块大小 字节信息
2)).unicode缓冲
a=create_unicode_buffer(5)  #创建一个10字节的unicode字符缓冲区
create_unicode_buffer('ffsa') 
create_unicode_buffer('ffsa',5) #结尾空字符
print(sizeof(a))  #内存块大小

厉害了,Python也能使用动态链接库_java_06

4).调用动态链接库

动态链接库的调用方法很多,任你挑选。

CDLL(xx.dll)
OleDLL(xx.dll)
PyDLL(xx.dll)
WinDLL(xx.dll)
cdll.LoadLibrary(xx.dll)
oledll.LoadLibrary(xx.dll)
pydll.LoadLibrary(xx.dll)
windll.LoadLibrary(xx.dll)
#也可使用链接库读取器LibraryLoader,它也同样支持上面的八种方式
LibraryLoader(CDLL('C:\\Windows\\System32\\user32.dll'))
LibraryLoader(cdll.LoadLibrary('C:\\Windows\\System32\\user32.dll'))

综上所述,调用动态链接库的方法共有16种之多。

5).查找动态链接库

from ctypes.util import find_library
find_library('user32')# 查找

厉害了,Python也能使用动态链接库_java_07

6).调用动态链接库函数

dll=windll.LoadLibrary(xx.dll)
dll.函数名

7).Windows Api函数

所有的Window Api 函数都包含在Dll中,其中有几个非常重要的Dll:

kernel32.dll    #用于管理内存、进程和线程的各个函数
user32.dll      #用于创建用户界面的各个函数
gdi32.dll       #用于画图和显示文本的各个函数
advapi32.dll    #用于操作注册表、系统终止与重启、Windows服务启动/停止/创建、账户管理的各个函数
shell32.dll     #用于访问操作系统shell提供的功能
netapi32.dll    #用于访问操作系统提供的各种通信功能
comctl32.dll    #用于访问操作系统的状态条、进度条、工具条等功能
comdlg32.dll    #用于管理文件打开、文件保存、颜色字体选择等标准对话框


#调用这些dll非常简单:
windll.gdi32  #即可

8).获取模块头

windll.kernel32#同时导出同一个函数的ANSI版本(GetModuleHandleA)和UNICODE版(GetModuleHandleW)
windll.kernel32.GetModuleHandleA(0) #里面只允许有0或者None,其它会报错
windll.kernel32.GetModuleHandleW()

9).Dll基本信息获取

当我们读取Dll后有时候需要对它的路径或者句柄进行操作,这个时候我们需要获取这些参数:

1)).获取窗口句柄
dll._handle
2)).获取Dll绝对路径
dll._name

10).调用Python中的Os模块中的所有方法

这个自是不必说,与Os模块密切相关。

os=ctypes._os
os.getcwd()

11).打印字符长度

windll.msvcrt.printf(b'fsfs') #不支持中文输出 输出为4
windll.msvcrt.printf('fsfs')  #输出为2
#以下情况与上相同
cdll.msvcrt.printf('fsfs')
pydll.msvcrt.printf('fsfs')
oledll.msvcrt.printf('fsfs')

12).获取C实例内部缓冲区地址

addressof(c_int(30))

13).判断是否有管理员权限

windll.shell32.IsUserAnAdmin()
cdll.shell32.IsUserAnAdmin()
pydll.shell32.IsUserAnAdmin()
oledll.shell32.IsUserAnAdmin()
#结果返回1则表示有,否则无

通过判断当前用户是否为管理员用户后,我可以进行下一步操作,如果是则打开浏览器,如果不是则尝试打开:

import ctypes, sys
def admin():
    try:
        aa=ctypes.windll.shell32.IsUserAnAdmin()
    except:
        return
    finally:
        return aa
if admin()==1:
    os.popen(r'E:\360browser\360se6\Application\360se.exe')
else:
    if sys.version_info[0] == 3:
        ctypes.windll.shell32.ShellExecuteW(0, "runas", sys.executable,'',__file__,0)
    else:
        print('版本太低,不支持2.0')
        
#sys.executable:python主程序的绝对路径
#__file__:当前程序的绝对路径

14).让鼠标键盘失灵

aa=cdll.LoadLibrary('C:\\Windows\\System32\\user32.dll')
aa.BlockInput(True)#键盘鼠标被锁定
time.sleep(5) # 锁定五秒
aa.BlockInput(False)  #被释放

15).打开文件或者应用程序

aa=windll.shell32.ShellExecuteW(0,'open','C:\\Windows\\System32\\notepad.exe','',os.path.join(os.path.dirname(__file__),'OSI.txt'),0)
#参数1:父窗口的句柄,如果没有父窗口,则为0
#参数2:要运行的操作,如:runas,open,print
#参数3:要运行的程序绝对路径
#参数4:给程序传递的参数,如果打开的是文件则为空
#参数5:要打开的文件的绝对路径
#参数6:是否显示窗口 0:后台打开 1:前台打开
#如果aa返回值小于32则表示打开失败
aa.bit_length()#指定数值的二进制的长度数、宽度数

16).结构体和联合体

要想使用必须从Structure 和 Union 继承,子类必须定义,Fields 属性,Fields属性必须是一个二元组的列表。

元祖中第一个是变量名,第二个是数据类型,可以是Ctypes任意一种变量类型。

1))结构体Structure

厉害了,Python也能使用动态链接库_linux_08

也可以一次性传递多个不同数据类型的参数:

厉害了,Python也能使用动态链接库_linux_09

2)).联合体Union

它和结构体的使用方法一样,但是结果不同,下面我们来看下:

厉害了,Python也能使用动态链接库_java_10

造成这一区别的原因联合体所有成员变量共用一块内存,可以内存复用;而结构体,每个成员变量占用一块内存。

17).数组操作

ARRAY(type,len) #前者是Ctypes的某个类型的值,而后者是值的长度,返回一个值与长度的乘积
Array(*args) #它是一个数组抽象基类, 我们可以重写它来进行使用,
class cx(Array):
    _length_=100  #重写方法_length_(数组中元素的数量)
    _type_=c_int  #指定数组中每个元素的类型(整形)
aa=cx(12,32,43,324,54,4,32,34,52434)
print(aa[2],aa[5:7]) #利用下标或者切片访问

厉害了,Python也能使用动态链接库_python_11

厉害了,Python也能使用动态链接库_linux_12

18).改变对象的内存缓冲区大小

Ctypes可以重新设置对象的内存缓冲区大小:

厉害了,Python也能使用动态链接库_java_13

可以看到,此时的同一个对象内存缓冲区大小不一样

19).转换指向不同数据类型的指针

这里我们用到了一个神器的函数“Cast”,它可以将不同的数据类型的指针进行转换。

class bb(Structure):
    _fields_ = [("val", POINTER(c_int))]
b=bb()
b.val=cast((c_float*4)(1.2,3.2,4.3),POINTER(c_int))
print(b.val[1])

厉害了,Python也能使用动态链接库_c++_14

20).进程操作

aa=0x00000020 #定义正常优先级的类
ker=windll.LoadLibrary('kernel32.dll') #加载动态链接库
crk=ker.CreateProcessA #获得创建后的进程的函数地址
rk=ker.ReadProcessMemory #获得读取进程内存的函数地址
wk=ker.WriteProcessMemory #获得写入进程内存的函数地址
ker.TerminateProcess    #终止进程
print(ker,'\n',crk,'\n',rk,'\n',wk)

厉害了,Python也能使用动态链接库_linux_15

21).调用Window系统Api

就以Window 弹框函数MessageBox为例。

from ctypes import c_int, WINFUNCTYPE, windll
from ctypes.wintypes import HWND, LPCWSTR, UINT
tp = WINFUNCTYPE(c_int, HWND, LPCWSTR, LPCWSTR, UINT) #window函数类型
bt = (1, "hwnd", 0), (1, "text", "我是内容"), (1, "caption", "我是标题"), (1, "type", 0)
MessageBox =tp(("MessageBoxW", windll.user32),bt)  #调用消息弹框函数
MessageBox()
ab=MessageBox(caption='提示',text="我也是内容") #设置弹框参数
aa=MessageBox(caption='警告',type=2,text="是否进入?")
if aa==3:  #判断输出类型
    print('终止操作')
if aa==4:
    print('重试')
if aa==5:
    print('忽略')

厉害了,Python也能使用动态链接库_编程语言_16

厉害了,Python也能使用动态链接库_编程语言_17

厉害了,Python也能使用动态链接库_编程语言_18

厉害了,Python也能使用动态链接库_c++_19

可以看到当我选择了中止之后它便提示终止操作。其实还有一种类似的方法:

厉害了,Python也能使用动态链接库_c++_20

注:这里弹窗的参数均不支持中文,务必按我上面的格式写,不然会出错。

22).获取Windows窗口中的所有顶层窗口的值

from ctypes import *
from ctypes import wintypes
#定义回调函数类型
res= WINFUNCTYPE(wintypes.BOOL,wintypes.HWND, wintypes.LPARAM)
def win(h,p):#实现回调函数功能,函数为bool类型;h:顶层窗口的句柄  p:应用程序定义的一个值,
    le= user32.GetWindowTextLengthW(h)+1  #获取window文本长度,加1可获取完整的信息
    buffer = create_unicode_buffer(le) #创建缓冲
    user32.GetWindowTextW(h, buffer, le)  #获取window文本
    print(buffer.value)  #获取缓冲区中的信息
    return True
user32 = windll.LoadLibrary('user32.dll')#加载dll
user32.EnumWindows(res(win), 0) #枚举顶层窗口,不列举子窗口

厉害了,Python也能使用动态链接库_linux_21

六、总结

Ctypes总的来说还是蛮不错的,挺适合C语言小白学习,毕竟Api的确太多太复杂了,还是Python友好点。


标签:aa,Python,厉害,windll,dll,int,ctypes,动态链接库,指针
From: https://blog.51cto.com/u_13389043/6217584

相关文章

  • Python能不能只选择合并一个excel当中指定的sheet 当中指定的列呢?
    春风桃李花开日,秋雨梧桐叶落时。大家好,我是皮皮。一、前言前几天在Python钻石交流群【不争】问了一个Python自动化办公的基础问题,这里拿出来给大家分享下。二、实现过程这里【瑜亮老师】、【狂吃山楂片】、【一级大头虾选手】给了一个解决思路,如下图所示:方法有很多。【瑜亮老师】......
  • Python正则怎么匹配\啊?
    玉容寂寞泪阑干,梨花一枝春带雨。大家好,我是皮皮。一、前言前几天在Python白银交流群【膨胀西瓜汁】问了一个Python正则表达式的问题,这里拿出来给大家分享下。下面是匹配的结果:二、实现过程这里【论草莓如何成为冻干莓】给了一个思路,在前面加个r,防止转义。后来发现\5不是反斜杠。......
  • 盘点几道Python面试题【ChatGPT作答】
    风吹仙袂飘飖举,犹似霓裳羽衣舞。大家好,我是皮皮。一、前言前几天在Python白银交流群看到了几道Python基础题目,这里拿出来给大家分享下,感兴趣的小伙伴可以学习学习。1、字典、元组、列表、集合的区别是什么?2、什么是装饰器,怎么用?3、为什么要有闭包?4、什么是订阅发布模式,写一个demo5......
  • Python生成requirements.txt方法
    Python生成requirements.txt方法 requirements.txt可以通过pip命令自动生成和安装,这种情况更适用于此项目是单独的虚拟python环境,生成requirements.txt文件。安装requirements.txt依赖:pipinstall-rrequirements.txt1.方法1:生成requirements.txt文件pipfreeze>re......
  • 浅析python中装饰器原理
    一、什么叫装饰器?装饰器(decorator)的本质是一个函数,它接收一个函数作为参数,并返回一个被装饰过后的函数二、装饰器的作用?在不修改被修饰过函数的代码和和调用方式的情况下,给被装饰函数增加额外的功能三、装饰器的应用场景?插入日志、性能测试、事务处理、缓存、权限校验等场景......
  • Python_9 py文件导入和路径处理
    一、查缺补漏Python中两个值交换可以直接交换如:a,b=b,a冒泡就是从小到大排序,因为越到后越大自动导包也适用于自己创建的模块关于正斜杠和反斜杠https://www.cnblogs.com/yangjian319/p/4801675.html 5. 内置异常汇总https://www.cnblogs.com/nmb-musen/p/108......
  • Python之多进程与多线程
     1.1线程frommultiprocessingimportProcessdefshow(name):print("Processnameis"+name)if__name__=="__main__":proc=Process(target=show,args=('subprocess',))proc.start()proc.join()......
  • python 操作阿里云oss 对象存储上传下载文件
    python操作阿里云对象存储oss阿里云账号要有操作oss权限安装OSSSDKpipinstalloss2上传文件#-*-coding:utf-8-*-importoss2importos#阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控......
  • 给定一个正整数字符串,使用Python正则表达式在其千分位上添加逗号
    大家好,我是皮皮。一、前言前几天在Python黄金青铜群【沐】问了一个Python正则表达式的问题,这里拿出来给大家分享下。题目:给定一个正整数字符串,如12345678、345678,使用正则表达式在其千分位上添加逗号,如12,345,678、345,678。二、实现过程这里ChatGPT给了一个答案,可以使用......
  • python+playwright 学习-55 在docker 容器中运行playwright脚本
    前言Dockerfile.focal可用于在Docker环境中运行Playwright脚本。这些镜像包括在Docker容器中运行浏览器所需的所有依赖项,还包括浏览器本身。playwright在linux系统上目前只支持Ubuntu系统的部分版本,centos和debian系统上是没法运行的。镜像查看所有可用的图像标签ht......