ctypes --- Thư viện hàm ngoại cho Python

Source code: Lib/ctypes


ctypes là thư viện hàm nước ngoài dành cho Python. Nó cung cấp các kiểu dữ liệu tương thích với C và cho phép gọi các hàm trong DLL hoặc thư viện dùng chung. Nó có thể được sử dụng để bọc các thư viện này bằng Python thuần túy.

Đây là một optional module. Nếu nó bị thiếu trong bản sao CPython của bạn, hãy tìm tài liệu từ nhà phân phối của bạn (nghĩa là bất kỳ ai đã cung cấp Python cho bạn). Nếu bạn là nhà phân phối, hãy xem Yêu cầu đối với các mô-đun tùy chọn.

hướng dẫn ctypes

Lưu ý: Một số mẫu mã tham chiếu loại ctypes c_int. Trên nền tảng có sizeof(long) == sizeof(int), nó là bí danh của c_long. Vì vậy, bạn không nên nhầm lẫn nếu c_long được in nếu bạn mong đợi c_int --- chúng thực sự cùng loại.

Truy cập các chức năng từ các dll được tải

Các hàm được truy cập dưới dạng thuộc tính của đối tượng dll:

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

Lưu ý rằng các dll hệ thống win32 như kernel32user32 thường xuất các phiên bản ANSI cũng như UNICODE của một hàm. Phiên bản UNICODE được xuất với W được thêm vào tên, trong khi phiên bản ANSI được xuất với A được thêm vào tên. Hàm win32 GetModuleHandle, trả về module handle cho một tên mô-đun nhất định, có nguyên mẫu C sau và macro được sử dụng để hiển thị một trong số chúng là GetModuleHandle tùy thuộc vào việc UNICODE có được xác định hay không:

/* phiên bản ANSI */
HMODULE GetModuleHandleA(LPCSTR lpModuleName);
/* phiên bản UNICODE */
HMODULE GetModuleHandleW(LPCWSTR lpModuleName);

windll không cố gắng chọn một trong số chúng bằng phép thuật, bạn phải truy cập phiên bản bạn cần bằng cách chỉ định rõ ràng GetModuleHandleA hoặc GetModuleHandleW, sau đó gọi nó bằng các đối tượng byte hoặc chuỗi tương ứng.

Đôi khi, dll xuất các hàm có tên không phải là mã định danh Python hợp lệ, như "??2@YAPAXI@Z". Trong trường hợp này, bạn phải sử dụng getattr() để truy xuất hàm

>>> getattr(cdll.msvcrt, "??2@YAPAXI@Z")
<_FuncPtr object at 0x...>
>>>

Trên Windows, một số dll xuất hàm không theo tên mà theo thứ tự. Các chức năng này có thể được truy cập bằng cách lập chỉ mục đối tượng dll với số thứ tự

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

Chức năng gọi

Bạn có thể gọi các hàm này giống như bất kỳ hàm Python nào khác có thể gọi được. Ví dụ này sử dụng hàm rand(), hàm này không nhận đối số và trả về một số nguyên giả ngẫu nhiên:

>>> print(libc.rand())
1804289383

Trên Windows, bạn có thể gọi hàm GetModuleHandleA(), hàm này trả về một mã điều khiển mô-đun win32 (chuyển None dưới dạng đối số duy nhất để gọi nó bằng con trỏ NULL):

>>> print(hex(windll.kernel32.GetModuleHandleA(None)))
0x1d000000
>>>

ValueError được nâng lên khi bạn gọi hàm stdcall với quy ước gọi cdecl hoặc ngược lại:

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

Để tìm ra quy ước gọi chính xác, bạn phải xem tệp tiêu đề C hoặc tài liệu về hàm bạn muốn gọi.

Trên Windows, ctypes sử dụng xử lý ngoại lệ có cấu trúc win32 để ngăn sự cố do lỗi bảo vệ chung khi các hàm được gọi với giá trị đối số không hợp lệ:

>>> windll.kernel32.GetModuleHandleA(32)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OSError: exception: access violation reading 0x00000020
>>>

Tuy nhiên, có đủ cách để phá hỏng Python bằng ctypes, vì vậy dù sao bạn cũng nên cẩn thận. Mô-đun faulthandler có thể hữu ích trong việc gỡ lỗi các sự cố (ví dụ: từ lỗi phân đoạn do lệnh gọi thư viện C sai).

None, số nguyên, đối tượng byte và chuỗi (unicode) là các đối tượng Python gốc duy nhất có thể được sử dụng trực tiếp làm tham số trong các lệnh gọi hàm này. None được truyền dưới dạng con trỏ C NULL, các đối tượng byte và chuỗi được truyền dưới dạng con trỏ tới khối bộ nhớ chứa dữ liệu của chúng (char* hoặc wchar_t*). Các số nguyên Python được chuyển dưới dạng loại C int mặc định của nền tảng, giá trị của chúng được che đi để phù hợp với loại C.

Trước khi chuyển sang gọi hàm với các loại tham số khác, chúng ta phải tìm hiểu thêm về các loại dữ liệu ctypes.

Các kiểu dữ liệu cơ bản

ctypes định nghĩa một số kiểu dữ liệu tương thích với C nguyên thủy:

loại ctype

loại C

loại Python

_type_

c_bool

_Bool

bool

'?'

c_char

char

1-character bytes

'c'

c_wchar

wchar_t

1 ký tự str

'u'

c_byte

char

int

'b'

c_ubyte

unsigned char

int

'B'

c_short

short

int

'h'

c_ushort

unsigned short

int

'H'

c_int

int

int

'i' *

c_int8

int8_t

int

*

c_int16

int16_t

int

*

c_int32

int32_t

int

*

c_int64

int64_t

int

*

c_uint

unsigned int

int

'I' *

c_uint8

uint8_t

int

*

c_uint16

uint16_t

int

*

c_uint32

uint32_t

int

*

c_uint64

uint64_t

int

*

c_long

long

int

'l'

c_ulong

unsigned long

int

'L'

c_longlong

long long

int

'q' *

c_ulonglong

unsigned long long

int

'Q' *

c_size_t

size_t

int

*

c_ssize_t

Py_ssize_t

int

*

c_time_t

time_t

int

*

c_float

float

float

'f'

c_double

double

float

'd'

c_longdouble

long double

float

'g' *

c_char_p

char* (NUL đã chấm dứt)

bytes hoặc None

'z'

c_wchar_p

wchar_t* (NUL đã chấm dứt)

str hoặc None

'Z'

c_void_p

void*

int hoặc None

'P'

py_object

PyObject*

object

'O'

VARIANT_BOOL

short int

bool

'v'

Ngoài ra, nếu số học phức tương thích IEC 60559 (Phụ lục G) được hỗ trợ trong cả C và libffi thì các loại phức sau đây sẽ khả dụng:

loại ctype

loại C

loại Python

_type_

c_float_complex

float complex

complex

'F'

c_double_complex

double complex

complex

'D'

c_longdouble_complex

long double complex

complex

'G'

Tất cả các loại này có thể được tạo bằng cách gọi chúng bằng trình khởi tạo tùy chọn có đúng loại và giá trị

>>> c_int()
c_long(0)
>>> c_wchar_p("Xin chào thế giới")
c_wchar_p(140018365411392)
>>> c_ushort(-3)
c_ushort(65533)
>>>

Các hàm tạo cho kiểu số sẽ chuyển đổi đầu vào bằng __bool__(), __index__() (đối với int), __float__() hoặc __complex__(). Điều này có nghĩa là c_bool chấp nhận bất kỳ đối tượng nào có giá trị thật

>>> Danh sách trống = []
>>> c_bool(danh sách trống)
c_bool(Sai)

Vì các loại này có thể thay đổi nên giá trị của chúng cũng có thể được thay đổi sau đó

>>> i = c_int(42)
>>> in(i)
c_long(42)
>>> in(i.value)
42
>>> i.giá trị = -99
>>> in(i.value)
-99
>>>

Việc gán một giá trị mới cho các thể hiện của các loại con trỏ c_char_p, c_wchar_pc_void_p sẽ thay đổi memory location mà chúng trỏ tới, not the contents của khối bộ nhớ (tất nhiên là không, vì các đối tượng chuỗi Python là bất biến):

>>> s = "Xin chào thế giới"
>>> c_s = c_wchar_p(s)
>>> in(c_s)
c_wchar_p(139966785747344)
>>> in(c_s.value)
Xin chào thế giới
>>> c_s.value = "Xin chào"
>>> print(c_s) # the vị trí bộ nhớ đã thay đổi
c_wchar_p(139966783348904)
>>> in(c_s.value)
Xin chào, ở đó
>>> đối tượng print(s) # first không thay đổi
Xin chào thế giới
>>>

Tuy nhiên, bạn nên cẩn thận không chuyển chúng cho các hàm mong đợi con trỏ tới bộ nhớ có thể thay đổi. Nếu bạn cần các khối bộ nhớ có thể thay đổi, ctypes có chức năng create_string_buffer() để tạo ra các khối này theo nhiều cách khác nhau. Nội dung khối bộ nhớ hiện tại có thể được truy cập (hoặc thay đổi) bằng thuộc tính raw; nếu bạn muốn truy cập nó dưới dạng chuỗi kết thúc NUL, hãy sử dụng thuộc tính value

>>> từ nhập ctypes *
>>> p = create_string_buffer(3) # create bộ đệm 3 byte, được khởi tạo thành byte NUL
>>> print(sizeof(p), repr(p.raw))
3 b'\x00\x00\x00'
>>> p = create_string_buffer(b"Hello") # create một bộ đệm chứa chuỗi kết thúc NUL
>>> print(sizeof(p), repr(p.raw))
6 b'Xin chào\x00'
>>> print(repr(p.value))
b'Xin chào'
>>> p = create_string_buffer(b"Xin chào", 10) # create bộ đệm 10 byte
>>> print(sizeof(p), repr(p.raw))
10 b'Xin chào\x00\x00\x00\x00\x00'
>>> p.value = b"Xin chào"
>>> print(sizeof(p), repr(p.raw))
10 b'Hi\x00lo\x00\x00\x00\x00\x00'
>>>

Hàm create_string_buffer() thay thế hàm c_buffer() cũ (vẫn có sẵn dưới dạng bí danh). Để tạo một khối bộ nhớ có thể thay đổi chứa các ký tự unicode loại C wchar_t, hãy sử dụng hàm create_unicode_buffer().

Chức năng gọi, tiếp tục

Lưu ý rằng printf in ra kênh đầu ra tiêu chuẩn thực, not đến sys.stdout, vì vậy những ví dụ này sẽ chỉ hoạt động tại dấu nhắc bảng điều khiển chứ không phải từ bên trong IDLE hoặc PythonWin:

>>> printf = libc.printf
>>> printf(b"Xin chào, %s\n", b"Thế giới!")
Xin chào Thế giới!
14
>>> printf(b"Xin chào, %S\n", "Thế giới!")
Xin chào Thế giới!
14
>>> printf(b"%d chai bia\n", 42)
42 chai bia
19
>>> printf(b"%f chai bia\n", 42.5)
Traceback (cuộc gọi gần đây nhất):
  Tệp "<stdin>", dòng 1, trong <module>
ctypes.ArgumentError: đối số 2: TypeError: Không biết cách chuyển đổi tham số 2
>>>

Như đã đề cập trước đó, tất cả các loại Python ngoại trừ các đối tượng số nguyên, chuỗi và byte phải được gói trong loại ctypes tương ứng của chúng để chúng có thể được chuyển đổi sang loại dữ liệu C được yêu cầu:

>>> printf(b"Một int %d, một double %f\n", 1234, c_double(3.14))
Một int 1234, gấp đôi 3.140000
31
>>>

Gọi các hàm biến thiên

Trên nhiều nền tảng, việc gọi các hàm biến đổi thông qua ctypes hoàn toàn giống với việc gọi các hàm với một số tham số cố định. Trên một số nền tảng và đặc biệt là ARM64 dành cho Nền tảng Apple, quy ước gọi các hàm biến đổi khác với quy ước gọi các hàm thông thường.

Trên các nền tảng đó, bắt buộc phải chỉ định thuộc tính argtypes cho các đối số hàm thông thường, không biến đổi:

libc.printf.argtypes = [ctypes.c_char_p]

Bởi vì việc chỉ định thuộc tính không hạn chế khả năng di chuyển nên bạn nên luôn chỉ định argtypes cho tất cả các hàm biến thiên.

Gọi hàm với các kiểu dữ liệu tùy chỉnh của riêng bạn

Bạn cũng có thể tùy chỉnh chuyển đổi đối số ctypes để cho phép sử dụng các phiên bản của lớp của riêng bạn làm đối số hàm. ctypes tìm kiếm thuộc tính _as_parameter_ và sử dụng thuộc tính này làm đối số hàm. Thuộc tính phải là số nguyên, chuỗi, byte, phiên bản ctypes hoặc đối tượng có thuộc tính _as_parameter_

>>> Chai đẳng cấp:
... def __init__(tự, số):
... self._as_parameter_ = số
...
>>> chai = Chai(42)
>>> printf(b"%d chai bia\n", chai)
42 chai bia
19
>>>

Nếu bạn không muốn lưu trữ dữ liệu của phiên bản trong biến phiên bản _as_parameter_, bạn có thể xác định property để cung cấp thuộc tính theo yêu cầu.

Chỉ định các loại đối số cần thiết (nguyên mẫu hàm)

Có thể chỉ định các loại đối số cần thiết của hàm được xuất từ ​​DLL bằng cách đặt thuộc tính argtypes.

argtypes phải là một chuỗi gồm các kiểu dữ liệu C (hàm printf() có lẽ không phải là một ví dụ hay ở đây, vì nó nhận một số lượng thay đổi và các loại tham số khác nhau tùy thuộc vào chuỗi định dạng, mặt khác, điều này khá tiện lợi để thử nghiệm tính năng này):

>>> printf.argtypes = [c_char_p, c_char_p, c_int, c_double]
>>> printf(b"Chuỗi '%s', Int %d, Double %f\n", b"Hi", 10, 2.2)
Chuỗi 'Xin chào', Int 10, Double 2.200000
37
>>>

Việc chỉ định một định dạng sẽ bảo vệ khỏi các loại đối số không tương thích (giống như nguyên mẫu cho hàm C) và cố gắng chuyển đổi các đối số thành loại hợp lệ

>>> printf(b"%d %d %d", 1, 2, 3)
Traceback (cuộc gọi gần đây nhất):
  Tệp "<stdin>", dòng 1, trong <module>
