如果你认为Python仅仅是一个脚本语言,那么在你看完本文后或许会有新的感慨:Woooo~,It's cool
Ctypes module是提供Python直接调用C库函数的接口模块,有了它你可以像写C一样写Python,或者像写Python一样来写C。它提供了跟C兼容的数据类型,使得你可以直接调用dlls或者共享库导出的函数。下面大概介绍一下使用方法。
- 首先当然是 from ctypes import *
- 获取链接库:
>>> print windll.kernel32 # doctest: +WINDOWS
<WinDLL 'kernel32', handle ... at ...>>>> cdll.LoadLibrary("libc.so.6") # doctest: +LINUX
<CDLL 'libc.so.6', handle ... at ...> - 获得库函数:
>>> libc.printf
<_FuncPtr object at 0x...>
>>> print windll.kernel32.GetModuleHandleA # doctest: +WINDOWS
<_FuncPtr object at 0x...>
注意:Windows下跟字符串相关的函数分ANSI和UNICODE版本:FuncNameA和FuncNameW。调用哪个需要你自己决定。
- 数据类型:
>>> c_int()
c_long(0)
>>> c_char_p("Hello, World")
c_char_p('Hello, World')
>>> c_ushort(-3)
c_ushort(65533)
注意:内容将被更改的字符串指针应该用create_string_buffer来获得,如下:
>>> p = create_string_buffer(3) # 3个字节长度,预置0x00
>>> print sizeof(p), repr(p.raw)
3 '\x00\x00\x00'
>>> p = create_string_buffer("Hello") #用Hello来初始化
>>> print sizeof(p), repr(p.raw) #内部表示
6 'Hello\x00'
>>> print repr(p.value) #值
'Hello'
>>> p = create_string_buffer("Hello", 10) # 同时指定初值和长度>>> print sizeof(p), repr(p.raw)
10 'Hello\x00\x00\x00\x00\x00'
>>> p.value = "Hi"
>>> print sizeof(p), repr(p.raw)
10 'Hi\x00lo\x00\x00\x00\x00\x00' # 获得新值,包含最后的0x00结尾
- 函数调用:
>>> printf = cdll.msvcrt.printf
>>> printf("Hello, %s\n", "World!")
Hello, World!
14
>>> printf("Hello, %S", u"World!")
Hello, World!
13
>>> printf("%d bottles of beer\n", 42)
42 bottles of beer
19>>>
下面用一个具体例子来介绍:
我的目标是(没有蛀牙!:)直接调用WIN32 API函数来移动桌面的某些窗口,此处涉及到传递callback 函数作为函数实参的例子。
- 首先定义callback函数类型:
ENUMCHILDPROC = WINFUNCTYPE(c_int, c_int, c_int) # 表示一个 int FuncProc(int, int) 的C类型函数
- 定义要用到的一些常用结构:
class RECT(Structure):
_fields_ = [('left', c_long),
('top', c_long),
('right', c_long),
('bottom', c_long)]
def __str__(self):
return str((self.left, self.top, self.right, self.bottom))
class POINT(Structure):
_fields_ = [('x', c_long),
('y', c_long)]
def __str__(self):
return str((self.x, self.y))
- 定义实际执行的Callback函数
def EnumChildProc(lhwnd, lParam):
"""return True, 继续枚举下一个窗口
return False, 不再继续"""
global wndlist
pt = POINT()
windll.user32.GetCursorPos(byref(pt))
winclassname = create_string_buffer(255)
windll.user32.GetClassNameA(lhWnd, winclassname,255)
wintitlename = create_string_buffer(255)
windll.user32.GetWindowTextA(lhWnd, wintitlename, 255)
rect = RECT()
windll.user32.GetWindowRect(lhWnd, byref(rect))
if wndlist.has_key(winclassname.value):
wndlist[winclassname.value].append((wintitlename.value, str(rect)))
else:
wndlist[winclassname.value] = [(wintitlename.value, str(rect))]
if len(winclassname.value)==0 or len(wintitlename.value)==0:
return True # 跳过那些"非实体"window
# 根据class name决定是否要移动
if winclassname.value=='TkTopLevel' and wintitlename.value.find('.py')!=-1:
windll.user32.MoveWindow(hwnd, pt.x, pt.y, rect.right-rect.left, rect.bottom-rect.top, True)
print '%s moved to under cursor. (%s)'%(wintitlename.value,winclassname.value)
return True
- 最后,开始启动找窗口:
wndlist = {}
enumchildprc = ENUMCHILDPROC(EnumChildProc) # 保持一个对callback的引用,防止被回收
tophwnd = windll.user32.GetDesktopWindow()
windll.user32.EnumChildWindows(tophwnd, enumchildprc, c_int(-1))
更多文档参见:
- The tutorial and the reference as one single HTML page each.
- Single HTML pages from the official Python 2.5 documentation.
源文档 <http://starship.python.net/crew/theller/ctypes/>
提示:ctypes在Windows, Mac OS X, Linux, Solaris, FreeBSD, OpenBSD下均可以使用,而且Python 2.5已经有ctypes默认安装。
没有评论:
发表评论