当前位置: 首页 > 科技观察

太好了,Python也可以使用动态链接库了

时间:2023-03-15 09:28:36 科技观察

动态链接库(DLL)想必大家都不陌生,C/C++编程经常用到,那跟我们的Python有什么关系呢?从关系上来说,恐怕Python是用C写的。不过,还有一个更重要的关系,那就是Python可以调用C函数,这一点在Pywin32中就有体现。让我们仔细看看Python是如何使用动态链接库的。1.神秘模块我们都知道C语言是比较接近底层语言的,所以如果我们要使用动态链接库,就必须在Python和C之间架起一座桥梁。今天我们要说的这座桥是---Ctypes。2.安装并导入Ctypes系统自带此模块。如果没有,请安装Pywin32。fromctypesimport*三、识别动态链接库动态链接库在Linux系统中显示为“.so”后缀文件,在Windows中显示为“.dll”后缀文件。4、初步了解Ctypes安装完成后,我们需要对Ctypes有个大概的了解。首先,我们必须检查它的所有功能和方法。下面我们就对这些方法做一个简单的了解,并附上一个小例子,让大家看完后更容易理解。我们的目标是让世界上不再存在非常困难的编程问题。五。Ctypes的基本使用1.数据类型Ctypes完美契合了C的数据类型,丰富多样。我们来看一下:我们可以看到,这些都是可以在Python中使用的C语言数据类型。我们来看看它的用处:可以看到,基本上就是这些数据类型的使用方法,而且非常简单。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:cinstance4:offset#返回一个指针的图片,做一个C实例,只能作为函数参数。上面的指针只是创建了一个指针实例。还有一种方法是先指定指针类型再创建:aa=POINTER(c_int)#创建指针aa(c_int(43))#创建指针实例aa(c_int(43)).contents.value#获取值指针并创建一个空指针:POINTER(c_int)()#创建一个空指针,它是一个bool值可以看出,空指针没有Contents属性。也可以使用抽象基类“_Pointer”完成指针操作:importctypesclassss(ctypes._Pointer):#这里必须带ctypes,否则会报错_type_=c_intcontents=c_floataa=ss(c_int(10))#指定对象类型为整数print(aa.contents)#替换为浮点类型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))#内存块sizebyteinformation(2)unicodebuffera=create_unicode_buffer(5)#创建一个10字的Section的unicode字符缓冲区create_unicode_buffer('ffsa')create_unicode_buffer('ffsa',5)#结束空字符print(sizeof(a))#内存块大小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.查找动态链接库fromctypes.utilimportfind_libraryfind_library('user32')#查找6.调用动态链接库函数dll=windll.LoadLibrary(xx.dll)dll。函数名称7.WindowsApi函数所有WindowApi函数都包含在Dll中,其中有几个非常重要的Dll:kernel32.dll#用来管理内存、进程和线程的函数user32.dll#用来创建用户界面的函数gdi32.dll#用于绘图和显示文本的各种功能advapi32.dll#用于操作注册表、系统终止和重启、Windows服务启动/停止/创建、账户管理功能shell32.dll#用于访问由操作系统shellnetapi32.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.dllbasic信息获取我们在读取Dll的时候,有时候需要对它的路径或者句柄进行操作。这时候我们需要获取这些参数:(1)获取窗口句柄dll._handle(2)获取Dll的绝对路径dll._name10。在Python中调用Os模块中的所有方法不用多说,它与Os模块密切相关。os=ctypes._osos.getcwd()11.打印字符长度windll.msvcrt.printf(b'fsfs')#不支持中文输出,输出为4windll.msvcrt.printf('fsfs')#输出为2#的以下条件与Sameascdll.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,表示有,否则不传给判断当前用户是否是admin用户,我可以进行下一步,如果是就打开浏览器,如果不是就试试:importctypes,sysdefadmin():try:aa=ctypes.windll.shell32.IsUserAnAdmin()除了:最后返回:returnaaifadmin()==1:os.popen(r'E:\360browser\360se6\Application\360se.exe')else:ifsys.version_info[0]==3:ctypes.windll.shell32.ShellExecuteW(0,"runas",sys.executable,'',__file__,0)else:print('版本太低,不支持2.0')#sys.可执行文件:python主程序绝对路径#__file__:当前程序的绝对路径14.让鼠标键盘失效aa=cdll.LoadLibrary('C:\\Windows\\System32\\user32.dll')aa.BlockInput(True)#Keyboardandmousearelockedtime.sleep(5)#锁定五秒aa.BlockInput(False)#Released15.打开文件或应用程序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,则表示openingfailedaa.bit_length()#指定值的二进制长宽16.Structure和union如果要用,必须继承Structure和Union,必须定义子类,Fields属性,Fields属性必须是两个tup的列表莱斯。元组中第一个是变量名,第二个是数据类型,可以是任何Ctypes的变量类型。(1)structure结构体也可以一次传递多个不同数据类型的参数:(2)Union的使用方法与结构体相同,只是结果不同。我们来看看造成这种差异的原因union的所有成员变量共享一块内存,可以重复使用;而在结构体中,每个成员变量都占用一块内存。17.数组操作ARRAY(type,len)#前者是Ctypes的某个类型的值,后者是值的长度,返回值与长度的乘积Array(*args)#是一个数组的抽象基类,我们可以改写使用,classcx(Array):_length_=100#改写方法_length_(数组中的元素个数)_type_=c_int#指定数组中每个元素的类型(integer)aa=cx(12,32,43,324,54,4,32,34,52434)print(aa[2],aa[5:7])#使用下标或者切片访问18.改变内存缓冲区对象的大小Ctypes可以重新设置对象内存缓冲区的大小:可以看到,此时同一个对象的内存缓冲区大小是不同的。19.将指针转换为不同的数据类型这里我们使用了一个神器函数“Cast”,它可以将不同数据类型的指针进行转换。类bb(结构):_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])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)21、调用Window系统Api,以Window弹出函数MessageBox为例。fromctypesimportc_int,WINFUNCTYPE,windllfromctypes.wintypesimportHWND,LPCWSTR,UINTtp=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='prompt',text="我也是内容")#设置弹框参数aa=MessageBox(caption='warning',type=2,text="是否进入?")ifaa==3:#判断输出类型print('终止操作')ifaa==4:print('重试')ifaa==5:print('忽略')可以看到当我选择Abort后提示终止操作。其实还有一个类似的方法:注意:这里的弹窗参数不支持中文,一定要按照上面的格式写,否则会出错。22.获取Windows窗口中所有顶层窗口的值fromctypesimport*fromctypesimportwintypes#定义回调函数typeres=WINFUNCTYPE(wintypes.BOOL,wintypes.HWND,wintypes.LPARAM)defwin(h,p):#实现回调函数,函数为bool类型;h:顶层窗口的句柄p:应用程序定义的一个值,le=user32.GetWindowTextLengthW(h)+1#获取窗口文本长度,加1得到完整信息buffer=create_unicode_buffer(le)#创建缓冲区user32.GetWindowTextW(h,buffer,le)#获取窗口文本print(buffer.value)#获取缓冲区中的信息returnTrueuser32=windll.LoadLibrary('user32.dll')#Loaddlluser32.EnumWindows(res(win),0)#Enumeratetop-levelwindows,donotlistsub-windows6.总结Ctypes总体来说还是比较不错的,很适合C语言初学者学习。毕竟,API太多太复杂了。它仍然对Python友好。