ctypes.ArgumentError: đối số 2: TypeError: đối tượng 'int' không thể được hiểu là ctypes.c_char_p
>>> printf(b"%s %d %f\n", b"X", 2, 3)
X 2 3.000000
13
>>>

Nếu bạn đã xác định các lớp của riêng mình mà bạn chuyển đến các lệnh gọi hàm, thì bạn phải triển khai phương thức lớp from_param() để chúng có thể sử dụng chúng theo trình tự argtypes. Phương thức lớp from_param() nhận đối tượng Python được truyền vào lệnh gọi hàm, nó sẽ thực hiện kiểm tra đánh máy hoặc bất cứ điều gì cần thiết để đảm bảo đối tượng này có thể chấp nhận được, sau đó trả về chính đối tượng đó, thuộc tính _as_parameter_ của nó hoặc bất cứ điều gì bạn muốn chuyển làm đối số hàm C trong trường hợp này. Một lần nữa, kết quả phải là số nguyên, chuỗi, byte, phiên bản ctypes hoặc đối tượng có thuộc tính _as_parameter_.

Các kiểu trả về

Theo mặc định, các hàm được giả định trả về loại C int. Các kiểu trả về khác có thể được chỉ định bằng cách đặt thuộc tính restype của đối tượng hàm.

Nguyên mẫu C của time()time_t time(time_t *). Vì time_t có thể thuộc loại khác với kiểu trả về mặc định int, bạn nên chỉ định thuộc tính restype:

>>> libc.time.resttype = c_time_t

The argument types can be specified using argtypes:

>>> libc.time.argtypes = (POINTER(c_time_t),)

Để gọi hàm với con trỏ NULL làm đối số đầu tiên, hãy sử dụng None:

>>> print(libc.time(None))
1150640792

Đây là một ví dụ nâng cao hơn, nó sử dụng hàm strchr(), hàm này yêu cầu một con trỏ chuỗi và một ký tự, đồng thời trả về một con trỏ tới một chuỗi:

>>> strchr = libc.strchr
>>> strchr(b"abcdef", ord("d"))
8059983
>>> strchr.restype = c_char_p    # c_char_p is a pointer to a string
>>> strchr(b"abcdef", ord("d"))
b'def'
>>> print(strchr(b"abcdef", ord("x")))
None
>>>

Nếu bạn muốn tránh các lệnh gọi ord("x") ở trên, bạn có thể đặt thuộc tính argtypes và đối số thứ hai sẽ được chuyển đổi từ một đối tượng byte Python ký tự đơn thành char C:

>>> strchr.restype = c_char_p
>>> strchr.argtypes = [c_char_p, c_char]
>>> strchr(b"abcdef", b"d")
b'def'
>>> strchr(b"abcdef", b"def")
Traceback (cuộc gọi gần đây nhất):
ctypes.ArgumentError: đối số 2: TypeError: dự kiến một byte ký tự, bytearray hoặc số nguyên
>>> print(strchr(b"abcdef", b"x"))
không có
>>> strchr(b"abcdef", b"d")
b'def'
>>>

Bạn cũng có thể sử dụng một đối tượng Python có thể gọi được (ví dụ: một hàm hoặc một lớp) làm thuộc tính restype, nếu hàm ngoại trả về một số nguyên. Hàm có thể gọi được sẽ được gọi với integer mà hàm C trả về và kết quả của lệnh gọi này sẽ được sử dụng làm kết quả của lệnh gọi hàm của bạn. Điều này rất hữu ích để kiểm tra các giá trị trả về lỗi và tự động đưa ra một ngoại lệ:

>>> GetModuleHandle = windll.kernel32.GetModuleHandleA
>>> def ValidHandle(value):
...     if value == 0:
...         raise WinError()
...     return value
...
>>>
>>> GetModuleHandle.restype = ValidHandle
>>> GetModuleHandle(None)
486539264
>>> GetModuleHandle("something silly")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in ValidHandle
OSError: [Errno 126] The specified module could not be found.
>>>

WinError là một hàm sẽ gọi api Windows FormatMessage() để lấy chuỗi biểu thị mã lỗi và returns là một ngoại lệ. WinError lấy tham số mã lỗi tùy chọn, nếu không ai sử dụng thì nó gọi GetLastError() để lấy lại.

Xin lưu ý rằng có sẵn cơ chế kiểm tra lỗi mạnh mẽ hơn nhiều thông qua thuộc tính errcheck; xem tài liệu tham khảo để biết chi tiết.

Truyền con trỏ (hoặc: truyền tham số bằng tham chiếu)

Đôi khi, hàm C api yêu cầu pointer chuyển thành kiểu dữ liệu làm tham số, có thể ghi vào vị trí tương ứng hoặc nếu dữ liệu quá lớn không thể truyền theo giá trị. Điều này còn được gọi là passing parameters by reference.

ctypes xuất hàm byref() được sử dụng để truyền tham số theo tham chiếu. Hiệu ứng tương tự có thể đạt được với hàm pointer(), mặc dù pointer() thực hiện nhiều công việc hơn vì nó xây dựng một đối tượng con trỏ thực, do đó, sử dụng byref() sẽ nhanh hơn nếu bạn không cần đối tượng con trỏ trong chính Python:

>>> i = c_int()
>>> f = c_float()
>>> s = create_string_buffer(b'\000' * 32)
>>> print(i.value, f.value, repr(s.value))
0 0,0 b''
>>> libc.sscanf(b"1 3.14 Xin chào", b"%d %f %s",
... byref(i), byref(f), s)
3
>>> print(i.value, f.value, repr(s.value))
1 3.1400001049 b'Xin chào'
>>>

Cơ cấu và công đoàn

Các cấu trúc và liên kết phải xuất phát từ các lớp cơ sở StructureUnion được xác định trong mô-đun ctypes. Mỗi lớp con phải xác định thuộc tính _fields_. _fields_ phải là danh sách 2-tuples, chứa field namefield type.

Loại trường phải là loại ctypes như c_int hoặc bất kỳ loại ctypes dẫn xuất nào khác: cấu trúc, liên kết, mảng, con trỏ.

Dưới đây là một ví dụ đơn giản về cấu trúc POINT, chứa hai số nguyên có tên xy, đồng thời cho biết cách khởi tạo cấu trúc trong hàm tạo:

>>> từ nhập ctypes *
>>> lớp POINT(Cấu trúc):
... _fields_ = [("x", c_int),
... ("y", c_int)]
...
>>> điểm = POINT(10, 20)
>>> in(point.x, point.y)
10 20
>>> điểm = POINT(y=5)
>>> in(point.x, point.y)
0 5
>>> POINT(1, 2, 3)
Traceback (cuộc gọi gần đây nhất):
  Tệp "<stdin>", dòng 1, trong <module>
TypeError: quá nhiều công cụ khởi tạo
>>>

Tuy nhiên, bạn có thể xây dựng các cấu trúc phức tạp hơn nhiều. Bản thân một cấu trúc có thể chứa các cấu trúc khác bằng cách sử dụng cấu trúc làm kiểu trường.

Đây là cấu trúc RECT chứa hai POINT có tên upperleftlowerright:

>>> lớp RECT(Cấu trúc):
... _fields_ = [("upperleft", POINT),
... ("phía dưới bên phải", POINT)]
...
>>> rc = RECT(điểm)
>>> print(rc.upperleft.x, rc.upperleft.y)
0 5
>>> print(rc.lowright.x, rc.lowright.y)
0 0
>>>

Các cấu trúc lồng nhau cũng có thể được khởi tạo trong hàm tạo theo nhiều cách:

>>> r = RECT(POINT(1, 2), POINT(3, 4))
>>> r = RECT((1, 2), (3, 4))

Trường descriptors có thể được truy xuất từ class, chúng rất hữu ích cho việc gỡ lỗi vì chúng có thể cung cấp thông tin hữu ích. Xem CField:

>>> POINT.x
<ctypes.CField 'x' type=c_int, ofs=0, size=4>
>>> POINT.y
<ctypes.CField 'y' type=c_int, ofs=4, size=4>
>>>

Cảnh báo

ctypes không hỗ trợ chuyển các liên kết hoặc cấu trúc có trường bit sang các hàm theo giá trị. Mặc dù điều này có thể hoạt động trên x86 32 bit, nhưng thư viện không đảm bảo nó sẽ hoạt động trong trường hợp chung. Các kết hợp và cấu trúc có trường bit phải luôn được chuyển đến các hàm bằng con trỏ.

Bố cục cấu trúc/liên kết, căn chỉnh và thứ tự byte

Theo mặc định, các trường Cấu trúc và Liên kết được trình bày giống như cách trình biên dịch C thực hiện. Có thể ghi đè hoàn toàn hành vi này bằng cách chỉ định thuộc tính lớp _layout_ trong định nghĩa lớp con; xem tài liệu thuộc tính để biết chi tiết.

Có thể chỉ định căn chỉnh tối đa cho các trường và/hoặc cho chính cấu trúc bằng cách đặt các thuộc tính lớp _pack_ và/hoặc _align_ tương ứng. Xem tài liệu thuộc tính để biết chi tiết.

ctypes sử dụng thứ tự byte gốc cho Cấu trúc và Liên minh. Để xây dựng cấu trúc với thứ tự byte không phải gốc, bạn có thể sử dụng một trong các lớp cơ sở BigEndianStructure, LittleEndianStructure, BigEndianUnionLittleEndianUnion. Các lớp này không thể chứa các trường con trỏ.

Các trường bit trong cấu trúc và công đoàn

Có thể tạo các cấu trúc và liên kết chứa các trường bit. Các trường bit chỉ có thể áp dụng cho các trường số nguyên, độ rộng bit được chỉ định là mục thứ ba trong bộ dữ liệu _fields_

>>> lớp Int(Cấu trúc):
... _fields_ = [("first_16", c_int, 16),
... ("thứ hai_16", c_int, 16)]
...
>>> in(Int.first_16)
<ctypes.CField 'first_16' type=c_int, ofs=0, bit_size=16, bit_offset=0>
>>> in(Int.second_16)
<ctypes.CField 'second_16' type=c_int, ofs=0, bit_size=16, bit_offset=16>

Điều quan trọng cần lưu ý là việc phân bổ và bố cục trường bit trong bộ nhớ không được xác định là tiêu chuẩn C; việc triển khai của chúng là dành riêng cho trình biên dịch. Theo mặc định, Python sẽ cố gắng khớp hành vi của trình biên dịch "gốc" cho nền tảng hiện tại. Xem thuộc tính _layout_ để biết chi tiết về hành vi mặc định và cách thay đổi nó.

Mảng

Mảng là các chuỗi, chứa một số lượng cố định các thể hiện cùng loại.

Cách được khuyến nghị để tạo kiểu mảng là nhân kiểu dữ liệu với số nguyên dương:

TenPointsArrayType = POINT * 10

Dưới đây là ví dụ về kiểu dữ liệu hơi giả tạo, cấu trúc chứa 4 ĐIỂM trong số các nội dung khác

>>> từ nhập ctypes *
>>> lớp POINT(Cấu trúc):
... _fields_ = ("x", c_int), ("y", c_int)
...
>>> lớp MyStruct(Cấu trúc):
... _fields_ = [("a", c_int),
... ("b", c_float),
... ("point_array", POINT * 4)]
>>>
>>> print(len(MyStruct(). point_array))
4
>>>

Các cá thể được tạo theo cách thông thường, bằng cách gọi lớp:

mảng = TenPointsArrayType()
cho pt trong mảng:
    in(pt.x, pt.y)

Đoạn mã trên in một loạt dòng 0 0, vì nội dung mảng được khởi tạo bằng 0.

Công cụ khởi tạo đúng loại cũng có thể được chỉ định

