cdll和windll的区别

Python要想调用C语言写的动态连接库,不仅要兼容C接口的调用习惯,还需要兼容C语言的数据类型。幸运的是ctypes库已经做了这两方面的工作,以便调用动态连接库是非常方便的。在Hello World的程序里,这行代码编写如下:

MessageBox = windll.user32.MessageBoxW

从这行代码的简洁程度来看,是非常优美的。这种优美是由于ctypes库在背后做了非常多的工作,比如windll其实是一个比较复杂的对象。在ctypes库里,它提供了三个容易加载动态连接库的对象:cdll、windll和oledll。通过访问这三个对象的属性,就可以调用动态连接库的函数了。其中cdll主要用来加载C语言调用方式(cdecl),windll主要用来加载WIN32调用方式(stdcall),而oledll使用WIN32调用方式(stdcall)且返回值是Windows里返回的HRESULT值。如果你以前没有学习过编程,肯定没有办法区分cdecl和stdcall,就算学习过编程,如果没有写过跨不同库之间的调用,也未必知道。因为在目前IDE的开发环境下,已经全部隐藏这些的细节。但在跨语言方面调用时,就不能忽略这种细节了。那么你也许问为什么会出现这两种调用方式,不是同一个动态连接库吗?对于这个问题,问得好。要回答这个问题,得从发明C语言那时候说起。在70年代,美国人丹尼斯·里奇发明了C语言,并且使用C语言编写UNIX,由此他就成为了C语言之父和UNIX操作系统之父。由于UNIX操作系统非常高效,修改起来也很方便,是得益于使用了C语言来编写。随着UNIX操作系统的推广,C语言也变成了一个流行的语言。要让UNIX变得高效率,那么C语言的设计上,就要着眼于高效的设计。在函数调用这方面的设计,就体现了这一点。在C语言的函数调用时,需要传送多个参数。这些参数的传送是可以通过寄存器或者栈来传送。那你也许问为什么不只使用寄存器这一种方式呢?由于函数调用的参数比较多,比如达到5个。并且在那时候的CPU的寄存器非常少,也满足不了这个要求。不像目前ARM或MIPS的CPU,寄存器比较多,多达13个之多。这时全部使用寄存器来传送参数是基本可以解决问题了。在当时的环境之下,设计的C语言的编译器都是按栈的方式来传递函数调用的参数,这样不但可以解决寄存器少的问题,也可以解决另外一个问题,就是可以动态地传递参数的个数。上面只是解决了个数的问题,那又出现了另外一个问题,就是参数的入栈的顺序问题。这个好比像学校里体育老师叫一班学生来排队,排头是从高到矮,还是从矮到高的选择。在入栈这个问题上,C语言也面临两个选择,一个跟代码的书写的顺序一样从左到右,另一个是从右到左。在考虑到动态参数的问题之后,C语言的设计者采用了从右到左的入栈方式,这种方式有两个优点:一是函数运行时,默认方式是从左到右,意味着出栈的方向应优先为栈顶的元素,这样可以提高运行效率;二是函数参数不定时,运行时分析字符串里出现需要的参数,每出现一个参数就弹出栈一次,跟运行分析的顺序一致。比如下面的函数声明:

printf(const char *,...);

由上可见入栈的顺序不同,调用的方式就不一样。在C语言里都是采用从右向左的方式入栈,在PASCAL语言里是从左向右入栈顺序的。在ctypes库里cdll、windll和oledll都是支持从右到左入栈的参数顺序。

接着下来又引出来了另外一个问题,既然参数是采用入栈的方式来传递,那么就会出现这种情况,当栈的参数没有使用到时,谁来清除,恢复栈的状态。在这个问题上,在编译器的设计者里又出现了两种选择:一种是倾向调用者清除,一种是倾向被调用者清除。这两种方式在性能上没有什么区别,只是安排清除的代码在不同的位置上。cdll是使用调用者清除的栈的方式,而windll和oledll是使用被调用者清除。这点就是它们之间的区别。因此,Python里调用动态连接库时,一定要清楚每个函数使用的调用方式,否则程序就会出问题,重则直接死掉。cdll和windll的区别如下图:

cdll和windll的区别

时间: 2024-10-11 13:28:24

cdll和windll的区别的相关文章

windll对象

回过头来,再看一下windll和oledll的区别,这两者之间最大的区别是oledll调用的函数都是固定返回HRESULT类型的值,而windll是不固定的类型的.在Python 3.3版本之前,都是返回命名为OSError类型错误,在这之后就返回命名为WindowsError类型错误.通一大段的讨论,我们彻底地了解cdll.windll和oledll之间的区别,为了更加清楚地记住它们,总结如下表所示: 表2-1: 对象名称 参数入栈顺序 清栈方式 返回值类型 cdll 从右向左 调用者 不固定

