2007年8月28日星期二

Ctypes: Unleash the power of Python

如果你认为Python仅仅是一个脚本语言,那么在你看完本文后或许会有新的感慨:Woooo~,It's cool

Ctypes module是提供Python直接调用C库函数的接口模块,有了它你可以像写C一样写Python,或者像写Python一样来写C。它提供了跟C兼容的数据类型,使得你可以直接调用dlls或者共享库导出的函数。下面大概介绍一下使用方法。

  1. 首先当然是 from ctypes import *
  2. 获取链接库:

    >>> print windll.kernel32 # doctest: +WINDOWS
    <WinDLL 'kernel32', handle ... at ...>

    >>> cdll.LoadLibrary("libc.so.6") # doctest: +LINUX
    <CDLL 'libc.so.6', handle ... at ...>

  3. 获得库函数:

    >>> libc.printf
    <_FuncPtr object at 0x...>
    >>> print windll.kernel32.GetModuleHandleA # doctest: +WINDOWS
    <_FuncPtr object at 0x...>

     
     

    注意:Windows下跟字符串相关的函数分ANSI和UNICODE版本:FuncNameA和FuncNameW。调用哪个需要你自己决定。

  4. 数据类型:

    >>> 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结尾

     
     

  5. 函数调用:

    >>> 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 函数作为函数实参的例子。

  6. 首先定义callback函数类型:

    ENUMCHILDPROC = WINFUNCTYPE(c_int, c_int, c_int) # 表示一个 int FuncProc(int, int) 的C类型函数

  7. 定义要用到的一些常用结构:

    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))

  8. 定义实际执行的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

  9. 最后,开始启动找窗口:

    wndlist = {}

    enumchildprc = ENUMCHILDPROC(EnumChildProc) # 保持一个对callback的引用,防止被回收

    tophwnd = windll.user32.GetDesktopWindow()

    windll.user32.EnumChildWindows(tophwnd, enumchildprc, c_int(-1))

     
     

    更多文档参见:

没有评论:

搜索此博客

你每天睡几小时?

Google