>>> từ nhập ctypes *
>>> Mười số nguyên = c_int * 10
>>> ii = Mười số nguyên(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
>>> in(ii)
<c_long_Array_10 đối tượng ở 0x...>
>>> for i in ii: print(i, end=" ")
...
1 2 3 4 5 6 7 8 9 10
>>>

Con trỏ

Các phiên bản con trỏ được tạo bằng cách gọi hàm pointer() trên loại ctypes

>>> từ nhập ctypes *
>>> i = c_int(42)
>>> pi = con trỏ(i)
>>>

Các cá thể con trỏ có thuộc tính contents trả về đối tượng mà con trỏ trỏ tới, đối tượng i ở trên:

>>> pi.contents
c_long(42)
>>>

Lưu ý rằng ctypes không có OOR (trả về đối tượng ban đầu), nó xây dựng một đối tượng mới, tương đương mỗi khi bạn truy xuất một thuộc tính

>>> pi.contents  tôi
sai
>>> pi.contents  pi.contents
sai
>>>

Việc gán một phiên bản c_int khác cho thuộc tính nội dung của con trỏ sẽ khiến con trỏ trỏ đến vị trí bộ nhớ nơi nó được lưu trữ

>>> i = c_int(99)
>>> pi.contents = i
>>> pi.contents
c_long(99)
>>>

Các cá thể con trỏ cũng có thể được lập chỉ mục bằng số nguyên:

>>> pi[0]
99
>>>

Việc gán cho một chỉ mục số nguyên sẽ thay đổi giá trị được trỏ tới

>>> in(i)
c_long(99)
>>> pi[0] = 22
>>> in(i)
c_long(22)
>>>

Cũng có thể sử dụng các chỉ mục khác 0, nhưng bạn phải biết mình đang làm gì, giống như trong C: Bạn có thể truy cập hoặc thay đổi các vị trí bộ nhớ tùy ý. Nói chung, bạn chỉ sử dụng tính năng này nếu bạn nhận được một con trỏ từ hàm C và bạn biết rằng con trỏ đó thực sự trỏ đến một mảng thay vì một mục duy nhất.

Đằng sau, hàm pointer() không chỉ đơn giản là tạo các phiên bản con trỏ mà trước tiên nó phải tạo con trỏ types. Điều này được thực hiện bằng hàm POINTER(), chấp nhận bất kỳ loại ctypes nào và trả về một loại mới:

>>> PI = POINTER(c_int)
>>> PI
<lớp 'ctypes.LP_c_long'>
>>> PI(42)
Traceback (cuộc gọi gần đây nhất):
  Tệp "<stdin>", dòng 1, trong <module>
TypeError: dự kiến ​​c_long thay vì int
>>> PI(c_int(42))
<ctypes.LP_c_long đối tượng ở 0x...>
>>>

Gọi loại con trỏ mà không có đối số sẽ tạo ra con trỏ NULL. Con trỏ NULL có giá trị boolean False

>>> null_ptr = POINTER(c_int)()
>>> in(bool(null_ptr))
sai
>>>

ctypes kiểm tra NULL khi các con trỏ hội thảo không hợp lệ (nhưng các con trỏ không phảiNULL không hợp lệ sẽ làm hỏng Python):

>>> null_ptr[0]
Traceback (cuộc gọi gần đây nhất):
    ....
ValueError: truy cập con trỏ NULL
>>>

>>> null_ptr[0] = 1234
Traceback (cuộc gọi gần đây nhất):
    ....
ValueError: truy cập con trỏ NULL
>>>

An toàn chỉ khi không có GIL

Từ Python 3.13 trở đi, GIL có thể bị tắt trên free-threaded build. Trong ctypes, việc đọc và ghi đồng thời vào một đối tượng là an toàn, nhưng không an toàn trên nhiều đối tượng:

>>> số = c_int(42)
>>> con trỏ_a = con trỏ(số)
>>> con trỏ_b = con trỏ(số)

Ở trên, chỉ an toàn cho một đối tượng đọc và ghi vào địa chỉ cùng một lúc nếu GIL bị tắt. Vì vậy, pointer_a có thể được chia sẻ và ghi vào nhiều luồng, nhưng chỉ khi pointer_b không cố gắng làm điều tương tự. Nếu đây là sự cố, hãy cân nhắc sử dụng threading.Lock để đồng bộ hóa quyền truy cập vào bộ nhớ:

>>> nhập luồng
>>> lock = threading.Lock()
>>> # Thread 1
>>>  khóa:
... con trỏ_a.contents = 24
>>> # Thread 2
>>>  khóa:
... con trỏ_b.contents = 42

Chuyển đổi loại

Thông thường, ctypes thực hiện kiểm tra loại nghiêm ngặt. Điều này có nghĩa là, nếu bạn có POINTER(c_int) trong danh sách argtypes của một hàm hoặc là loại của trường thành viên trong định nghĩa cấu trúc thì chỉ các phiên bản có cùng loại mới được chấp nhận. Có một số ngoại lệ đối với quy tắc này, trong đó ctypes chấp nhận các đối tượng khác. Ví dụ: bạn có thể chuyển các thể hiện mảng tương thích thay vì các kiểu con trỏ. Vì vậy, đối với POINTER(c_int), ctypes chấp nhận một mảng c_int:

>>> Thanh lớp (Cấu trúc):
... _fields_ = [("đếm", c_int), ("giá trị", POINTER(c_int))]
...
>>> thanh = Thanh()
>>> bar.values = (c_int * 3)(1, 2, 3)
>>> bar.count = 3
>>> cho i trong phạm vi (bar.count):
... print(bar.values[i])
...
1
2
3
>>>

Ngoài ra, nếu một đối số hàm được khai báo rõ ràng là một loại con trỏ (chẳng hạn như POINTER(c_int)) trong argtypes, thì một đối tượng thuộc loại nhọn (c_int trong trường hợp này) có thể được truyền cho hàm. ctypes sẽ tự động áp dụng chuyển đổi byref() cần thiết trong trường hợp này.

Để đặt trường loại POINTER thành NULL, bạn có thể gán None:

>>> bar.values = Không
>>>

Đôi khi bạn có những trường hợp thuộc loại không tương thích. Trong C, bạn có thể chuyển kiểu này sang kiểu khác. ctypes cung cấp chức năng cast() có thể được sử dụng theo cách tương tự. Cấu trúc Bar được xác định ở trên chấp nhận các con trỏ POINTER(c_int) hoặc mảng c_int cho trường values của nó, nhưng không chấp nhận các phiên bản của các loại khác:

>>> bar.values = (c_byte * 4)()
Traceback (cuộc gọi gần đây nhất):
  Tệp "<stdin>", dòng 1, trong <module>
TypeError: các loại không tương thích, phiên bản c_byte_Array_4 thay vì phiên bản LP_c_long
>>>

Đối với những trường hợp này, chức năng cast() rất hữu ích.

Hàm cast() có thể được sử dụng để chuyển một thể hiện ctypes thành một con trỏ tới một kiểu dữ liệu ctypes khác. cast() có hai tham số, một đối tượng ctypes đang hoặc có thể được chuyển đổi thành một loại con trỏ nào đó và một loại con trỏ ctypes. Nó trả về một thể hiện của đối số thứ hai, tham chiếu cùng khối bộ nhớ với đối số đầu tiên

>>> a = (c_byte * 4)()
>>> diễn viên(a, POINTER(c_int))
<ctypes.LP_c_long đối tượng tại ...>
>>>

Vì vậy, cast() có thể được sử dụng để gán cho trường values của Bar cấu trúc:

>>> thanh = Thanh()
>>> bar.values = cast((c_byte * 4)(), POINTER(c_int))
>>> print(bar.values[0])
0
>>>

Các loại chưa hoàn chỉnh

Incomplete Types là các cấu trúc, liên kết hoặc mảng mà các thành viên chưa được chỉ định. Trong C, chúng được chỉ định bằng các khai báo chuyển tiếp, được xác định sau

tế bào cấu trúc; /* khai báo chuyển tiếp */

ô cấu trúc {
    char *tên;
    ô cấu trúc *tiếp theo;
};

Bản dịch đơn giản sang mã ctypes sẽ là thế này, nhưng nó không hoạt động

>>> ô lớp (Cấu trúc):
... _fields_ = [("tên", c_char_p),
... ("tiếp theo", POINTER(ô))]
...
Traceback (cuộc gọi gần đây nhất):
  Tệp "<stdin>", dòng 1, trong <module>
  Tệp "<stdin>", dòng 2, trong ô
NameError: tên 'ô' không được xác định
>>>

bởi vì class cell mới không có sẵn trong chính câu lệnh lớp. Trong ctypes, chúng ta có thể định nghĩa lớp cell và đặt thuộc tính _fields_ sau, sau câu lệnh lớp

>>> từ nhập ctypes *
>>> ô lớp (Cấu trúc):
... vượt qua
...
>>> cell._fields_ = [("name", c_char_p),
... ("tiếp theo", POINTER(ô))]
>>>

Hãy thử nó. Chúng tôi tạo hai phiên bản của cell và để chúng trỏ đến nhau và cuối cùng theo chuỗi con trỏ một vài lần

>>> c1 = ô()
>>> c1.name = b"foo"
>>> c2 = ô()
>>> c2.name = b"bar"
>>> c1.next = con trỏ(c2)
>>> c2.next = con trỏ(c1)
>>> p = c1
>>> cho i trong phạm vi (8):
... print(p.name, end=" ")
... p = p.next[0]
...
thanh foo thanh foo thanh foo thanh foo thanh
>>>

Chức năng gọi lại

ctypes cho phép tạo các con trỏ hàm có thể gọi được C từ các lệnh gọi được của Python. Đôi khi chúng được gọi là callback functions.

Đầu tiên, bạn phải tạo một lớp cho hàm gọi lại. Lớp biết quy ước gọi, kiểu trả về cũng như số lượng và kiểu đối số mà hàm này sẽ nhận được.

Hàm xuất xưởng CFUNCTYPE() tạo các loại cho hàm gọi lại bằng cách sử dụng quy ước gọi cdecl. Trên Windows, hàm xuất xưởng WINFUNCTYPE() tạo các kiểu cho hàm gọi lại bằng cách sử dụng quy ước gọi stdcall.

Cả hai hàm xuất xưởng này đều được gọi với loại kết quả là đối số đầu tiên và các hàm gọi lại dự kiến loại đối số là đối số còn lại.

Ở đây tôi sẽ trình bày một ví dụ sử dụng hàm qsort() của thư viện C tiêu chuẩn, được dùng để sắp xếp các mục với sự trợ giúp của hàm gọi lại. qsort() sẽ được sử dụng để sắp xếp một mảng các số nguyên:

>>> IntArray5 = c_int * 5
>>> ia = IntArray5(5, 1, 7, 33, 99)
>>> qsort = libc.qsort
>>> qsort.restype = Không 
>>>

qsort() phải được gọi bằng một con trỏ tới dữ liệu cần sắp xếp, số lượng mục trong mảng dữ liệu, kích thước của một mục và một con trỏ tới hàm so sánh, hàm gọi lại. Lệnh gọi lại sau đó sẽ được gọi với hai con trỏ tới các mục và nó phải trả về một số nguyên âm nếu mục đầu tiên nhỏ hơn mục thứ hai, số 0 nếu chúng bằng nhau và một số nguyên dương nếu ngược lại.

Vì vậy, hàm gọi lại của chúng ta nhận con trỏ tới số nguyên và phải trả về một số nguyên. Đầu tiên chúng ta tạo type cho hàm gọi lại:

>>> CMPFUNC = CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
>>>

Để bắt đầu, đây là một lệnh gọi lại đơn giản hiển thị các giá trị mà nó được chuyển:

>>> def py_cmp_func(a, b):
... print("py_cmp_func", a[0], b[0])
... trả về 0
...
>>> cmp_func = CMPFUNC(py_cmp_func)
>>>

Kết quả:

>>> qsort(ia, len(ia), sizeof(c_int), cmp_func)
py_cmp_func 5 1
py_cmp_func 33 99
py_cmp_func 7 33
py_cmp_func 5 7
py_cmp_func 1 7
>>>

Bây giờ chúng ta thực sự có thể so sánh hai mục và trả về một kết quả hữu ích

>>> def py_cmp_func(a, b):
...     print("py_cmp_func", a[0], b[0])
...     return a[0] - b[0]
...
>>>
>>> qsort(ia, len(ia), sizeof(c_int), CMPFUNC(py_cmp_func))
py_cmp_func 5 1
py_cmp_func 33 99
py_cmp_func 7 33
py_cmp_func 1 7
py_cmp_func 5 7
>>>

Như chúng ta có thể dễ dàng kiểm tra, mảng của chúng ta hiện đã được sắp xếp:

>>> cho i in ia: print(i, end=" ")
...
1 5 7 33 99
>>>

Các nhà máy chức năng có thể được sử dụng làm nhà máy trang trí, vì vậy chúng ta cũng có thể viết:

>>> @CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
... def py_cmp_func(a, b):
... print("py_cmp_func", a[0], b[0])
... trả về a[0] - b[0]
...
>>> qsort(ia, len(ia), sizeof(c_int), py_cmp_func)
py_cmp_func 5 1
py_cmp_func 33 99
py_cmp_func 7 33
py_cmp_func 1 7
py_cmp_func 5 7
>>>

Ghi chú

Đảm bảo bạn giữ các tham chiếu đến đối tượng CFUNCTYPE() miễn là chúng được sử dụng từ mã C. ctypes thì không, và nếu bạn không làm vậy, chúng có thể bị thu gom rác, làm hỏng chương trình của bạn khi thực hiện lệnh gọi lại.

Ngoài ra, hãy lưu ý rằng nếu hàm gọi lại được gọi trong một luồng được tạo bên ngoài sự kiểm soát của Python (ví dụ: bằng mã nước ngoài gọi hàm gọi lại), thì ctypes sẽ tạo một luồng Python giả mới trên mỗi lệnh gọi. Hành vi này đúng cho hầu hết các mục đích, nhưng điều đó có nghĩa là các giá trị được lưu trữ bằng threading.local sẽ tồn tại qua các lệnh gọi lại khác nhau, ngay cả khi các lệnh gọi đó được thực hiện từ cùng một luồng C.

Truy cập các giá trị được xuất từ dll

Một số thư viện dùng chung không chỉ xuất các hàm mà còn xuất các biến. Một ví dụ trong chính thư viện Python là Py_Version, số phiên bản thời gian chạy Python được mã hóa dưới dạng một số nguyên không đổi.

ctypes có thể truy cập các giá trị như thế này bằng các phương thức lớp in_dll() cùng loại. pythonapi là biểu tượng được xác định trước cho phép truy cập vào api Python C

>>> phiên bản = ctypes.c_int.in_dll(ctypes.pythonapi, "Py_Version")
>>> in(hex(version.value))
0x30c00a0

Một ví dụ mở rộng cũng minh họa cách sử dụng con trỏ truy cập con trỏ PyImport_FrozenModules do Python xuất.

Trích dẫn tài liệu cho giá trị đó:

Con trỏ này được khởi tạo để trỏ đến một mảng các bản ghi _frozen, được kết thúc bởi một bản ghi có tất cả các thành viên là NULL hoặc 0. Khi một mô-đun bị đóng băng được nhập vào, nó sẽ được tìm kiếm trong bảng này. Mã của bên thứ ba có thể giở trò với điều này để cung cấp một bộ sưu tập các mô-đun cố định được tạo động.

Vì vậy, việc thao tác con trỏ này thậm chí có thể hữu ích. Để hạn chế kích thước ví dụ, chúng tôi chỉ hiển thị cách đọc bảng này bằng ctypes:

>>> từ nhập ctypes *
>>>
>>> lớp struct_frozen(Cấu trúc):
... _fields_ = [("tên", c_char_p),
... ("mã", POINTER(c_ubyte)),
... ("kích thước", c_int),
... ("get_code", POINTER(c_ubyte)), con trỏ # Function
...]
...
>>>

Chúng tôi đã xác định kiểu dữ liệu _frozen, vì vậy chúng tôi có thể đưa con trỏ tới bảng

>>> FrozenTable = POINTER(struct_frozen)
>>> bảng = FrozenTable.in_dll(pythonapi, "_PyImport_FrozenBootstrap")
>>>

table là một pointer của mảng các bản ghi struct_frozen nên chúng ta có thể lặp lại nó, nhưng chúng ta chỉ cần đảm bảo rằng vòng lặp của chúng ta kết thúc, vì con trỏ không có kích thước. Sớm hay muộn nó có thể sẽ gặp sự cố do vi phạm quyền truy cập hoặc bất cứ điều gì, vì vậy tốt hơn là bạn nên thoát ra khỏi vòng lặp khi chúng ta nhấn vào mục NULL:

>>> cho mục trong bảng:
... nếu item.name  Không :
... nghỉ
... print(item.name.decode("ascii"), item.size)
...
_frozen_importlib 31764
_frozen_importlib_external 41499
zipnhập khẩu 12345
>>>

Việc Python tiêu chuẩn có mô-đun cố định và gói cố định (được biểu thị bằng thành viên size âm) không được nhiều người biết đến, nó chỉ được sử dụng để thử nghiệm. Hãy dùng thử với import __hello__ chẳng hạn.

bất ngờ

Có một số khía cạnh trong ctypes mà bạn có thể mong đợi điều gì đó khác với những gì thực sự xảy ra.

Hãy xem xét ví dụ sau:

>>> từ nhập ctypes *
>>> lớp POINT(Cấu trúc):
... _fields_ = ("x", c_int), ("y", c_int)
...
>>> lớp RECT(Cấu trúc):
... _fields_ = ("a", POINT), ("b", POINT)
...
>>> p1 = POINT(1, 2)
>>> p2 = POINT(3, 4)
>>> rc = RECT(p1, p2)
>>> print(rc.a.x, rc.a.y, rc.b.x, rc.b.y)
1 2 3 4
>>> # now hoán đổi hai điểm
>>> rc.a, rc.b = rc.b, rc.a
>>> print(rc.a.x, rc.a.y, rc.b.x, rc.b.y)
3 4 3 4
>>>

Ừm. Chúng tôi chắc chắn đã mong đợi câu lệnh cuối cùng sẽ in 3 4 1 2. Chuyện gì đã xảy ra thế? Dưới đây là các bước của dòng rc.a, rc.b = rc.b, rc.a ở trên:

>>> temp0, temp1 = rc.b, rc.a
>>> rc.a = temp0
>>> rc.b = temp1
>>>

Lưu ý rằng temp0temp1 là các đối tượng vẫn sử dụng bộ đệm trong của đối tượng rc ở trên. Vì vậy, việc thực thi rc.a = temp0 sẽ sao chép nội dung bộ đệm của temp0 vào bộ đệm của rc. Điều này lần lượt thay đổi nội dung của temp1. Vì vậy, nhiệm vụ cuối cùng rc.b = temp1 không mang lại hiệu quả như mong đợi.

Hãy nhớ rằng việc truy xuất các đối tượng con từ Cấu trúc, Liên kết và Mảng không copy đối tượng phụ, thay vào đó, nó truy xuất một đối tượng trình bao bọc truy cập vào bộ đệm bên dưới của đối tượng gốc.

Một ví dụ khác có thể hoạt động khác với những gì người ta mong đợi là đây:

>>> s = c_char_p()
>>> s.value = b"abc def ghi"
>>> s.giá trị
b'abc def ghi'
>>> s.value  s.value
sai
>>>

Ghi chú

Các đối tượng được khởi tạo từ c_char_p chỉ có thể đặt giá trị của chúng thành byte hoặc số nguyên.

Tại sao nó in False? Các phiên bản ctypes là các đối tượng chứa một khối bộ nhớ cộng với một số descriptors truy cập nội dung của bộ nhớ. Việc lưu trữ một đối tượng Python trong khối bộ nhớ không lưu trữ chính đối tượng đó, thay vào đó, contents của đối tượng được lưu trữ. Việc truy cập lại nội dung sẽ tạo ra một đối tượng Python mới mỗi lần!

Các kiểu dữ liệu có kích thước thay đổi

ctypes cung cấp một số hỗ trợ cho các mảng và cấu trúc có kích thước thay đổi.

Hàm resize() có thể được sử dụng để thay đổi kích thước bộ nhớ đệm của đối tượng ctypes hiện có. Hàm lấy đối tượng làm đối số đầu tiên và kích thước được yêu cầu tính bằng byte làm đối số thứ hai. Khối bộ nhớ không thể được tạo nhỏ hơn khối bộ nhớ tự nhiên được chỉ định bởi loại đối tượng, ValueError sẽ tăng lên nếu điều này được thử

>>> short_array = (c_short * 4)()
>>> in(sizeof(short_array))
8
>>> thay đổi kích thước(short_array, 4)
Traceback (cuộc gọi gần đây nhất):
    ...
ValueError: kích thước tối thiểu là 8
>>> thay đổi kích thước(short_array, 32)
>>> sizeof(short_array)
32
>>> sizeof(type(short_array))
8
>>>

Điều này tốt và ổn, nhưng làm cách nào để truy cập các phần tử bổ sung có trong mảng này? Vì loại vẫn chỉ biết về 4 phần tử nên chúng tôi gặp lỗi khi truy cập các phần tử khác

>>> short_array[:]
[0, 0, 0, 0]
>>> short_array[7]
Traceback (cuộc gọi gần đây nhất):
    ...
IndexError: chỉ mục không hợp lệ
>>>

Một cách khác để sử dụng các loại dữ liệu có kích thước thay đổi với ctypes là sử dụng tính chất động của Python và (-) xác định lại loại dữ liệu sau khi đã biết kích thước yêu cầu, tùy từng trường hợp.

tài liệu tham khảo ctypes

Tìm thư viện chia sẻ

Khi lập trình bằng ngôn ngữ được biên dịch, các thư viện dùng chung sẽ được truy cập khi biên dịch/liên kết chương trình và khi chương trình được chạy.

Mục đích của hàm find_library() là định vị thư viện theo cách tương tự như cách trình biên dịch hoặc trình tải thời gian thực thực hiện (trên nền tảng có một số phiên bản của thư viện dùng chung, phiên bản mới nhất sẽ được tải), trong khi trình tải thư viện ctypes hoạt động giống như khi một chương trình đang chạy và gọi trực tiếp trình tải thời gian chạy.

Mô-đun ctypes.util cung cấp một chức năng có thể giúp xác định thư viện sẽ tải.

ctypes.util.find_library(name)

Cố gắng tìm một thư viện và trả về tên đường dẫn. name là tên thư viện không có tiền tố như lib, hậu tố như .so, .dylib hoặc số phiên bản (đây là dạng dùng cho tùy chọn liên kết posix -l). Nếu không tìm thấy thư viện nào, trả về None.

Chức năng chính xác phụ thuộc vào hệ thống.

Trên Linux, find_library() cố gắng chạy các chương trình bên ngoài (/sbin/ldconfig, gcc, objdumpld) để tìm tệp thư viện. Nó trả về tên tệp của tệp thư viện.

Lưu ý rằng nếu đầu ra của các chương trình này không tương ứng với trình liên kết động được Python sử dụng thì kết quả của hàm này có thể sai lệch.

Thay đổi trong phiên bản 3.6: Trên Linux, giá trị của biến môi trường LD_LIBRARY_PATH được sử dụng khi tìm kiếm thư viện, nếu không thể tìm thấy thư viện bằng bất kỳ phương tiện nào khác.

Dưới đây là một số ví dụ:

>>> từ ctypes.util nhập find_library
>>> find_library("m")
'libm.so.6'
>>> find_library("c")
'libc.so.6'
>>> find_library("bz2")
'libbz2.so.1.0'
>>>

Trên macOS và Android, find_library() sử dụng đường dẫn và sơ đồ đặt tên tiêu chuẩn của hệ thống để định vị thư viện và trả về tên đường dẫn đầy đủ nếu thành công:

>>> từ ctypes.util nhập find_library
>>> find_library("c")
'/usr/lib/libc.dylib'
>>> find_library("m")
'/usr/lib/libm.dylib'
>>> find_library("bz2")
'/usr/lib/libbz2.dylib'
>>> find_library("AGL")
'/System/Library/Frameworks/AGL.framework/AGL'
>>>

Trên Windows, find_library() tìm kiếm dọc theo đường dẫn tìm kiếm hệ thống và trả về tên đường dẫn đầy đủ, nhưng vì không có sơ đồ đặt tên được xác định trước nên lệnh gọi như find_library("c") sẽ không thành công và trả về None.

Nếu gói một thư viện dùng chung bằng ctypes, tốt hơn hết là may nên xác định tên thư viện dùng chung tại thời điểm phát triển và mã hóa cứng tên đó vào mô-đun trình bao bọc thay vì sử dụng find_library() để định vị thư viện trong thời gian chạy.

Liệt kê các thư viện chia sẻ đã tải

Khi viết mã dựa trên mã được tải từ thư viện dùng chung, việc biết thư viện dùng chung nào đã được tải vào quy trình hiện tại có thể hữu ích.

Mô-đun ctypes.util cung cấp chức năng dllist(), gọi các API khác nhau do các nền tảng khác nhau cung cấp để giúp xác định thư viện dùng chung nào đã được tải vào quy trình hiện tại.

Đầu ra chính xác của chức năng này sẽ phụ thuộc vào hệ thống. Trên hầu hết các nền tảng, mục đầu tiên của danh sách này đại diện cho chính quy trình hiện tại, có thể là một chuỗi trống. Ví dụ: trên Linux dựa trên glibc, kết quả trả về có thể trông như sau:

>>> từ danh sách dll nhập ctypes.util
>>> dlllist()
['', 'linux-vdso.so.1', '/lib/x86_64-linux-gnu/libm.so.6', '/lib/x86_64-linux-gnu/libc.so.6', ... ]

Đang tải thư viện chia sẻ

Có một số cách để tải các thư viện dùng chung vào quy trình Python. Một cách là khởi tạo CDLL hoặc một trong các lớp con của nó:

class ctypes.CDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False, winmode=None)

