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. [No000095].Net入门级逆向工程-1.SpreadsheetGear汉化

    一.SpreadsheetGear汉化 最近,我们的有个项目要用到表格功能,由于历(la)史(ji)遗(kuang)留(jia)的问题,我们不得不用第三方的一个插件SpreadsheetGear来实现 ...

  2. 4种解决json日期格式问题的办法

    4种解决json日期格式问题的办法   开发中有时候需要从服务器端返回json格式的数据,在后台代码中如果有DateTime类型的数据使用系统自带的工具类序列化后将得到一个很长的数字表示日期数据,如下 ...

  3. [LeetCode] Design Snake Game 设计贪吃蛇游戏

    Design a Snake game that is played on a device with screen size = width x height. Play the game onli ...

  4. 如何在ASP.NET Core中应用Entity Framework

    注:本文提到的代码示例下载地址> How to using Entity Framework DB first in ASP.NET Core 如何在ASP.NET Core中应用Entity ...

  5. 学券制(教育券、school voucher)

    美国「学券制」是怎样的一种制度?它为什么是共和党的执政政策?它在美国及其它地区有实施吗?效果如何?能否在保证公平的同时,通过市场提高教育质量? 作者:冉筱韬链接:https://www.zhihu.c ...

  6. Pairwise Sum and Divide 51nod

      1305 Pairwise Sum and Divide 题目来源: HackerRank 基准时间限制:1 秒 空间限制:131072 KB 分值: 5 难度:1级算法题  收藏  关注 有这样 ...

  7. [翻译] Android是怎样绘制视图的

    原文:How Android Draws Views 当一个Activity获取到焦点的时候,它的布局就开始被绘制. 绘制的过程由Android framework处理.但布局层级的根节点必须由Act ...

  8. SQL基础语法(三)

    SQL WHERE 子句 WHERE 子句用于规定选择的标准. WHERE 子句 如需有条件地从表中选取数据,可将 WHERE 子句添加到 SELECT 语句. 语法SELECT 列名称 FROM 表 ...

  9. 使用Xunit进行单元测试

    http://xunit.github.io/docs/getting-started-desktop.html 1. 新建一个类库项目 2. 通过NuGet引入xunit,Shouldly,xuni ...

  10. setTimeout和setInterval定时器使用详解测试

    var len=4; while(len--){ var time=setTimeout(function(){ console.log(len); },0); console.log(time); ...