ctypes --- 用於 Python 的外部函式庫¶
原始碼:Lib/ctypes
ctypes is a foreign function library for Python. It provides C compatible
data types, and allows calling functions in DLLs or shared libraries. It can be
used to wrap these libraries in pure Python.
This is an optional module. If it is missing from your copy of CPython, look for documentation from your distributor (that is, whoever provided Python to you). If you are the distributor, see 可選模組的需求.
ctypes 教學¶
Note: Some code samples reference the ctypes c_int type. On platforms
where sizeof(long) == sizeof(int) it is an alias to c_long.
So, you should not be confused if c_long is printed if you would expect
c_int --- they are actually the same type.
Loading dynamic link libraries¶
ctypes exports the cdll, and on Windows
windll and oledll
objects, for loading dynamic link libraries.
You load libraries by accessing them as attributes of these objects.
cdll loads libraries which export functions using the
standard cdecl calling convention, while windll
libraries call functions using the stdcall
calling convention.
oledll also uses the stdcall calling convention, and
assumes the functions return a Windows HRESULT error code. The error
code is used to automatically raise an OSError exception when the
function call fails.
在 3.3 版的變更: Windows errors used to raise WindowsError, which is now an alias
of OSError.
Here are some examples for Windows. Note that msvcrt is the MS standard C
library containing most standard C functions, and uses the cdecl calling
convention:
>>> from ctypes import *
>>> print(windll.kernel32)
<WinDLL 'kernel32', handle ... at ...>
>>> print(cdll.msvcrt)
<CDLL 'msvcrt', handle ... at ...>
>>> libc = cdll.msvcrt
>>>
Windows appends the usual .dll file suffix automatically.
備註
Accessing the standard C library through cdll.msvcrt will use an
outdated version of the library that may be incompatible with the one
being used by Python. Where possible, use native Python functionality,
or else import and use the msvcrt module.
Other systems require the filename including the extension to
load a library, so attribute access can not be used to load libraries. Either the
LoadLibrary() method of the dll loaders should be used,
or you should load the library by creating an instance of CDLL
by calling the constructor.
舉例來說,在 Linux 上:
>>> cdll.LoadLibrary("libc.so.6")
<CDLL 'libc.so.6', handle ... at ...>
>>> libc = CDLL("libc.so.6")
>>> libc
<CDLL 'libc.so.6', handle ... at ...>
>>>
在 macOS 上:
>>> cdll.LoadLibrary("libc.dylib")
<CDLL 'libc.dylib', handle ... at ...>
>>> libc = CDLL("libc.dylib")
>>> libc
<CDLL 'libc.dylib', handle ... at ...>
Accessing functions from loaded dlls¶
Functions are accessed as attributes of dll objects:
>>> libc.printf
<_FuncPtr object at 0x...>
>>> print(windll.kernel32.GetModuleHandleA)
<_FuncPtr object at 0x...>
>>> print(windll.kernel32.MyOwnFunction)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "ctypes.py", line 239, in __getattr__
func = _StdcallFuncPtr(name, self)
AttributeError: function 'MyOwnFunction' not found
>>>
Note that win32 system dlls like kernel32 and user32 often export ANSI
as well as UNICODE versions of a function. The UNICODE version is exported with
a W appended to the name, while the ANSI version is exported with an A
appended to the name. The win32 GetModuleHandle function, which returns a
module handle for a given module name, has the following C prototype, and a
macro is used to expose one of them as GetModuleHandle depending on whether
UNICODE is defined or not:
/* ANSI 版本 */
HMODULE GetModuleHandleA(LPCSTR lpModuleName);
/* UNICODE 版本 */
HMODULE GetModuleHandleW(LPCWSTR lpModuleName);
windll does not try to select one of them by magic, you must access the
version you need by specifying GetModuleHandleA or GetModuleHandleW
explicitly, and then call it with bytes or string objects respectively.
Sometimes, dlls export functions with names which aren't valid Python
identifiers, like "??2@YAPAXI@Z". In this case you have to use
getattr() to retrieve the function:
>>> getattr(cdll.msvcrt, "??2@YAPAXI@Z")
<_FuncPtr object at 0x...>
>>>
On Windows, some dlls export functions not by name but by ordinal. These functions can be accessed by indexing the dll object with the ordinal number:
>>> cdll.kernel32[1]
<_FuncPtr object at 0x...>
>>> cdll.kernel32[0]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "ctypes.py", line 310, in __getitem__
func = _StdcallFuncPtr(name, self)
AttributeError: function ordinal 0 not found
>>>
呼叫函式¶
You can call these functions like any other Python callable. This example uses
the rand() function, which takes no arguments and returns a pseudo-random integer:
>>> print(libc.rand())
1804289383
On Windows, you can call the GetModuleHandleA() function, which returns a win32 module
handle (passing None as single argument to call it with a NULL pointer):
>>> print(hex(windll.kernel32.GetModuleHandleA(None)))
0x1d000000
>>>
ValueError is raised when you call an stdcall function with the
cdecl calling convention, or vice versa:
>>> cdll.kernel32.GetModuleHandleA(None)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: Procedure probably called with not enough arguments (4 bytes missing)
>>>
>>> windll.msvcrt.printf(b"spam")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: Procedure probably called with too many arguments (4 bytes in excess)
>>>
To find out the correct calling convention you have to look into the C header file or the documentation for the function you want to call.
On Windows, ctypes uses win32 structured exception handling to prevent
crashes from general protection faults when functions are called with invalid
argument values:
>>> windll.kernel32.GetModuleHandleA(32)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OSError: exception: access violation reading 0x00000020
>>>
There are, however, enough ways to crash Python with ctypes, so you
should be careful anyway. The faulthandler module can be helpful in
debugging crashes (e.g. from segmentation faults produced by erroneous C library
calls).
None, integers, bytes objects and (unicode) strings are the only native
Python objects that can directly be used as parameters in these function calls.
None is passed as a C NULL pointer, bytes objects and strings are passed
as pointer to the memory block that contains their data (char* or
wchar_t*). Python integers are passed as the platform's default C
int type, their value is masked to fit into the C type.
Before we move on calling functions with other parameter types, we have to learn
more about ctypes data types.
Fundamental data types¶
ctypes defines a number of primitive C compatible data types:
ctypes 型別 |
C 型別 |
Python 型別 |
|
|---|---|---|---|
_Bool |
|
||
char |
1-character |
|
|
|
1-character |
|
|
char |
|
||
unsigned char |
|
||
short |
|
||
unsigned short |
|
||
int |
|
||
|
* |
||
|
* |
||
|
* |
||
|
* |
||
unsigned int |
|
||
|
* |
||
|
* |
||
|
* |
||
|
* |
||
long |
|
||
unsigned long |
|
||
long long |
|
||
unsigned long long |
|
||
|
* |
||
* |
|||
|
* |
||
float |
|
||
double |
|
||
long double |
|
||
char* (NUL terminated) |
|
|
|
wchar_t* (NUL terminated) |
|
|
|
void* |
|
|
|
|
|||
short int |
|
Additionally, if IEC 60559 compatible complex arithmetic (Annex G) is supported
in both C and libffi, the following complex types are available:
ctypes 型別 |
C 型別 |
Python 型別 |
|
|---|---|---|---|
float complex |
|
||
double complex |
|
||
long double complex |
|
All these types can be created by calling them with an optional initializer of the correct type and value:
>>> c_int()
c_long(0)
>>> c_wchar_p("Hello, World")
c_wchar_p(140018365411392)
>>> c_ushort(-3)
c_ushort(65533)
>>>
The constructors for numeric types will convert input using
__bool__(),
__index__() (for int),
__float__() or __complex__().
This means c_bool accepts any object with a truth value:
>>> empty_list = []
>>> c_bool(empty_list)
c_bool(False)
Since these types are mutable, their value can also be changed afterwards:
>>> i = c_int(42)
>>> print(i)
c_long(42)
>>> print(i.value)
42
>>> i.value = -99
>>> print(i.value)
-99
>>>
Assigning a new value to instances of the pointer types c_char_p,
c_wchar_p, and c_void_p changes the memory location they
point to, not the contents of the memory block (of course not, because Python
string objects are immutable):
>>> s = "Hello, World"
>>> c_s = c_wchar_p(s)
>>> print(c_s)
c_wchar_p(139966785747344)
>>> print(c_s.value)
Hello World
>>> c_s.value = "Hi, there"
>>> print(c_s) # 記憶體位置已改變
c_wchar_p(139966783348904)
>>> print(c_s.value)
Hi, there
>>> print(s) # 第一個物件未改變
Hello, World
>>>
You should be careful, however, not to pass them to functions expecting pointers
to mutable memory. If you need mutable memory blocks, ctypes has a
create_string_buffer() function which creates these in various ways. The
current memory block contents can be accessed (or changed) with the raw
property; if you want to access it as NUL terminated string, use the value
property:
>>> from ctypes import *
>>> p = create_string_buffer(3) # create a 3 byte buffer, initialized to NUL bytes
>>> print(sizeof(p), repr(