Đại diện cho một thư viện chia sẻ được tải.

Các hàm trong thư viện này sử dụng quy ước gọi C tiêu chuẩn và được giả định trả về int. Python global interpreter lock được phát hành trước khi gọi bất kỳ hàm nào được xuất bởi các thư viện này và được yêu cầu lại sau đó. Đối với các hành vi chức năng khác nhau, hãy sử dụng lớp con: OleDLL, WinDLL hoặc PyDLL.

Nếu bạn có một handle hiện có cho một thư viện chia sẻ đã được tải, nó có thể được chuyển dưới dạng đối số handle để bọc thư viện đã mở trong một đối tượng CDLL mới. Trong trường hợp này, name chỉ được sử dụng để đặt thuộc tính _name nhưng nó có thể được điều chỉnh và/hoặc xác thực.

Nếu handleNone, thì hàm dlopen(3) hoặc LoadLibrary() của nền tảng cơ bản sẽ được sử dụng để tải thư viện vào quy trình và xử lý nó.

name là tên đường dẫn của thư viện dùng chung để mở. Nếu name không chứa dấu phân cách đường dẫn, thư viện sẽ được tìm thấy theo cách dành riêng cho nền tảng.

Trên các hệ thống không phải Windows, name có thể là None. Trong trường hợp này, dlopen() được gọi bằng NULL, nó sẽ mở chương trình chính dưới dạng "thư viện". (Một số hệ thống làm tương tự là name trống; None/NULL dễ mang theo hơn.)

Chi tiết triển khai CPython

Vì CPython được liên kết với libc nên None name thường được sử dụng để truy cập thư viện chuẩn C:

>>> printf = ctypes.CDLL(Không ).printf
>>> printf.argtypes = [ctypes.c_char_p]
>>> printf(b"xin chào\n")
xin chào
6

Để truy cập Python C API, hãy ưu tiên ctypes.pythonapi hoạt động trên nhiều nền tảng.

Tham số mode có thể được sử dụng để chỉ định cách tải thư viện. Để biết chi tiết, hãy tham khảo trang chủ dlopen(3). Trên Windows, mode bị bỏ qua. Trên hệ thống posix, RTLD_NOW luôn được thêm vào và không thể định cấu hình được.

Tham số use_errno, khi được đặt thành true, sẽ kích hoạt cơ chế ctypes cho phép truy cập vào số lỗi errno của hệ thống một cách an toàn. ctypes duy trì một bản sao luồng cục bộ của biến errno của hệ thống; nếu bạn gọi các hàm nước ngoài được tạo bằng use_errno=True thì giá trị errno trước lệnh gọi hàm sẽ được hoán đổi bằng bản sao riêng ctypes, điều tương tự cũng xảy ra ngay sau lệnh gọi hàm.

Hàm ctypes.get_errno() trả về giá trị của bản sao riêng tư của ctypes và hàm ctypes.set_errno() thay đổi bản sao riêng tư của ctypes thành một giá trị mới và trả về giá trị cũ.

Tham số use_last_error, khi được đặt thành true, sẽ kích hoạt cơ chế tương tự đối với mã lỗi Windows được quản lý bởi các hàm GetLastError()SetLastError() của Windows API; ctypes.get_last_error()ctypes.set_last_error() được sử dụng để yêu cầu và thay đổi bản sao riêng ctypes của mã lỗi windows.

Tham số winmode được sử dụng trên Windows để chỉ định cách tải thư viện (vì mode bị bỏ qua). Nó nhận bất kỳ giá trị nào hợp lệ cho tham số cờ Win32 API LoadLibraryEx. Khi bị bỏ qua, mặc định là sử dụng các cờ dẫn đến tải DLL an toàn nhất, giúp tránh các vấn đề như chiếm quyền điều khiển DLL. Chuyển đường dẫn đầy đủ tới DLL là cách an toàn nhất để đảm bảo tải đúng thư viện và các phần phụ thuộc.

Trên Windows, việc tạo phiên bản CDLL có thể không thành công ngay cả khi tên DLL tồn tại. Khi không tìm thấy DLL phụ thuộc của DLL đã tải, lỗi OSError sẽ xuất hiện cùng với thông báo "[WinError 126] The specified module could not be found". Thông báo lỗi này không chứa tên của DLL bị thiếu vì Windows API không trả về thông tin này khiến lỗi này khó chẩn đoán. Để khắc phục lỗi này và xác định DLL nào không được tìm thấy, bạn cần tìm danh sách các DLL phụ thuộc và xác định cái nào không được tìm thấy bằng các công cụ truy tìm và gỡ lỗi của Windows.

Xem thêm

Microsoft DUMPBIN tool -- Một công cụ để tìm người phụ thuộc DLL.

Thay đổi trong phiên bản 3.8: Đã thêm tham số winmode.

Thay đổi trong phiên bản 3.12: Tham số name bây giờ có thể là path-like object.

Các thể hiện của lớp này không có phương thức công khai. Các hàm được xuất bởi thư viện dùng chung có thể được truy cập dưới dạng thuộc tính hoặc theo chỉ mục. Xin lưu ý rằng việc truy cập hàm thông qua một thuộc tính sẽ lưu trữ kết quả và do đó việc truy cập nó liên tục trả về cùng một đối tượng mỗi lần. Mặt khác, việc truy cập nó thông qua một chỉ mục sẽ trả về một đối tượng mới mỗi lần

>>> từ ctypes nhập CDLL
>>> libc = CDLL("libc.so.6") # On Linux
>>> libc.time == libc.time
đúng
>>> libc['time'] == libc['time']
sai

Các thuộc tính công khai sau đây có sẵn. Tên của chúng bắt đầu bằng dấu gạch dưới để không xung đột với tên hàm đã xuất:

_handle

Tay cầm hệ thống được sử dụng để truy cập thư viện.

_name

Tên của thư viện được truyền vào hàm tạo.

class ctypes.OleDLL

Xem CDLL, siêu lớp, để biết thông tin chung.

