1. 加载 Windows API 和 C 运行库

先看例子

from ctypes import *
u32 = windll.LoadLibrary('user32.dll') #加载user32.dll
u32.MessageBoxW(0, u'内容', u'标题',0)
crt = cdll.LoadLibrary('msvcrt.dll') #加载C运行库
crt.printf('hello world !\n')

调用 C 库用 cdll,而调用 Windows API 用 windll。

之所以有 cdll 和 windll 的区别,是因为 Windows API 和 C 库函数的调用规范不一样,前者遵从的是 __stdcall,后者是 __cdecl。

__cdecl 是 C Declaration 的缩写,表示 C 编译器默认的函数调用规范,这种方式下由调用者负责清理参数栈。比如在VC中,如果函数前面不加任何修饰,默认的就是 __cdecl。

__stdcall 是 Standard Call 的缩写,这种方式下被调的函数自己负责清理参数栈。在 VC 中,如果函数前有 __stdcall 修饰的话,编译出来的就是这种类型。

Windows API 全部采用的是 __stdcall 。因为 Windows API 要被各种编译器下的各种语言调用,如果采用 __cdecl 的方式,由于各种编译器中使用的栈结构也许并不相同,所以就不敢保证其每种编译器都能正确清理 Windows API 产生的参数栈。而采用 __stdcall 的方式,由被调用者自己清理参数栈,就能避免了以上麻烦。

在 Windows 环境,LoadLibrary() 的参数可以不包括扩展名。还有一种更简短的写法,比如下面三个是等效的:

from ctypes import *
windll.LoadLibrary('user32.dll').MessageBoxW(0, u'内容', u'标题',0)
windll.LoadLibrary('user32').MessageBoxW(0, u'内容', u'标题',0)
windll.user32.MessageBoxW(0, u'内容', u'标题',0)

下面三个也一样

from ctypes import *
cdll.LoadLibrary('msvcrt.dll').printf('hello world !\n')
cdll.LoadLibrary('msvcrt').printf('hello world !\n')
cdll.msvcrt.printf('hello world !\n')

2. 隐式类型转换

int、str、unicode,这三种类型是可以直接作为参数传递的,不用指明对应的C类型

from ctypes import *
cdll.msvcrt.printf('%d, %s\n', 100, 'abc')

3. 显式类型转换

int、str、unicode 之外的类型在作为参数时要做显式类型转换,指出对应的C类型

from ctypes import *
cdll.msvcrt.printf('%.2f\n', c_double(3.14))

4. 指针和引用

byref() 可以引用内存地址,相当于 C 的 &

from ctypes import *
i = c_int(0)
cdll.msvcrt.sscanf('100', '%d', byref(i))
print i.value

pointer() 也可以达到相同的效果

from ctypes import *
i = c_int(0)
cdll.msvcrt.sscanf('100', '%d', pointer(i))
print i.value

二者的区别在于,pointer() 会构造一个指针对象,而 byref() 只是一个函数,所以 byref() 的开销较小。

如果只是用于传递参数,那么用 byref() 就足够了。

5. 字符串指针

直接用 create_string_buffer() 和 create_unicode_buffer()

from ctypes import *
s = create_string_buffer('abc') #初始值abc
cdll.msvcrt.sscanf('def', '%s', s)
print s.value #现在是def

6. 回调函数

为了测试回调函数,先编写一个 test.dll,下面是 C 代码

 #include <stdio.h>
typedef void __stdcall (*MyCallback)(int);
__declspec(dllexport) void __stdcall TestMyCallback( int i, MyCallback mycallback)
{
printf("TestMyCallback:%d\n", i);
mycallback(i*10);
}

上面这个函数接受两个参数,第一个是整型变量 i,第二个是一个回调函数。

首先打印变量 i,然后再把 i*10 传给回调函数。

( __declspec(dllexport) 关键字用于从dll导出函数,详见这里 )

下面的Python代码中实现了回调函数,并传给了test.TestMyCallbackPython

from ctypes import *
def mycallback( i ):
print 'mycallback:', i
c_mycallback = WINFUNCTYPE(None, #返回值类型 None 代表 void
c_int #第一个参数类型 int
)(mycallback) #括号内是需要被包装的Python函数
windll.test.TestMyCallback(100, c_mycallback)

WINFUNCTYPE 的用法实际包括两个步骤:

  • 首先要声明一个 C 函数原型
c_function_type = WINFUNCTYPE(返回值类型,第一个参数类型,第二个参数类型...)
  • 然后再用这个原型包装 Python 函数
c_function = c_function_type(Python函数)

WINFUNCTYPE 遵守 __stdcall 调用规范,对应的 __cdecl 版本是 CFUNCTYPE

