调用方法
最主要的是 from ctypes import * 里面包含了windll调用加载方法,具体用windll还是cdll加载动态库见百度
接下来就是
class GoString(Structure):
_fields_ = [('p', c_char_p),
('n', c_int)]
这段代码表示该类 Structure 继承自构造函数。ctypes 支持结构体的使用,从 Structure 类派生,数据放在 _fields_ 中
# encoding:utf-8
from ctypes import *
import pytest
dllfile= r'd:\py_scripts\pythondll\testsdk\testcase\spalibex_64.dll'
dll = windll.LoadLibrary(dllfile)
#dll=cdll.LoadLibrary(r'd:\py_scripts\pythondll\testsdk\testcase\spalibex_32.dll')
class GoString(Structure):
_fields_ = [('p', c_char_p),
('n', c_int)]
filesdir=GoString()
filesdir.p= r'd:\py_scripts\pythondll\testsdk\files'.encode()
filesdir.n=len(r'd:\py_scripts\pythondll\testsdk\files')
storage_init_func=dll.dc_storage_init
storage_init_func.restype = c_int #返回值的类型
storage_init_func.argtypes=[GoString] #参数的类型
storage_init=storage_init_func(filesdir) #调用参数
assert(storage_init==0)
# self.assertEqual(storage_init,0)
基础数据类型
ctypes 定义了一些和C兼容的基本数据类型:
_Boolcharwchar_tcharunsigned charshortunsigned shortintunsigned intlongunsigned long__int64long longunsigned __int64unsigned long longsize_tssize_tPy_ssize_tfloatdoublelong doublechar *Nonewchar_t *Nonevoid *None
- 创建可变的 string buffer
Python 默认的 string 是不可变的,所以不能传递 string 到一个 C 函数去改变它的内容,所以需要使用 create_string_buffer,对应 Unicode 字符串,要使用 create_unicode_buffer,
定义和用法如下,
>>> help(create_string_buffer)
Help on function create_string_buffer in module ctypes:
create_string_buffer(init, size=None)
create_string_buffer(aString) -> character array
create_string_buffer(anInteger) -> character array
create_string_buffer(aString, anInteger) -> character array
from ctypes import *
p = create_string_buffer(5)
print sizeof(p) # 5
print repr(p.raw) # '\x00\x00\x00\x00\x00'
p.raw = 'Hi'
print repr(p.raw) # 'Hi\x00\x00\x00'
print repr(p.value) # 'Hi'
- 传递自定义参数类型到 C 函数
ctypes 允许你创建自定义参数类型,它会自动去搜索自定义数据的 _as_parameter 属性,将其作为 C 函数的参数,例如,
import platform
from ctypes import *
if platform.system() == 'Windows':
libc = cdll.LoadLibrary('msvcrt.dll')
elif platform.system() == 'Linux':
libc = cdll.LoadLibrary('libc.so.6')
class Bottles(object):
def __init__(self, number):
self._as_parameter_ = number # here only accept integer, string, unicode string
bottles = Bottles(42)
libc.printf('%d bottles of beer\n', bottles)
输出,
42 bottles of beer
也可以为你的数据定义 _as_parameter 属性,如下,
import platform
from ctypes import *
if platform.system() == 'Windows':
libc = cdll.LoadLibrary('msvcrt.dll')
elif platform.system() == 'Linux':
libc = cdll.LoadLibrary('libc.so.6')
class Bottles(object):
def __init__(self):
self._as_parameter_ = None # only accept integer, string, unicode string
@property
def aspram(self):
return self._as_parameter_
@aspram.setter
def aspram(self, number):
self._as_parameter_ = number
bottles = Bottles()
bottles.aspram = 63
libc.printf('%d bottles of beer\n', bottles)
输出,
63 bottles of beer
- 指定 C 函数的参数类型
可以指定要调用 C 函数的参数类型,如果传入参数不符合指定的类型,则 ctypes 会尝试转换,如果转换不成功,则抛 ArgumentError,例如,
import platform
from ctypes import *
if platform.system() == 'Windows':
libc = cdll.LoadLibrary('msvcrt.dll')
elif platform.system() == 'Linux':
libc = cdll.LoadLibrary('libc.so.6')
libc.printf.argtypes = [c_char_p, c_char_p, c_int, c_double]
libc.printf('String is "%s", Int is %d, Double is %f\n', 'Hi', 10, 2.2)
libc.printf('%s, %d, %f\n', 'X', 2, 3)
try:
libc.printf("%d %d %d", 1, 2, 3)
except ArgumentError, e:
print "*** ERROR: %s" % str(e)
输出,
String is "Hi", Int is 10, Double is 2.200000
X, 2, 3.000000
*** ERROR: argument 2: <type 'exceptions.TypeError'>: wrong type
- 指定 C 函数的返回值类型
如果不指定 C 函数的返回值, ctypes 默认返回 int 类型,如果要返回特定类型,需要指定返回类型 restype,
例如,
import platform
from ctypes import *
if platform.system() == 'Windows':
libc = cdll.LoadLibrary('msvcrt.dll')
elif platform.system() == 'Linux':
libc = cdll.LoadLibrary('libc.so.6')
print '1->', libc.strchr('abcdefghij', c_char('d'))
libc.strchr.restype = c_char_p
print '2->', libc.strchr('abcdefghij', c_char('d'))
print '3->', libc.strchr('abcdefghij', 'd') # Note, here C function strchr not know what 'd' mean, so rerurn None
libc.strchr.argtypes = [c_char_p, c_char]
print '4->', libc.strchr('abcdefghij', 'd') # Note, here not use c_char('w')
输出:
1-> 40291315
2-> defghij
3-> None
4-> defghij
- 按引用传递参数
有些情况下,需要 C 函数修改传入的参数,或者参数过大不适合传值,需要按引用传递,ctypes 提供关键字 byref() 处理这种情况,
例如,
import platform
from ctypes import *
if platform.system() == 'Windows':
libc = cdll.LoadLibrary('msvcrt.dll')
elif platform.system() == 'Linux':
libc = cdll.LoadLibrary('libc.so.6')
i = c_int()
f = c_float()
s = create_string_buffer('\000' * 32)
print 'i.val =', i.value
print 'f.val =', f.value
print 'repr(s.value) =', repr(s.value)
libc.sscanf('1 3.14 Hello', '%d %f %s', byref(i), byref(f), s)
print 'after, i.val =', i.value
print 'after, f.val =', f.value
print 'after, repr(s.value) =', repr(s.value)
输出,
i.val = 0
f.val = 0.0
repr(s.value) = ''
after, i.val = 1
after, f.val = 3.1400001049
after, repr(s.value) = 'Hello'
- 使用结构体
ctypes 支持结构体的使用,从 Structure 类派生,数据放在 _fields_ 中,
例如,
class Point(Structure):
_fields_ = [('x', c_int), ('y', c_int)]
point = Point(10, 20)
print 'point.x =', point.x
print 'point.y =', point.y
point = Point(y=5)
print 'after, point.x =', point.x
print 'after, point.y =', point.y
class Rect(Structure):
_fields_ = [('upperleft', Point), ('lowerright', Point)]
rc = Rect(point)
print 'rc.upperleft.x = %d, rc.upperleft.y = %d' % (rc.upperleft.x, rc.upperleft.y)
print 'rc.lowerright.x = %d, rc.lowerright.y = %d' % (rc.lowerright.x, rc.lowerright.y)
r = Rect(Point(1, 2), Point(3, 4))
print 'r.upperleft.x = %d, r.upperleft.y = %d' % (r.upperleft.x, r.upperleft.y)
print 'r.lowerright.x = %d, r.lowerright.y = %d' % (r.lowerright.x, r.lowerright.y)
输出,
point.x = 10
point.y = 20
after, point.x = 0
after, point.y = 5
rc.upperleft.x = 0, rc.upperleft.y = 5
rc.lowerright.x = 0, rc.lowerright.y = 0
r.upperleft.x = 1, r.upperleft.y = 2
r.lowerright.x = 3, r.lowerright.y = 4
- 位域
ctypes 提供了对位域的支持,
例如,
class IntBit(Structure):
_fields_ = [('x', c_uint, 2), ('y', c_uint, 4)]
IB = IntBit(1, 15)
print 'IB.x = %d' % IB.x
print 'IB.y = %d' % IB.y
IB2 = IntBit(4, 16)
print '-> IB2.x = %d' % IB2.x
print '-> IB2.y = %d' % IB2.y
输出,
IB.x = 1
IB.y = 15
-> IB2.x = 0
-> IB2.y = 0
- 数组
ctypes 提供了对 Array 的支持,
例如,
TenIntArrayType = c_int * 10
ta = TenIntArrayType(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
for item in ta:
print item,
class PointEx(Structure):
_fields_ = [('x', c_int), ('y', c_int)]
class MyStruct(Structure):
_fields_ = [('a', c_int), ('b', c_int), ('pointex_array', PointEx * 4)]
ms = MyStruct(4, 5, ((1,1), (2,2), (3,3), (4,4)))
for item in ms.pointex_array:
print '(item.x, item.y) = (%d, %d)' % (item.x, item.y)
输出,
1 2 3 4 5 6 7 8 9 10
(item.x, item.y) = (1, 1)
(item.x, item.y) = (2, 2)
(item.x, item.y) = (3, 3)
(item.x, item.y) = (4, 4)
- 指针
ctypes 使用关键字 pointer 提供了对指针的支持,注意指针解引用使用 [0],
例如,
i = c_int(42)
print 'before, i.value =', i.value
pi = pointer(i)
pi[0] = 57
print 'after, i.value =', i.value
# create NULL pointer, also can use this way, but recommend use 'pointer' not 'POINTER'
null_ptr = POINTER(c_int)()
print 'bool(null_ptr) =', bool(null_ptr)
输出,
before, i.value = 42
after, i.value = 57
bool(null_ptr) = False
- 类型转换
ctypes 提供了类型转换方法 cast(),
例如,
class Bar(Structure):
_fields_ = [('count', c_int), ('value', POINTER(c_int))]
bar = Bar()
bar.count = 3
bar.value = (c_int * 3)(1, 2, 3)
for idx in range(bar.count):
print 'bar.value[%d] = %d' % (idx, bar.value[idx])
## use cast to convert
try:
bar.value = (c_byte * 4)()
except TypeError, e:
print '*** ERROR: %s' % str(e)
bar.value = cast((c_byte * 4)(), POINTER(c_int))
for idx in range(4):
print 'now, bar.value[%d] = %d' % (idx, bar.value[idx])
输出,
bar.value[0] = 1
bar.value[1] = 2
bar.value[2] = 3
*** ERROR: incompatible types, c_byte_Array_4 instance instead of LP_c_long instance
now, bar.value[0] = 0
now, bar.value[1] = 0
now, bar.value[2] = 0
now, bar.value[3] = 0
- 回调函数
ctypes 通过 CFUNCTYPE 支持回调函数,
例如,
import platform
from ctypes import *
if platform.system() == 'Windows':
libc = cdll.LoadLibrary('msvcrt.dll')
elif platform.system() == 'Linux':
libc = cdll.LoadLibrary('libc.so.6')
IntArray5 = c_int * 5
ia = IntArray5(5, 1, 7, 33, 99)
# CFUNCTYPE(restype, *argtypes, **kw)
CmpFuncType = CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
def py_cmp_func(a, b):
if a[0] > b[0]:
return 1
elif a[0] < b[0]:
return -1
else:
return 0
cmpfunc = CmpFuncType(py_cmp_func)
print 'before sort, the ia list is: ',
for item in ia:
print item,
# void qsort (void* base, size_t num, size_t size, int (*compar)(const void*,const void*));
libc.qsort(ia, len(ia), sizeof(c_int), cmpfunc)
print '\nafter sort, the ia list is: ',
for item in ia:
print item,
输出,
before sort, the ia list is: 5 1 7 33 99
after sort, the ia list is: 1 5 7 33 99
- Resize Space
ctypes 提供了 resize 变量占用空间的方法 resize(),注意,只能增大,不能小于原始空间,
例如,
short_array = (c_short * 4)(1, 2, 3, 4)
print 'sizeof(short_array) =', sizeof(short_array) # get 8, means short_array take 8-byte memory
print 'len(short_array) =', len(short_array)
print 'before resize, short_array is: ',
for idx in range(len(short_array)):
print short_array[idx],
try:
resize(short_array, 4) # resize short_array to 4-byte, raise error, due to cannot resize smaller than original
except ValueError, e:
print 'ERROR: %s' % str(e)
resize(short_array, 32)
print 'after succeed resize to 32-byte, now sizeof(short_array) =', sizeof(short_array)
print 'after succeed resize to 32-byte, now len(short_array) =', len(short_array)
print 'after reszie, short_array is: ',
for idx in range(len(short_array)):
print short_array[idx],
输出,
sizeof(short_array) = 8
len(short_array) = 4
before resize, short_array is: 1 2 3 4
ERROR: minimum size is 8
after succeed resize to 32-byte, now sizeof(short_array) = 32
after succeed resize to 32-byte, now len(short_array) = 4
after reszie, short_array is: 1 2 3 4
- Other
class cell(Structure):
pass
cell._fields_ = [('name', c_char_p), ('next', POINTER(cell))]
c1 = cell()
c2 = cell()
c1.name = 'foo'
c2.name = 'bar'
c1.next = pointer(c2)
c2.next = pointer(c1)
p = c1
for i in range(10):
print p.name,
p = p.next[0]
输出,
foo bar foo bar foo bar foo bar foo bar