Các hàm trong thư viện này sử dụng quy ước gọi stdcall và được giả định trả về mã HRESULT cụ thể của windows. Các giá trị HRESULT chứa thông tin xác định lệnh gọi hàm thất bại hay thành công, cùng với mã lỗi bổ sung. Nếu giá trị trả về báo hiệu lỗi, OSError sẽ tự động được nâng lên.

sẵn có: Windows

Thay đổi trong phiên bản 3.3: WindowsError từng được nâng lên, hiện là bí danh của OSError.

class ctypes.WinDLL

Xem CDLL, siêu lớp, để biết thông tin chung.

Các hàm trong các thư viện này sử dụng quy ước gọi stdcall và được giả định trả về int theo mặc định.

sẵn có: Windows

class ctypes.PyDLL

Xem CDLL, siêu lớp, để biết thông tin chung.

Khi các hàm trong thư viện này được gọi, GIL của Python sẽ được giải phóng trong quá trình gọi hàm và sau khi thực thi hàm, cờ lỗi Python sẽ được kiểm tra. Nếu cờ lỗi được đặt, ngoại lệ Python sẽ xuất hiện.

Vì vậy, điều này chỉ hữu ích khi gọi trực tiếp các hàm Python C API.

ctypes.RTLD_GLOBAL

Cờ để sử dụng làm tham số mode. Trên các nền tảng không có cờ này, nó được định nghĩa là số nguyên 0.

ctypes.RTLD_LOCAL

Cờ để sử dụng làm tham số mode. Trên các nền tảng không có sẵn, nó giống như RTLD_GLOBAL.

ctypes.DEFAULT_MODE

Chế độ mặc định được sử dụng để tải các thư viện dùng chung. Trên OSX 10.3 thì đây là RTLD_GLOBAL, còn lại thì giống với RTLD_LOCAL.

Các thư viện dùng chung cũng có thể được tải bằng cách sử dụng một trong các đối tượng đúc sẵn, là các phiên bản của lớp LibraryLoader, bằng cách gọi phương thức LoadLibrary() hoặc bằng cách truy xuất thư viện dưới dạng thuộc tính của phiên bản trình tải.

class ctypes.LibraryLoader(dlltype)

Lớp tải các thư viện dùng chung. dlltype phải là một trong các loại CDLL, PyDLL, WinDLL hoặc OleDLL.

__getattr__() có hành vi đặc biệt: Nó cho phép tải thư viện dùng chung bằng cách truy cập nó dưới dạng thuộc tính của phiên bản trình tải thư viện. Kết quả được lưu vào bộ nhớ đệm, do đó, các lần truy cập thuộc tính lặp lại sẽ trả về cùng một thư viện mỗi lần.

LoadLibrary(name)

Tải thư viện dùng chung vào quy trình và trả lại. Phương thức này luôn trả về một phiên bản mới của thư viện.

Các trình tải thư viện đúc sẵn này có sẵn:

ctypes.cdll

Tạo phiên bản CDLL.

ctypes.windll

Tạo phiên bản WinDLL.

sẵn có: Windows

ctypes.oledll

Tạo phiên bản OleDLL.

sẵn có: Windows

ctypes.pydll

Tạo phiên bản PyDLL.

Để truy cập trực tiếp vào api C Python, có sẵn một đối tượng thư viện chia sẻ Python sẵn sàng sử dụng:

ctypes.pythonapi

Một phiên bản của PyDLL hiển thị các hàm API của Python C dưới dạng thuộc tính. Note that all these functions are assumed to return C int, which is of course not always the truth, so you have to assign the correct restype attribute to use these functions.

Việc tải thư viện thông qua bất kỳ đối tượng nào trong số này sẽ tạo ra một auditing event ctypes.dlopen với đối số chuỗi name, tên được sử dụng để tải thư viện.

Việc truy cập một hàm trên thư viện đã tải sẽ tạo ra một sự kiện kiểm tra ctypes.dlsym với các đối số library (đối tượng thư viện) và name (tên ký hiệu dưới dạng chuỗi hoặc số nguyên).

Trong trường hợp chỉ có trình điều khiển thư viện chứ không phải đối tượng, việc truy cập một hàm sẽ phát sinh sự kiện kiểm tra ctypes.dlsym/handle với các đối số handle (điều khiển thư viện thô) và name.

Chức năng đối ngoại

Như đã giải thích ở phần trước, các hàm ngoại có thể được truy cập dưới dạng thuộc tính của các thư viện dùng chung được tải. Các đối tượng hàm được tạo theo cách này theo mặc định chấp nhận bất kỳ số lượng đối số nào, chấp nhận bất kỳ trường hợp dữ liệu ctypes nào làm đối số và trả về loại kết quả mặc định được chỉ định bởi trình tải thư viện.

Chúng là các phiên bản của lớp _FuncPtr cục bộ riêng tư (không được hiển thị trong ctypes) kế thừa từ lớp _CFuncPtr riêng tư:

>>> nhập ctypes
>>> lib = ctypes.CDLL(Không )
>>> issubclass(lib._FuncPtr, ctypes._CFuncPtr)
đúng
>>> lib._FuncPtr  ctypes._CFuncPtr
sai
class ctypes._CFuncPtr

Lớp cơ sở cho các hàm nước ngoài có thể gọi được trong C.

Các phiên bản của hàm ngoại cũng là loại dữ liệu tương thích với C; chúng đại diện cho các con trỏ hàm C.

Hành vi này có thể được tùy chỉnh bằng cách gán cho các thuộc tính đặc biệt của đối tượng hàm ngoại.

restype

Gán loại ctypes để chỉ định loại kết quả của hàm ngoại. Sử dụng None cho void, một hàm không trả về bất cứ thứ gì.

Có thể gán một đối tượng Python có thể gọi được không phải là loại ctypes, trong trường hợp này, hàm được giả sử trả về C int và đối tượng có thể gọi được sẽ được gọi với số nguyên này, cho phép xử lý thêm hoặc kiểm tra lỗi. Việc sử dụng tính năng này không được dùng nữa, để xử lý bài đăng hoặc kiểm tra lỗi linh hoạt hơn, hãy sử dụng kiểu dữ liệu ctypes là restype và gán một lệnh gọi được cho thuộc tính errcheck.

argtypes

Gán một bộ loại ctypes để chỉ định loại đối số mà hàm chấp nhận. Các hàm sử dụng quy ước gọi stdcall chỉ có thể được gọi với cùng số lượng đối số bằng độ dài của bộ dữ liệu này; các hàm sử dụng quy ước gọi C cũng chấp nhận các đối số bổ sung, không xác định.

Khi một hàm ngoại được gọi, mỗi đối số thực tế sẽ được chuyển đến phương thức lớp from_param() của các mục trong bộ argtypes, phương thức này cho phép điều chỉnh đối số thực tế cho phù hợp với một đối tượng mà hàm ngoại chấp nhận. Ví dụ: một mục c_char_p trong bộ argtypes sẽ chuyển đổi một chuỗi được truyền dưới dạng đối số thành đối tượng byte bằng cách sử dụng quy tắc chuyển đổi ctypes.

Mới: Hiện tại có thể đặt các mục trong argtypes không phải là loại ctypes, nhưng mỗi mục phải có một phương thức from_param() trả về một giá trị có thể sử dụng làm đối số (ví dụ số nguyên, chuỗi, ctypes). Điều này cho phép xác định các bộ điều hợp có thể điều chỉnh các đối tượng tùy chỉnh dưới dạng tham số hàm.

errcheck

Gán một hàm Python hoặc một hàm khác có thể gọi được cho thuộc tính này. Có thể gọi được sẽ được gọi với ba đối số trở lên:

callable(result, func, arguments)

result là hàm ngoại trả về, như được chỉ định bởi thuộc tính restype.

func chính là đối tượng hàm ngoại, điều này cho phép sử dụng lại cùng một đối tượng có thể gọi được để kiểm tra hoặc đăng xử lý kết quả của một số hàm.

arguments là một bộ chứa các tham số ban đầu được truyền cho lệnh gọi hàm, điều này cho phép chuyên môn hóa hành vi trên các đối số được sử dụng.

Đối tượng mà hàm này trả về sẽ được trả về từ lệnh gọi hàm ngoại, nhưng nó cũng có thể kiểm tra giá trị kết quả và đưa ra một ngoại lệ nếu lệnh gọi hàm ngoại không thành công.

Trên Windows, khi một lệnh gọi hàm nước ngoài phát sinh một ngoại lệ hệ thống (ví dụ: do vi phạm quyền truy cập), nó sẽ bị ghi lại và thay thế bằng một ngoại lệ Python phù hợp. Hơn nữa, một sự kiện kiểm tra ctypes.set_exception với đối số code sẽ được đưa ra, cho phép móc kiểm tra thay thế ngoại lệ bằng ngoại lệ của chính nó.

Một số cách để gọi các lệnh gọi hàm nước ngoài cũng như một số hàm trong mô-đun này có thể tạo ra sự kiện kiểm tra ctypes.call_function với các đối số function pointerarguments.

Nguyên mẫu chức năng

Các hàm ngoại cũng có thể được tạo bằng cách khởi tạo các nguyên mẫu hàm. Nguyên mẫu hàm tương tự như nguyên mẫu hàm trong C; họ mô tả một hàm (kiểu trả về, kiểu đối số, quy ước gọi) mà không xác định cách triển khai. Các hàm xuất xưởng phải được gọi với loại kết quả mong muốn và các loại đối số của hàm, đồng thời có thể được sử dụng làm các xưởng trang trí và do đó, được áp dụng cho các hàm thông qua cú pháp @wrapper. Xem Chức năng gọi lại để biết ví dụ.

ctypes.CFUNCTYPE(restype, *argtypes, use_errno=False, use_last_error=False)

Nguyên mẫu hàm được trả về tạo ra các hàm sử dụng quy ước gọi C tiêu chuẩn. Chức năng này sẽ giải phóng GIL trong khi gọi. Nếu use_errno được đặt thành true, bản sao riêng ctypes của biến errno hệ thống sẽ được trao đổi với giá trị errno thực trước và sau cuộc gọi; use_last_error cũng làm tương tự với mã lỗi Windows.

ctypes.WINFUNCTYPE(restype, *argtypes, use_errno=False, use_last_error=False)

Nguyên mẫu hàm được trả về tạo ra các hàm sử dụng quy ước gọi stdcall. Chức năng này sẽ giải phóng GIL trong khi gọi. use_errnouse_last_error có ý nghĩa tương tự như trên.

sẵn có: Windows

ctypes.PYFUNCTYPE(restype, *argtypes)

Nguyên mẫu hàm được trả về tạo ra các hàm sử dụng quy ước gọi Python. Chức năng này sẽ not giải phóng GIL trong khi gọi.

Nguyên mẫu hàm được tạo bởi các hàm xuất xưởng này có thể được khởi tạo theo nhiều cách khác nhau, tùy thuộc vào loại và số lượng tham số trong lệnh gọi:

prototype(address)

Trả về hàm ngoại tại địa chỉ đã chỉ định và phải là số nguyên.

prototype(callable)

Tạo hàm có thể gọi C (hàm gọi lại) từ Python callable.

prototype(func_spec[, paramflags])

Trả về một hàm ngoại được xuất bởi thư viện dùng chung. func_spec phải là (name_or_ordinal, library) gồm 2 bộ. Mục đầu tiên là tên của hàm được xuất dưới dạng chuỗi hoặc thứ tự của hàm được xuất dưới dạng số nguyên nhỏ. Mục thứ hai là phiên bản thư viện dùng chung.

prototype(vtbl_index, name[, paramflags[, iid]])

Trả về một hàm ngoại sẽ gọi phương thức COM. vtbl_index là chỉ số trong bảng hàm ảo, một số nguyên nhỏ không âm. name là tên của phương thức COM. iid là một con trỏ tùy chọn tới mã định danh giao diện được sử dụng trong báo cáo lỗi mở rộng.

Nếu iid không được chỉ định, OSError sẽ được nâng lên nếu lệnh gọi phương thức COM không thành công. Nếu iid được chỉ định, thay vào đó, COMError sẽ được nâng lên.

Các phương thức COM sử dụng quy ước gọi đặc biệt: Chúng yêu cầu một con trỏ tới giao diện COM làm đối số đầu tiên, ngoài các tham số được chỉ định trong bộ dữ liệu argtypes.

sẵn có: Windows

Tham số paramflags tùy chọn tạo ra các trình bao bọc hàm ngoại với nhiều chức năng hơn các tính năng được mô tả ở trên.

paramflags phải là một bộ có cùng độ dài với argtypes.

Mỗi mục trong bộ dữ liệu này chứa thông tin bổ sung về một tham số, nó phải là một bộ dữ liệu chứa một, hai hoặc ba mục.

Mục đầu tiên là một số nguyên chứa tổ hợp các cờ chỉ hướng cho tham số:

1

Chỉ định một tham số đầu vào cho hàm.

2

Tham số đầu ra. Hàm nước ngoài điền vào một giá trị.

4

Tham số đầu vào mặc định là số nguyên 0.

Mục thứ hai tùy chọn là tên tham số dưới dạng chuỗi. Nếu điều này được chỉ định, hàm ngoại có thể được gọi với các tham số được đặt tên.

Mục thứ ba tùy chọn là giá trị mặc định cho tham số này.

Ví dụ sau đây minh họa cách bọc hàm Windows MessageBoxW để nó hỗ trợ các tham số mặc định và các đối số được đặt tên. Khai báo C từ tệp tiêu đề windows là thế này

WINUSERAPI int WINAPI
MessageBoxW(
    HWND hWnd,
    LPCWSTR lpText,
    chú thích LPCWSTR lp,
    UINT uType);

Đây là gói với ctypes:

>>> từ ctypes nhập c_int, WINFUNCTYPE, windll
>>> từ ctypes.wintypes nhập HWND, LPCWSTR, UINT
>>> nguyên mẫu = WINFUNCTYPE(c_int, HWND, LPCWSTR, LPCWSTR, UINT)
>>> paramflags = (1, "hwnd", 0), (1, "text", "Hi"), (1, "caption", "Xin chào từ ctypes"), (1, "flags", 0)
>>> MessageBox = nguyên mẫu(("MessageBoxW", Windll.user32), paramflags)

Giờ đây, hàm ngoại MessageBox có thể được gọi theo những cách sau:

>>> Hộp tin nhắn()
>>> MessageBox(text="Thư rác, thư rác, thư rác")
>>> MessageBox(flags=2, text="foo bar")

