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. [No000093]按住Alt 再按数字键敲出任意汉字和字符!

    1.在notepad里,(中文系统下) 按住Alt 然后按52946最后放开Alt 按住Alt 然后按45230最后放开Alt 按住Alt 然后按50403最后放开Alt 你会看到"我爱你& ...

  2. Windows 下 zip 版的 MySQL 的安装

     创建 配置文件 当 MySQL server 启动时,它会在按照下表列出位置的顺序寻找并读取配置文件: File Name Purpose %PROGRAMDATA%\MySQL\MySQL Ser ...

  3. DataSet与DataTable与DataRow的关系

    1. 添加数据: DataRow dr=MyDataSet.Tables["User"].NewRow(); dr["UserName"] = "张三 ...

  4. 用vue.js学习es6(三):数组、对象和函数的解构

    一.数组的解构: 以前的方式: var arr = [1,2,3]; console.log(arr[0]); //1 console.log(arr[1]); //2 现在的方式: var [a,b ...

  5. 教你一招:解决win10/win8.1系统在安装、卸载软件时出现2502、2503错误代码的问题

    经常遇到win10/win8.1系统在安装.卸载软件时出现2502.2503错误代码的问题. 解决办法: 1.打开任务管理器后,切换到“详细信息”选项卡,找到explore.exe这个进程,然后结束进 ...

  6. 用flashfxp做ftp镜像同步

    简单说,用flashfxp建立上传或者下载队列,然后设定一个定时任务来处理这个队列,就能同步下载或上传制定的目录了. 本人遇到的需求只需要做同步上传,记录如下: 开发者PC ---> 国内win ...

  7. 【BZOJ-2597】剪刀石头布 最小费用最大流

    2597: [Wc2007]剪刀石头布 Time Limit: 20 Sec  Memory Limit: 128 MBSec  Special JudgeSubmit: 1016  Solved:  ...

  8. Python 【第六章】:Python操作 RabbitMQ、Redis、Memcache、SQLAlchemy

    Memcached Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态.数据库驱动网站的速度 ...

  9. django进阶

    路由系统 简而言之,django的路由系统作用就是使views里面处理数据的函数与请求的url建立映射关系.使请求到来之后,根据urls.py里的关系条目,去查找到与请求对应的处理方法,从而返回给客户 ...

  10. Beta阶段项目终审报告

    先上图 设想和目标 我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述? 我们的软件主要是用来解决玩狼人杀这款桌游时无牌.无法官.游戏流程不熟悉等情况的.我觉得我们对典型 ...