python—类对象和实例对象的区别

最近在对RF的通讯层的模块进行封装,需要将之前放在类似main里面的一个方法,如下所示:这段代码是开发提供,用于接口测试,模拟底层通讯,具体的通讯是在dll内,python这边只是做了个封装让RF进行调用.这段通讯层的代码实质上做了五件事: 第一:加载dll: 第二:初始化dll内的通讯参数: 第三:与服务器进行连接,创建session 第四:把数据senbuffer通过sessionManger发送给服务器 第五:取得的数据返回recibuffer def testlogin(ip,port,

lib 和 dll 的区别、生成以及使用详解

首先介绍一下静态库(静态链接库).动态库(动态链接库)的概念,首先两者都是代码共享的方式. 静态库:在链接步骤中,连接器将从库文件取得所需的代码,复制到生成的可执行文件中,这种库称为静态库,其特点是可执行文件中包含了库代码的一份完整拷贝:缺点就是被多次使用就会有多份冗余拷贝.即静态库中的指令都全部被直接包含在最终生成的 EXE 文件中了.在vs中新建生成静态库的工程,编译生成成功后,只产生一个.lib文件 动态库:动态链接库是一个包含可由多个程序同时使用的代码和数据的库,DLL不是可执行文件.动

lib 和 dll 的区别、生成以及使用详解(转)

原文章地址:https://www.cnblogs.com/TenosDoIt/p/3203137.html#c 首先介绍一下静态库(静态链接库).动态库(动态链接库)的概念,首先两者都是代码共享的方式. 静态库:在链接步骤中,连接器将从库文件取得所需的代码,复制到生成的可执行文件(链接过程就已经复制好了)中,这种库称为静态库,其特点是exe中包含了库代码的一份完整拷贝:缺点就是被多次使用就会有多份冗余拷贝.即静态库中的指令都全部被直接包含在最终生成的 EXE 文件中了.在vs中新建生成静态库的

Nginx 反代参数:$X-Real-Ip和$X-Forwarded-For的区别

## \$X-Real-Ip和$X-Forwarded-For的区别 标签(空格分隔): nignx 负载均衡 client-ip --- ####1.如果只有一层代理,这两个头的值就是一样的####2.多层代理> * X-Forwarded-For:  header包含这样一行        `*X-Forwarded-For: 1.1.1.1, 2.2.2.2, 3.3.3.3*`> * X-Real-Ip:没有相关标准,上面的例子,如果配置了X-Read-IP,可能会有两种情况`// 最

C#中Convert和parse的区别

Convert.ToInt32()与int.Parse()的区别(1)这两个方法的最大不同是它们对null值的处理方法: Convert.ToInt32(null)会返回0而不会产生任何异常,但int.Parse(null)则会产生异常. 没搞清楚Convert.ToInt32和int.Parse()的细细微区别时千万别乱用,否则可能会产生无法预料的结果,举例来说:假如从url中取一个参数page的值,我们知道这个值是一个int,所以即可以用Convert.ToInt32(Request.Que

python判断字符串,str函数isdigit、isdecimal、isnumeric的区别

s为字符串s.isalnum() 所有字符都是数字或者字母s.isalpha() 所有字符都是字母s.isdigit() 所有字符都是数字s.islower() 所有字符都是小写s.isupper() 所有字符都是大写s.istitle() 所有单词都是首字母大写,像标题s.isspace() 所有字符都是空白字符.\t.\n.\r 判断是整数还是浮点数a=123b=123.123 >>>isinstance(a,int)True>>>isinstance(b,floa

java web 过滤器跟拦截器的区别和使用

1.首先要明确什么是拦截器.什么是过滤器 1.1 什么是拦截器: 拦截器,在AOP(Aspect-Oriented Programming)中用于在某个方法或字段被访问之前,进行拦截然后在之前或之后加入某些操作.拦截是AOP的一种实现策略. 在Webwork的中文文档的解释为--拦截器是动态拦截Action调用的对象.它提供了一种机制可以使开发者可以定义在一个action执行的前后执行的代码,也可以在一个action执行前阻止其执行.同时也是提供了一种可以提取action中可重用的部分的方式.

mysql中int、bigint、smallint和tinyint的区别与长度

对比发现 int bigint smallint 和 tinyint 类型,如果创建新表时没有指定 int(M) 中的M时,默认分别是 : int             -------     int(11) bigint       -------     bigint(20) smallint   -------     smallint(6) tinyint     -------     tinyint(4) 下面是这几种类型的取值范围 参考:http://www.2cto.com/d