Ví dụ thứ hai thể hiện các tham số đầu ra. Hàm win32 GetWindowRect truy xuất kích thước của một cửa sổ được chỉ định bằng cách sao chép chúng vào cấu trúc RECT mà người gọi phải cung cấp. Đây là khai báo C:

WINUSERAPI BOOL WINAPI
GetWindowRect(
     HWND hWnd,
     LPRECT lpRect);

Đây là gói với ctypes:

>>> từ ctypes nhập POINTER, WINFUNCTYPE, windll, WinError
>>> từ ctypes.wintypes nhập BOOL, HWND, RECT
>>> nguyên mẫu = WINFUNCTYPE(BOOL, HWND, POINTER(RECT))
>>> paramflags = (1, "hwnd"), (2, "lpfect")
>>> GetWindowRect = nguyên mẫu(("GetWindowRect", windll.user32), paramflags)
>>>

Các hàm có tham số đầu ra sẽ tự động trả về giá trị tham số đầu ra nếu có một giá trị tham số đầu ra hoặc một bộ chứa các giá trị tham số đầu ra khi có nhiều hơn một, do đó, hàm GetWindowRect hiện trả về một phiên bản RECT khi được gọi.

Các tham số đầu ra có thể được kết hợp với giao thức errcheck để thực hiện thêm quá trình xử lý đầu ra và kiểm tra lỗi. Hàm api win32 GetWindowRect trả về BOOL để báo hiệu thành công hay thất bại, do đó, hàm này có thể thực hiện kiểm tra lỗi và đưa ra một ngoại lệ khi lệnh gọi api không thành công:

>>> def errcheck(kết quả, func, args):
... nếu không  kết quả:
... nâng cao WinError()
... trả về đối số
...
>>> GetWindowRect.errcheck = errcheck
>>>

Nếu hàm errcheck trả về bộ đối số mà nó nhận được không thay đổi, thì ctypes sẽ tiếp tục quá trình xử lý bình thường đối với các tham số đầu ra. Nếu muốn trả về một bộ tọa độ cửa sổ thay vì phiên bản RECT, bạn có thể truy xuất các trường trong hàm và trả về chúng, quá trình xử lý thông thường sẽ không diễn ra nữa:

>>> def errcheck(kết quả, func, args):
... nếu không  kết quả:
... nâng cao WinError()
... rc = args[1]
... trả về rc.left, rc.top, rc.bottom, rc.right
...
>>> GetWindowRect.errcheck = errcheck
>>>

Chức năng tiện ích

ctypes.addressof(obj)

Trả về địa chỉ của bộ nhớ đệm dưới dạng số nguyên. obj phải là phiên bản của loại ctypes.

Tăng một auditing event ctypes.addressof với đối số obj.

ctypes.alignment(obj_or_type)

Trả về các yêu cầu căn chỉnh của loại ctypes. obj_or_type phải là loại hoặc phiên bản ctypes.

ctypes.byref(obj[, offset])

Trả về một con trỏ nhẹ cho obj, con trỏ này phải là một phiên bản của loại ctypes. offset mặc định là 0 và phải là số nguyên sẽ được thêm vào giá trị con trỏ bên trong.

byref(obj, offset) tương ứng với mã C này:

(((char *)&obj) + offset)

Đối tượng được trả về chỉ có thể được sử dụng làm tham số gọi hàm ngoại. Nó hoạt động tương tự như pointer(obj), nhưng việc xây dựng nhanh hơn rất nhiều.

ctypes.CopyComPointer(src, dst)

Sao chép con trỏ COM từ src sang dst và trả về giá trị HRESULT cụ thể của Windows.

Nếu src không phải là NULL, phương thức AddRef của nó sẽ được gọi, tăng số lượng tham chiếu.

Ngược lại, số tham chiếu của dst sẽ không bị giảm trước khi gán giá trị mới. Trừ khi dstNULL, người gọi có trách nhiệm giảm số lượng tham chiếu bằng cách gọi phương thức Release của nó khi cần thiết.

sẵn có: Windows

Added in version 3.14.

ctypes.cast(obj, type)

Hàm này tương tự như toán tử ép kiểu trong C. Nó trả về một phiên bản mới của type trỏ đến cùng khối bộ nhớ với obj. type phải là một loại con trỏ và obj phải là một đối tượng có thể được hiểu là một con trỏ.

ctypes.create_string_buffer(init, size=None)
ctypes.create_string_buffer(size)

Hàm này tạo ra một bộ đệm ký tự có thể thay đổi. Đối tượng được trả về là mảng ctypes của c_char.

Nếu size được cung cấp (chứ không phải None), thì đó phải là int. Nó chỉ định kích thước của mảng trả về.

Nếu đối số init được đưa ra thì nó phải là bytes. Nó được sử dụng để khởi tạo các mục mảng. Các byte không được khởi tạo theo cách này được đặt thành 0 (NUL).

Nếu size không được cung cấp (hoặc nếu là None), bộ đệm sẽ được tạo một phần tử lớn hơn init, thêm một bộ kết thúc NUL một cách hiệu quả.

Nếu cả hai đối số đều được đưa ra thì size không được nhỏ hơn len(init).

Cảnh báo

Nếu size bằng len(init) thì dấu kết thúc NUL sẽ không được thêm vào. Đừng coi bộ đệm như vậy là chuỗi C.

Ví dụ:

>>> byte(create_string_buffer(2))
b'\x00\x00'
>>> byte(create_string_buffer(b'ab'))
b'ab\x00'
>>> byte(create_string_buffer(b'ab', 2))
b'ab'
>>> byte(create_string_buffer(b'ab', 4))
b'ab\x00\x00'
>>> byte(create_string_buffer(b'abcdef', 2))
Traceback (cuộc gọi gần đây nhất):
   ...
ValueError: chuỗi byte quá dài

Tăng một auditing event ctypes.create_string_buffer với các đối số init, size.

ctypes.create_unicode_buffer(init, size=None)
ctypes.create_unicode_buffer(size)

Hàm này tạo ra một bộ đệm ký tự unicode có thể thay đổi. Đối tượng được trả về là mảng ctypes của c_wchar.

Hàm này nhận các đối số giống như create_string_buffer() ngoại trừ init phải là một chuỗi và size đếm c_wchar.

Tăng một auditing event ctypes.create_unicode_buffer với các đối số init, size.

ctypes.DllCanUnloadNow()

Chức năng này là một cái móc cho phép triển khai các máy chủ COM trong quá trình với ctypes. Nó được gọi từ hàm DllCanUnloadNow mà dll mở rộng _ctypes xuất.

sẵn có: Windows

ctypes.DllGetClassObject()

Chức năng này là một cái móc cho phép triển khai các máy chủ COM trong quá trình với ctypes. Nó được gọi từ hàm DllGetClassObject mà dll mở rộng _ctypes xuất.

sẵn có: Windows

ctypes.util.find_library(name)

Cố gắng tìm một thư viện và trả về tên đường dẫn. name là tên thư viện không có tiền tố như lib, hậu tố như .so, .dylib hoặc số phiên bản (đây là dạng dùng cho tùy chọn liên kết posix -l). Nếu không tìm thấy thư viện, trả về None.

Chức năng chính xác phụ thuộc vào hệ thống.

Xem Tìm thư viện chia sẻ để có tài liệu đầy đủ.

ctypes.util.find_msvcrt()

Trả về tên tệp của thư viện thời gian chạy VC được Python sử dụng và bởi các mô-đun mở rộng. Nếu không xác định được tên thư viện, None sẽ được trả về.

Ví dụ: nếu bạn cần giải phóng bộ nhớ được phân bổ bởi mô-đun mở rộng có lệnh gọi đến free(void *), điều quan trọng là bạn phải sử dụng hàm trong cùng thư viện đã phân bổ bộ nhớ.

sẵn có: Windows

ctypes.util.dllist()

Cố gắng cung cấp danh sách đường dẫn của các thư viện dùng chung được tải vào quy trình hiện tại. Những đường dẫn này không được chuẩn hóa hoặc xử lý theo bất kỳ cách nào. Hàm này có thể tăng OSError nếu API nền tảng cơ bản không thành công. Chức năng chính xác phụ thuộc vào hệ thống.

On most platforms, the first element of the list represents the current executable file. Nó có thể là một chuỗi trống.

sẵn có: Windows, macOS, iOS, glibc, BSD libc, musl

Added in version 3.14.

ctypes.FormatError([code])

Trả về mô tả bằng văn bản của mã lỗi code. Nếu không chỉ định mã lỗi, mã lỗi cuối cùng sẽ được sử dụng bằng cách gọi hàm Windows API GetLastError().

sẵn có: Windows

ctypes.GetLastError()

Trả về mã lỗi cuối cùng do Windows đặt trong chuỗi cuộc gọi. Hàm này gọi trực tiếp hàm GetLastError() của Windows, nó không trả về bản sao mã lỗi ctypes-private.

sẵn có: Windows

ctypes.get_errno()

Trả về giá trị hiện tại của bản sao ctypes-private của biến errno hệ thống trong luồng gọi.

Tăng auditing event ctypes.get_errno mà không có đối số.

ctypes.get_last_error()

Trả về giá trị hiện tại của bản sao ctypes-private của biến LastError hệ thống trong luồng gọi.

sẵn có: Windows

Tăng auditing event ctypes.get_last_error mà không có đối số.

ctypes.memmove(dst, src, count)

Tương tự như chức năng thư viện memmove C tiêu chuẩn: sao chép byte count từ src sang dst. dstsrc phải là số nguyên hoặc ctypes có thể được chuyển đổi thành con trỏ.

ctypes.memset(dst, c, count)

Tương tự như chức năng thư viện bộ nhớ C tiêu chuẩn: lấp đầy khối bộ nhớ tại địa chỉ dst với count byte có giá trị c. dst phải là số nguyên chỉ định địa chỉ hoặc phiên bản ctypes.

ctypes.POINTER(type, /)

Tạo hoặc trả về kiểu con trỏ ctypes. Các loại con trỏ được lưu vào bộ nhớ đệm và tái sử dụng nội bộ, vì vậy việc gọi hàm này nhiều lần sẽ rẻ hơn. type phải là loại ctypes.

Kiểu con trỏ kết quả được lưu trữ trong thuộc tính __pointer_type__ của type. Có thể đặt thuộc tính này trước lệnh gọi đầu tiên tới POINTER để đặt loại con trỏ tùy chỉnh. Tuy nhiên, việc này không được khuyến khích: việc tạo thủ công một loại con trỏ phù hợp là khó khăn nếu không dựa vào các chi tiết triển khai có thể thay đổi trong các phiên bản Python trong tương lai.

ctypes.pointer(obj, /)

Tạo một thể hiện con trỏ mới, trỏ tới obj. Đối tượng được trả về có kiểu POINTER(type(obj)).

Lưu ý: Nếu bạn chỉ muốn chuyển con trỏ tới một đối tượng cho lệnh gọi hàm ngoại, bạn nên sử dụng byref(obj) để nhanh hơn nhiều.

ctypes.resize(obj, size)

Hàm này thay đổi kích thước bộ đệm bộ nhớ trong của obj, bộ đệm này phải là một phiên bản của loại ctypes. Không thể làm cho bộ đệm nhỏ hơn kích thước gốc của loại đối tượng, như được đưa ra bởi sizeof(type(obj)), nhưng có thể phóng to bộ đệm.

ctypes.set_errno(value)

Đặt giá trị hiện tại của bản sao ctypes-private của biến errno hệ thống trong luồng gọi thành value và trả về giá trị trước đó.

Tăng một auditing event ctypes.set_errno với đối số errno.

ctypes.set_last_error(value)

Đặt giá trị hiện tại của bản sao ctypes-private của biến LastError hệ thống trong luồng gọi thành value và trả về giá trị trước đó.

sẵn có: Windows

Tăng một auditing event ctypes.set_last_error với đối số error.

ctypes.sizeof(obj_or_type)

Trả về kích thước tính bằng byte của loại ctypes hoặc bộ đệm bộ nhớ phiên bản. Thực hiện tương tự như toán tử C sizeof.

ctypes.string_at(ptr, size=-1)

Trả về chuỗi byte tại void *ptr. If size được chỉ định, nó được sử dụng làm kích thước, nếu không thì chuỗi được coi là kết thúc bằng 0.

Tăng một auditing event ctypes.string_at với các đối số ptr, size.

ctypes.WinError(code=None, descr=None)

Tạo một phiên bản của OSError. Nếu code không được chỉ định, GetLastError() sẽ được gọi để xác định mã lỗi. Nếu descr không được chỉ định, FormatError() sẽ được gọi để nhận mô tả bằng văn bản về lỗi.

sẵn có: Windows

Thay đổi trong phiên bản 3.3: Một phiên bản của WindowsError đã từng được tạo, hiện là bí danh của OSError.

ctypes.wstring_at(ptr, size=-1)

Trả về chuỗi ký tự rộng tại void *ptr. If size được chỉ định, nó được sử dụng làm số ký tự của chuỗi, nếu không thì chuỗi được coi là kết thúc bằng 0.

Tăng một auditing event ctypes.wstring_at với các đối số ptr, size.

ctypes.memoryview_at(ptr, size, readonly=False)

Trả về đối tượng memoryview có độ dài size tham chiếu bộ nhớ bắt đầu từ void *ptr.

Nếu readonly là đúng, đối tượng memoryview được trả về không thể được sử dụng để sửa đổi bộ nhớ cơ bản. (Những thay đổi được thực hiện bằng các phương tiện khác sẽ vẫn được phản ánh trong đối tượng được trả về.)

Chức năng này tương tự như string_at() với điểm khác biệt chính là không tạo bản sao của bộ nhớ được chỉ định. Nó là một giải pháp thay thế tương đương về mặt ngữ nghĩa (nhưng hiệu quả hơn) cho memoryview((c_byte * size).from_address(ptr)). (Trong khi from_address() chỉ lấy số nguyên, ptr cũng có thể được cung cấp dưới dạng đối tượng ctypes.POINTER hoặc byref().)

Tăng một auditing event ctypes.memoryview_at với các đối số address, size, readonly.

Added in version 3.14.

Các kiểu dữ liệu

class ctypes._CData

Lớp không công khai này là lớp cơ sở chung của tất cả các kiểu dữ liệu ctypes. Trong số những thứ khác, tất cả các phiên bản loại ctypes đều chứa một khối bộ nhớ chứa dữ liệu tương thích với C; địa chỉ của khối bộ nhớ được trả về bởi hàm trợ giúp addressof(). Một biến thể hiện khác được hiển thị dưới dạng _objects; cái này chứa các đối tượng Python khác cần được duy trì trong trường hợp khối bộ nhớ chứa con trỏ.