ctypes 调用 dll的更多相关文章

  1. ctypes库调用dll的个人见解

    最近着手开发一个小东西涉及到了API接口的知识点, 第一次使用到了ctypes库,在网上找了一大圈,基本都是讲add.dll之后就没了. 就像下面这个: from ctypes import * dl ...

  2. Python调用DLL动态链接库——ctypes使用

    最近要使用python调用C++编译生成的DLL动态链接库,因此学习了一下ctypes库的基本使用. ctypes是一个用于Python的外部函数库,它提供C兼容的数据类型,并允许在DLL或共享库中调 ...

  3. python调用dll方法

    在python中调用dll文件中的接口比较简单,实例代码如下: 如我们有一个test.dll文件,内部定义如下: extern "C"{ int __stdcall test( v ...

  4. Python手机开发调用DLL实现部分ADB功能-乾颐堂

    近期学了一点Python,然后正好有一个手机同步工具方面的预研工作要完成. 要实现PC与手机的通信,首先要找到他们的通信协议,还好的是Android有完善的协议:ADB ADB的代码是开源的,而且支持 ...

  5. python调用dll详解

    参考链接https://www.cnblogs.com/TQCAI/p/8881530.html https://www.jb51.net/article/52513.htm https://www. ...

  6. Windows平台Go调用DLL的坑

    最近的项目中,使用了GO来开发一些服务中转程序.业务比较简单,但是有一些业务需要复用原有C++开发的代码.而在WINDOWS,用CGO方式来集成C/C++代码并不是太方便.所以用DLL把C++的代码封 ...

  7. Java 调用 C++ (Java 调用 dll)康哥手把手教你

    摘要: 本文原创,转载请注明地址 http://www.cnblogs.com/baokang/p/4979243.html 因为要做点图形处理的项目,需要在Java中调用dll库,所以开发的第一步是 ...

  8. 【转】C#调用DLL

    C#中如何调用动态链接库DLL(转)     每种编程语言调用DLL的方法都不尽相同,在此只对用C#调用DLL的方法进行介绍.首先,您需要了解什么是托管,什么是非托管.一般可以认为:非托管代码主要是基 ...

  9. C#程序实现动态调用DLL的研究(转)

    摘 要:在<csdn开发高手>2004年第03期中的<化功大法——将DLL嵌入EXE>一文,介绍了如何把一个动态链接库作为一个资源嵌入到可执行文件,在可执行文件运行时,自动从资 ...

随机推荐

  1. Transient的作用

    1:transient的作用及其使用方法 当一个对象实现类Serilizable接口,那么这个类就可以被序列化,java的这种序列化的模式为开发者提供了很多的便利. 然而在实际开发中,我们常常遇到这样 ...

  2. [No0000A3]护眼谎言大揭秘,选择正确的方式保护眼睛!

    当眼睛因为过度劳累而状况频出的时候,许多人没有选择极目远眺.眼保健操.充分睡眠等简单易行的养眼方式,而是求助于各种护眼工具.于是,在视疲劳成为常见眼病之后,护眼市场产品层出不穷:护眼灯.眼贴.眼保仪. ...

  3. codevs 1285 二叉查找树STL基本用法

    C++STL库的set就是一个二叉查找树,并且支持结构体. 在写结构体式的二叉查找树时,需要在结构体里面定义操作符 < ,因为需要比较. set经常会用到迭代器,这里说明一下迭代器:可以类似的把 ...

  4. SQL Server 中master..spt_values的应用

    今天在做数据分析报表的时候遇到一个这样的问题. 表结构如下.部门编码.部门名称.部门人员ID(中间用逗号分割) 我想通过和人员表链接,查询出一个新的数据集,查询出的结果集格式如下:人员信息(ID或者姓 ...

  5. css

    1.css代码语法 p{font-size:12px;color:red;}p 为选择符:又称选择器,指明网页中要应用样式规则的元素,如本例中是网页中所有的段(p)的文字将变成蓝色,而其他的元素(如o ...

  6. VS2013快捷键及技巧

    VS2013快捷键很多,灵活使用常用快捷键及各项技巧可以让你事半功倍.下面的visual studio 2013快捷键和操作技巧你知道多少? 1.回到上一个光标位置/前进到下一个光标位置 1)回到上一 ...

  7. 打包SpringBoot工程并部署

    使用工具:Eclipse Linux下JDK版本:jdk-7u79-linux-x64.tar.gz 一.打包成jar并部署 步骤如下: 首先上pom.xml: <project xmlns=& ...

  8. vs2013中2.0类库提示是英文,解决方案

     将C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\zh-Hans下的XML文件复 ...

  9. Markdown

    1. 斜体和粗体 代码: *斜体*或_斜体_ **粗体** ***加粗斜体*** ~~删除线~~ 显示效果: 这是一段斜体 这是一段粗体 这是一段加粗斜体 这是一段删除线 2. 分级标题 第一种写法: ...

  10. 【USACO 3.1】Stamps (完全背包)

    题意:给你n种价值不同的邮票,最大的不超过10000元,一次最多贴k张,求1到多少都能被表示出来?n≤50,k≤200. 题解:dp[i]表示i元最少可以用几张邮票表示,那么对于价值a的邮票,可以推出 ...