Các phương thức phổ biến của kiểu dữ liệu ctypes, đây đều là các phương thức lớp (chính xác thì chúng là các phương thức của metaclass):

from_buffer(source[, offset])

Phương thức này trả về một thể hiện ctypes chia sẻ bộ đệm của đối tượng source. Đối tượng source phải hỗ trợ giao diện bộ đệm có thể ghi. Tham số offset tùy chọn chỉ định phần bù vào bộ đệm nguồn theo byte; mặc định là bằng không. Nếu bộ đệm nguồn không đủ lớn thì ValueError sẽ được nâng lên.

Tăng một auditing event ctypes.cdata/buffer với các đối số pointer, size, offset.

from_buffer_copy(source[, offset])

Phương thức này tạo một thể hiện ctypes, sao chép bộ đệm từ bộ đệm đối tượng source mà bộ đệm này phải có thể đọc được. Tham số offset tùy chọn chỉ định phần bù vào bộ đệm nguồn tính bằng byte; mặc định là bằng không. Nếu bộ đệm nguồn không đủ lớn thì ValueError sẽ được nâng lên.

Tăng một auditing event ctypes.cdata/buffer với các đối số pointer, size, offset.

from_address(address)

Phương thức này trả về một thể hiện kiểu ctypes sử dụng bộ nhớ được chỉ định bởi address, bộ nhớ này phải là số nguyên.

Phương thức này và các phương thức khác gọi gián tiếp phương thức này sẽ tạo ra một auditing event ctypes.cdata với đối số address.

from_param(obj)

Phương pháp này điều chỉnh obj thành loại ctypes. Nó được gọi với đối tượng thực tế được sử dụng trong lệnh gọi hàm ngoại khi loại này có trong bộ dữ liệu argtypes của hàm ngoại; nó phải trả về một đối tượng có thể được sử dụng làm tham số gọi hàm.

Tất cả các kiểu dữ liệu ctypes đều có cách triển khai mặc định của phương thức lớp này thường trả về obj nếu đó là một phiên bản của kiểu đó. Một số loại cũng chấp nhận các đối tượng khác.

in_dll(library, name)

Phương thức này trả về một thể hiện kiểu ctypes được xuất bởi thư viện dùng chung. name là tên của biểu tượng xuất dữ liệu, library là thư viện chia sẻ được tải.

Các biến lớp phổ biến của kiểu dữ liệu ctypes:

__pointer_type__

Kiểu con trỏ được tạo bằng cách gọi POINTER() cho kiểu dữ liệu ctypes tương ứng. Nếu loại con trỏ chưa được tạo thì thuộc tính đó sẽ bị thiếu.

Added in version 3.14.

Các biến thể hiện phổ biến của kiểu dữ liệu ctypes:

_b_base_

Đôi khi các phiên bản dữ liệu ctypes không sở hữu khối bộ nhớ mà chúng chứa, thay vào đó chúng chia sẻ một phần khối bộ nhớ của một đối tượng cơ sở. Thành viên chỉ đọc _b_base_ là đối tượng ctypes gốc sở hữu khối bộ nhớ.

_b_needsfree_

Biến chỉ đọc này là đúng khi phiên bản dữ liệu ctypes đã tự phân bổ khối bộ nhớ, nếu không thì sai.

_objects

Thành viên này là None hoặc một từ điển chứa các đối tượng Python cần được duy trì để nội dung khối bộ nhớ được giữ hợp lệ. Đối tượng này chỉ được hiển thị để gỡ lỗi; không bao giờ sửa đổi nội dung của từ điển này.

Các kiểu dữ liệu cơ bản

class ctypes._SimpleCData

Lớp không công khai này là lớp cơ sở của tất cả các kiểu dữ liệu ctypes cơ bản. Nó được đề cập ở đây vì nó chứa các thuộc tính chung của các kiểu dữ liệu ctypes cơ bản. _SimpleCData là lớp con của _CData nên nó kế thừa các phương thức và thuộc tính của chúng. Các kiểu dữ liệu ctypes không có và không chứa con trỏ giờ đây có thể được chọn.

Các trường hợp có một thuộc tính duy nhất:

value

Thuộc tính này chứa giá trị thực tế của thể hiện. Đối với các kiểu số nguyên và con trỏ, nó là một số nguyên, đối với các kiểu ký tự, nó là một đối tượng hoặc chuỗi byte ký tự đơn, đối với các kiểu con trỏ ký tự, nó là một đối tượng hoặc chuỗi byte Python.

Khi thuộc tính value được truy xuất từ phiên bản ctypes, thông thường một đối tượng mới sẽ được trả về mỗi lần. ctypes not thực hiện trả về đối tượng ban đầu, luôn có một đối tượng mới được xây dựng. Điều này cũng đúng với tất cả các phiên bản đối tượng ctypes khác.

Mỗi lớp con có một thuộc tính lớp:

_type_

Thuộc tính lớp chứa mã loại nội bộ, dưới dạng chuỗi ký tự đơn. Xem Các kiểu dữ liệu cơ bản để biết tóm tắt.

Các loại được đánh dấu * trong phần tóm tắt có thể (hoặc luôn luôn là) bí danh của một lớp con _SimpleCData khác và sẽ không nhất thiết phải sử dụng mã loại được liệt kê. Ví dụ: nếu các loại long, long longtime_t C của nền tảng giống nhau thì c_long, c_longlongc_time_t đều đề cập đến một lớp duy nhất, c_long, có mã _type_'l'. Mã 'L' sẽ không được sử dụng.

Xem thêm

Các mô-đun arraystruct, cũng như các mô-đun của bên thứ ba như numpy, sử dụng mã loại tương tự -- nhưng hơi khác một chút.

Các kiểu dữ liệu cơ bản, khi được trả về dưới dạng kết quả lệnh gọi hàm ngoại, hoặc, ví dụ, bằng cách truy xuất các thành viên trường cấu trúc hoặc các mục mảng, sẽ được chuyển đổi rõ ràng thành các kiểu Python gốc. Nói cách khác, nếu một hàm ngoại có restypec_char_p, bạn sẽ luôn nhận được một đối tượng byte Python, not một phiên bản c_char_p.

Các lớp con của kiểu dữ liệu cơ bản not kế thừa hành vi này. Vì vậy, nếu hàm ngoại restype là lớp con của c_void_p, bạn sẽ nhận được một phiên bản của lớp con này từ lệnh gọi hàm. Tất nhiên, bạn có thể lấy giá trị của con trỏ bằng cách truy cập thuộc tính value.

Đây là các kiểu dữ liệu ctypes cơ bản:

class ctypes.c_byte

Biểu thị kiểu dữ liệu C signed char và diễn giải giá trị dưới dạng số nguyên nhỏ. Hàm tạo chấp nhận một bộ khởi tạo số nguyên tùy chọn; không có kiểm tra tràn được thực hiện.

class ctypes.c_char

Đại diện cho kiểu dữ liệu C char và diễn giải giá trị dưới dạng một ký tự đơn. Hàm tạo chấp nhận trình khởi tạo chuỗi tùy chọn, độ dài của chuỗi phải chính xác là một ký tự.

class ctypes.c_char_p

Biểu thị kiểu dữ liệu C char* khi nó trỏ đến một chuỗi kết thúc bằng 0. Đối với một con trỏ ký tự chung cũng có thể trỏ đến dữ liệu nhị phân, phải sử dụng POINTER(c_char). Hàm tạo chấp nhận một địa chỉ số nguyên hoặc một đối tượng byte.

class ctypes.c_double

Đại diện cho kiểu dữ liệu C double. Hàm tạo chấp nhận một bộ khởi tạo float tùy chọn.

class ctypes.c_longdouble

Đại diện cho kiểu dữ liệu C long double. Hàm tạo chấp nhận một bộ khởi tạo float tùy chọn. Trên nền tảng có sizeof(long double) == sizeof(double), nó là bí danh của c_double.

class ctypes.c_float

Đại diện cho kiểu dữ liệu C float. Hàm tạo chấp nhận một bộ khởi tạo float tùy chọn.

class ctypes.c_double_complex

Đại diện cho kiểu dữ liệu C double complex, nếu có. Hàm tạo chấp nhận trình khởi tạo complex tùy chọn.

Added in version 3.14.

class ctypes.c_float_complex

Đại diện cho kiểu dữ liệu C float complex, nếu có. Hàm tạo chấp nhận trình khởi tạo complex tùy chọn.

Added in version 3.14.

class ctypes.c_longdouble_complex

Đại diện cho kiểu dữ liệu C long double complex, nếu có. Hàm tạo chấp nhận trình khởi tạo complex tùy chọn.

Added in version 3.14.

class ctypes.c_int

Đại diện cho kiểu dữ liệu C signed int. Hàm tạo chấp nhận một bộ khởi tạo số nguyên tùy chọn; không có kiểm tra tràn được thực hiện. Trên nền tảng có sizeof(int) == sizeof(long), nó là bí danh của c_long.

class ctypes.c_int8

Đại diện cho kiểu dữ liệu signed int 8 bit C. Nó là bí danh của c_byte.

class ctypes.c_int16

Đại diện cho kiểu dữ liệu signed int C 16-bit. Thường là bí danh cho c_short.

class ctypes.c_int32

Đại diện cho kiểu dữ liệu signed int C 32-bit. Thường là bí danh cho c_int.

class ctypes.c_int64

Đại diện cho kiểu dữ liệu signed int C 64-bit. Thường là bí danh cho c_longlong.

class ctypes.c_long

Đại diện cho kiểu dữ liệu C signed long. Hàm tạo chấp nhận một bộ khởi tạo số nguyên tùy chọn; không có kiểm tra tràn được thực hiện.

class ctypes.c_longlong

Đại diện cho kiểu dữ liệu C signed long long. Hàm tạo chấp nhận một bộ khởi tạo số nguyên tùy chọn; không có kiểm tra tràn được thực hiện. Trên nền tảng có sizeof(long long) == sizeof(long), nó là bí danh của c_long.

class ctypes.c_short

Đại diện cho kiểu dữ liệu C signed short. Hàm tạo chấp nhận một bộ khởi tạo số nguyên tùy chọn; không có kiểm tra tràn được thực hiện.

class ctypes.c_size_t

Đại diện cho kiểu dữ liệu C size_t. Thường là bí danh cho một loại số nguyên không dấu khác.

class ctypes.c_ssize_t

Đại diện cho kiểu dữ liệu Py_ssize_t. Đây là phiên bản có chữ ký của size_t; tức là loại POSIX ssize_t. Thường là bí danh cho một loại số nguyên khác.

Added in version 3.2.

class ctypes.c_time_t

Đại diện cho kiểu dữ liệu C time_t. Thường là bí danh cho một loại số nguyên khác.

Added in version 3.12.

class ctypes.c_ubyte

Đại diện cho kiểu dữ liệu C unsigned char, nó diễn giải giá trị dưới dạng số nguyên nhỏ. Hàm tạo chấp nhận một bộ khởi tạo số nguyên tùy chọn; không có kiểm tra tràn được thực hiện.

class ctypes.c_uint

Đại diện cho kiểu dữ liệu C unsigned int. Hàm tạo chấp nhận một bộ khởi tạo số nguyên tùy chọn; không có kiểm tra tràn được thực hiện. Trên nền tảng có sizeof(int) == sizeof(long), nó là bí danh của c_ulong.

class ctypes.c_uint8

Đại diện cho kiểu dữ liệu unsigned int 8 bit C. Nó là bí danh của c_ubyte.

class ctypes.c_uint16

Đại diện cho kiểu dữ liệu unsigned int C 16-bit. Thường là bí danh cho c_ushort.

class ctypes.c_uint32

Đại diện cho kiểu dữ liệu unsigned int C 32-bit. Thường là bí danh cho c_uint.

class ctypes.c_uint64

Đại diện cho kiểu dữ liệu unsigned int C 64-bit. Thường là bí danh cho c_ulonglong.

class ctypes.c_ulong

Đại diện cho kiểu dữ liệu C unsigned long. Hàm tạo chấp nhận một bộ khởi tạo số nguyên tùy chọn; không có kiểm tra tràn được thực hiện.

class ctypes.c_ulonglong

Đại diện cho kiểu dữ liệu C unsigned long long. Hàm tạo chấp nhận một bộ khởi tạo số nguyên tùy chọn; không có kiểm tra tràn được thực hiện. Trên nền tảng có sizeof(long long) == sizeof(long), nó là bí danh của c_long.

class ctypes.c_ushort

Đại diện cho kiểu dữ liệu C unsigned short. Hàm tạo chấp nhận một bộ khởi tạo số nguyên tùy chọn; không có kiểm tra tràn được thực hiện.

class ctypes.c_void_p

Đại diện cho loại C void*. Giá trị được biểu diễn dưới dạng số nguyên. Hàm tạo chấp nhận một bộ khởi tạo số nguyên tùy chọn.

class ctypes.c_wchar

Đại diện cho kiểu dữ liệu C wchar_t và diễn giải giá trị dưới dạng một chuỗi unicode ký tự đơn. Hàm tạo chấp nhận trình khởi tạo chuỗi tùy chọn, độ dài của chuỗi phải chính xác là một ký tự.

class ctypes.c_wchar_p

Đại diện cho kiểu dữ liệu C wchar_t*, phải là một con trỏ tới chuỗi ký tự rộng có đầu cuối bằng 0. Hàm tạo chấp nhận một địa chỉ số nguyên hoặc một chuỗi.

class ctypes.c_bool

Đại diện cho kiểu dữ liệu C bool (chính xác hơn là _Bool từ C99). Giá trị của nó có thể là True hoặc False và hàm tạo chấp nhận bất kỳ đối tượng nào có giá trị thật.

class ctypes.HRESULT

Đại diện cho giá trị HRESULT, chứa thông tin thành công hoặc lỗi cho lệnh gọi hàm hoặc phương thức.

sẵn có: Windows

class ctypes.py_object

Đại diện cho kiểu dữ liệu C PyObject*. Gọi cái này mà không có đối số sẽ tạo ra một con trỏ NULL PyObject*.

Thay đổi trong phiên bản 3.14: py_object bây giờ là generic type.

Mô-đun ctypes.wintypes cung cấp khá nhiều loại dữ liệu cụ thể khác của Windows, ví dụ HWND, WPARAM, VARIANT_BOOL hoặc DWORD. Một số cấu trúc hữu ích như MSG hoặc RECT cũng được xác định.

Các kiểu dữ liệu có cấu trúc

class ctypes.Union(*args, **kw)

Lớp cơ sở trừu tượng cho các kết hợp theo thứ tự byte gốc.

Các công đoàn chia sẻ các thuộc tính và hành vi chung với các cơ cấu; xem tài liệu Structure để biết chi tiết.

class ctypes.BigEndianUnion(*args, **kw)

Lớp cơ sở trừu tượng cho các kết hợp theo thứ tự byte big endian.

Added in version 3.11.

class ctypes.LittleEndianUnion(*args, **kw)

Lớp cơ sở trừu tượng cho các kết hợp theo thứ tự byte little endian.

Added in version 3.11.

class ctypes.BigEndianStructure(*args, **kw)

Lớp cơ sở trừu tượng cho các cấu trúc theo thứ tự byte big endian.

class ctypes.LittleEndianStructure(*args, **kw)

Lớp cơ sở trừu tượng cho các cấu trúc theo thứ tự byte little endian.

Các cấu trúc và liên kết có thứ tự byte không gốc không thể chứa các trường loại con trỏ hoặc bất kỳ loại dữ liệu nào khác chứa các trường loại con trỏ.

class ctypes.Structure(*args, **kw)

Lớp cơ sở trừu tượng cho các cấu trúc theo thứ tự byte native.

Cấu trúc cụ thể và các kiểu kết hợp phải được tạo bằng cách phân lớp con một trong các kiểu này và ít nhất xác định một biến lớp _fields_. ctypes sẽ tạo descriptors cho phép đọc và ghi các trường bằng cách truy cập thuộc tính trực tiếp. Đây là những

_fields_

Một trình tự xác định các trường cấu trúc. Các mục phải là 2 bộ hoặc 3 bộ. Mục đầu tiên là tên của trường, mục thứ hai chỉ định loại trường; nó có thể là bất kỳ kiểu dữ liệu ctypes nào.

Đối với các trường loại số nguyên như c_int, có thể cung cấp mục tùy chọn thứ ba. Nó phải là một số nguyên dương nhỏ xác định độ rộng bit của trường.

Tên trường phải là duy nhất trong một cấu trúc hoặc liên kết. Điều này không được kiểm tra, chỉ có thể truy cập một trường khi tên được lặp lại.

Có thể định nghĩa biến lớp _fields_ after câu lệnh lớp xác định lớp con Cấu trúc, điều này cho phép tạo các kiểu dữ liệu tham chiếu trực tiếp hoặc gián tiếp đến chính chúng:

Danh sách lớp (Cấu trúc):
    vượt qua
List._fields_ = [("pnext", POINTER(List)),
                 ...
                ]

Biến lớp _fields_ chỉ có thể được đặt một lần. Các bài tập sau sẽ tăng AttributeError.

Ngoài ra, biến lớp _fields_ phải được xác định trước khi cấu trúc hoặc loại kết hợp được sử dụng lần đầu tiên: một phiên bản hoặc lớp con được tạo, sizeof() được gọi trên đó, v.v. Các nhiệm vụ sau này cho _fields_ sẽ tăng AttributeError. Nếu _fields_ chưa được đặt trước khi sử dụng, cấu trúc hoặc liên kết sẽ không có trường riêng, như thể _fields_ trống.

Các lớp con của các kiểu cấu trúc kế thừa các trường của lớp cơ sở cộng với _fields_ được xác định trong lớp con, nếu có.

_pack_

Một số nguyên nhỏ tùy chọn cho phép ghi đè việc căn chỉnh các trường cấu trúc trong phiên bản.

Điều này chỉ được triển khai cho bố cục bộ nhớ tương thích với MSVC (xem _layout_).

Đặt _pack_ thành 0 cũng giống như không cài đặt gì cả. Nếu không, giá trị phải là lũy thừa dương của hai. Hiệu ứng này tương đương với #pragma pack(N) trong C, ngoại trừ ctypes có thể cho phép n lớn hơn mức mà trình biên dịch chấp nhận.

_pack_ phải được xác định khi _fields_ được gán, nếu không nó sẽ không có hiệu lực.

Không được dùng nữa kể từ phiên bản 3.14, sẽ bị xóa trong phiên bản 3.19: Vì lý do lịch sử, nếu _pack_ khác 0 thì bố cục tương thích với MSVC sẽ được sử dụng theo mặc định. Trên các nền tảng không phải Windows, mặc định này không được dùng nữa và dự kiến ​​sẽ trở thành lỗi trong Python 3.19. Nếu có ý định, hãy đặt _layout_ thành 'ms' một cách rõ ràng.

_align_

Một số nguyên nhỏ tùy chọn cho phép tăng sự liên kết của cấu trúc khi được đóng gói hoặc giải nén vào/từ bộ nhớ.

Giá trị không được âm. Hiệu ứng này tương đương với __attribute__((aligned(N))) trên GCC hoặc #pragma align(N) trên MSVC, ngoại trừ ctypes có thể cho phép các giá trị mà trình biên dịch sẽ từ chối.

_align_ chỉ có thể increase các yêu cầu căn chỉnh của cấu trúc. Đặt nó thành 0 hoặc 1 không có hiệu lực.

Việc sử dụng các giá trị không phải là lũy thừa của 2 không được khuyến khích và có thể dẫn đến hành vi đáng ngạc nhiên.

_align_ phải được xác định khi _fields_ được gán, nếu không nó sẽ không có hiệu lực.

Added in version 3.13.

_layout_

Một chuỗi tùy chọn đặt tên cho bố cục struct/union. Hiện tại nó có thể được đặt thành:

  • "ms": bố cục được trình biên dịch Microsoft sử dụng (MSVC). Trên GCC và Clang, bố cục này có thể được chọn bằng __attribute__((ms_struct)).

  • "gcc-sysv": bố cục được GCC sử dụng với mô hình dữ liệu System V hoặc “giống SysV”, như được sử dụng trên Linux và macOS. Với cách bố trí này, _pack_ phải được bỏ đặt hoặc bằng 0.

Nếu không được đặt rõ ràng, ctypes sẽ sử dụng giá trị mặc định phù hợp với quy ước của nền tảng. Mặc định này có thể thay đổi trong các bản phát hành Python trong tương lai (ví dụ: khi một nền tảng mới nhận được hỗ trợ chính thức hoặc khi tìm thấy sự khác biệt giữa các nền tảng tương tự). Hiện tại mặc định sẽ là:

  • Trên Windows: "ms"

  • Khi _pack_ được chỉ định: "ms". (Điều này không được dùng nữa; xem tài liệu _pack_.)

  • Mặt khác: "gcc-sysv"

_layout_ phải được xác định khi _fields_ được gán, nếu không nó sẽ không có hiệu lực.

Added in version 3.14.

_anonymous_

Một chuỗi tùy chọn liệt kê tên của các trường không tên (ẩn danh). _anonymous_ phải được xác định trước khi _fields_ được gán, nếu không nó sẽ không có hiệu lực.

Các trường được liệt kê trong biến này phải là các trường loại cấu trúc hoặc kết hợp. ctypes sẽ tạo các bộ mô tả theo kiểu cấu trúc cho phép truy cập trực tiếp vào các trường lồng nhau mà không cần tạo cấu trúc hoặc trường kết hợp.

Đây là một loại ví dụ (Windows):

lớp _U(Liên minh):
    _fields_ = [("lptdesc", POINTER(TYPEDESC)),
                ("lpadesc", POINTER(ARRAYDESC)),
                ("hreftype", HREFTYPE)]

lớp TYPEDESC (Cấu trúc):
    _anonymous_ = ("u",)
    _fields_ = [("u", _U),
                ("vt", VARTYPE)]

Cấu trúc TYPEDESC mô tả kiểu dữ liệu COM, trường vt chỉ định một trong các trường hợp hợp lệ. Vì trường u được xác định là trường ẩn danh nên giờ đây có thể truy cập trực tiếp vào các thành viên từ phiên bản TYPEDESC. td.lptdesctd.u.lptdesc tương đương nhau, nhưng cái trước nhanh hơn vì nó không cần tạo một phiên bản hợp nhất tạm thời:

td = TYPEDESC()
td.vt = VT_PTR
td.lptdesc = POINTER(some_type)
td.u.lptdesc = POINTER(some_type)

Có thể định nghĩa các lớp con của cấu trúc, chúng kế thừa các trường của lớp cơ sở. Nếu định nghĩa lớp con có một biến _fields_ riêng biệt thì các trường được chỉ định trong phần này sẽ được thêm vào các trường của lớp cơ sở.

Các hàm tạo cấu trúc và hợp nhất chấp nhận cả đối số vị trí và từ khóa. Đối số vị trí được sử dụng để khởi tạo các trường thành viên theo thứ tự giống như chúng xuất hiện trong _fields_. Các đối số từ khóa trong hàm tạo được hiểu là các phép gán thuộc tính, vì vậy chúng sẽ khởi tạo _fields_ với cùng tên hoặc tạo thuộc tính mới cho các tên không có trong _fields_.

class ctypes.CField(*args, **kw)

Bộ mô tả cho các trường của StructureUnion. Ví dụ:

>>> Màu lớp (Cấu trúc):
... _fields_ = (
... ('đỏ', c_uint8),
...('xanh', c_uint8),
...         ('blue', c_uint8),
... ('dữ dội', c_bool, 1),
... ('nhấp nháy', c_bool, 1),
... )
...
>>> Màu.red
<ctypes.CField 'red' type=c_ubyte, ofs=0, size=1>
>>> Màu.green.type
<lớp 'ctypes.c_ubyte'>
>>> Color.blue.byte_offset
2
>>> Màu sắc. mãnh liệt
<ctypes.CField 'dữ dội' type=c_bool, ofs=3, bit_size=1, bit_offset=0>
>>> Màu.blinking.bit_offset
1

Tất cả các thuộc tính là chỉ đọc.

Các đối tượng CField được tạo thông qua _fields_; không khởi tạo lớp một cách trực tiếp.

Added in version 3.14: Trước đây, bộ mô tả chỉ có thuộc tính offsetsize và biểu diễn chuỗi có thể đọc được; lớp CField không có sẵn trực tiếp.

name

Tên của trường, dưới dạng một chuỗi.

type

Loại trường, dưới dạng ctypes class.

offset
byte_offset

Độ lệch của trường, tính bằng byte.

Đối với các trường bit, đây là phần bù của storage unit được căn chỉnh theo byte cơ bản; xem bit_offset.

byte_size

Kích thước của trường, tính bằng byte.

Đối với các trường bit, đây là kích thước của storage unit cơ bản. Thông thường, nó có cùng kích thước với loại của bitfield.

size

Đối với các trường không phải bit, tương đương với byte_size.

Đối với các trường bit, giá trị này chứa giá trị đóng gói bit tương thích ngược kết hợp bit_sizebit_offset. Thay vào đó, hãy ưu tiên sử dụng các thuộc tính rõ ràng.

is_bitfield

Đúng nếu đây là một bitfield.

bit_offset
bit_size

Vị trí của một trường bit trong storage unit của nó, nghĩa là trong byte_size byte bộ nhớ bắt đầu từ byte_offset.

Để nhận giá trị của trường, hãy đọc đơn vị lưu trữ dưới dạng số nguyên, shift left x bit_offset và lấy các bit có trọng số nhỏ nhất của bit_size.

Đối với các trường không phải bit, bit_offset bằng 0 và bit_size bằng byte_size * 8.

is_anonymous

Đúng nếu trường này ẩn danh, nghĩa là nó chứa các trường con lồng nhau cần được hợp nhất thành một cấu trúc hoặc liên kết chứa.

Mảng và con trỏ

class ctypes.Array(*args)

Lớp cơ sở trừu tượng cho mảng.

Cách được khuyến nghị để tạo các kiểu mảng cụ thể là nhân bất kỳ kiểu dữ liệu ctypes nào với một số nguyên không âm. Ngoài ra, bạn có thể phân lớp loại này và xác định các biến lớp _length__type_. Các phần tử mảng có thể được đọc và ghi bằng cách sử dụng các truy cập chỉ số và lát cắt tiêu chuẩn; đối với các lần đọc lát cắt, đối tượng kết quả chính là notArray.

_length_

Một số nguyên dương xác định số phần tử trong mảng. Các chỉ số đăng ký ngoài phạm vi sẽ dẫn đến IndexError. Sẽ được trả lại bởi len().

_type_

Xác định kiểu của từng phần tử trong mảng.

Hàm tạo của lớp con mảng chấp nhận các đối số vị trí, được sử dụng để khởi tạo các phần tử theo thứ tự.

ctypes.ARRAY(type, length)

Tạo một mảng. Tương đương với type * length, trong đó type là kiểu dữ liệu ctypeslength là số nguyên.

Hàm này là soft deprecated thiên về phép nhân. Không có kế hoạch để loại bỏ nó.

class ctypes._Pointer

Lớp cơ sở riêng tư, trừu tượng cho con trỏ.

Các kiểu con trỏ cụ thể được tạo bằng cách gọi POINTER() với kiểu sẽ được trỏ tới; việc này được thực hiện tự động bởi pointer().

Nếu một con trỏ trỏ đến một mảng, các phần tử của nó có thể được đọc và ghi bằng cách sử dụng các truy cập chỉ số dưới và lát cắt tiêu chuẩn. Đối tượng con trỏ không có kích thước nên len() sẽ tăng TypeError. Các chỉ số âm sẽ đọc từ bộ nhớ before con trỏ (như trong C) và các chỉ số ngoài phạm vi có thể sẽ gặp sự cố do vi phạm quyền truy cập (nếu bạn may mắn).

_type_

Chỉ định loại được trỏ đến.

contents

Trả về đối tượng mà con trỏ trỏ tới. Việc gán cho thuộc tính này sẽ thay đổi con trỏ để trỏ tới đối tượng được gán.

Ngoại lệ

exception ctypes.ArgumentError

Ngoại lệ này xuất hiện khi lệnh gọi hàm ngoại không thể chuyển đổi một trong các đối số được truyền.

exception ctypes.COMError(hresult, text, details)

Ngoại lệ này được đưa ra khi lệnh gọi phương thức COM không thành công.

hresult

Giá trị số nguyên đại diện cho mã lỗi.

text

Thông báo lỗi.

details

5 bộ (descr, source, helpfile, helpcontext, progid).

descr là mô tả bằng văn bản. sourceProgID phụ thuộc vào ngôn ngữ dành cho lớp hoặc ứng dụng đã gây ra lỗi. helpfile là đường dẫn của file trợ giúp. helpcontext là mã định danh ngữ cảnh trợ giúp. progidProgID của giao diện xác định lỗi.

sẵn có: Windows

Added in version 3.14.