“Python/docs/3.9/library/ctypes”的版本间差异
(autoload) |
小 (Page commit) |
||
第1行: | 第1行: | ||
+ | {{DISPLAYTITLE:ctypes — Python 的外部函数库 — Python 文档}} | ||
<div id="module-ctypes" class="section"> | <div id="module-ctypes" class="section"> | ||
<span id="ctypes-a-foreign-function-library-for-python"></span> | <span id="ctypes-a-foreign-function-library-for-python"></span> | ||
− | = | + | = ctypes — Python 的外部函数库 = |
− | [[#module-ctypes| | + | |
− | + | ----- | |
− | + | ||
+ | [[#module-ctypes|ctypes]] 是 Python 的外部函数库。 它提供与 C 兼容的数据类型,并允许调用 DLL 或共享库中的函数。 它可用于将这些库包装在纯 Python 中。 | ||
<div id="ctypes-tutorial" class="section"> | <div id="ctypes-tutorial" class="section"> | ||
<span id="ctypes-ctypes-tutorial"></span> | <span id="ctypes-ctypes-tutorial"></span> | ||
− | == | + | == ctypes教程 == |
− | + | 注意:本教程中的代码示例使用 [[../doctest#module-doctest|doctest]] 来确保它们确实有效。 由于某些代码示例在 Linux、Windows 或 macOS 下的行为不同,因此它们在注释中包含 doctest 指令。 | |
− | |||
− | |||
− | + | 注意:一些代码示例引用了 ctypes [[#ctypes.c_int|c_int]] 类型。 在 <code>sizeof(long) == sizeof(int)</code> 它是 [[#ctypes.c_long|c_long]] 的别名的平台上。 因此,如果您期望 [[#ctypes.c_int|c_int]] 打印 [[#ctypes.c_long|c_long]],您不应该感到困惑——它们实际上是相同的类型。 | |
− | |||
− | |||
− | [[#ctypes. | ||
<div id="loading-dynamic-link-libraries" class="section"> | <div id="loading-dynamic-link-libraries" class="section"> | ||
<span id="ctypes-loading-dynamic-link-libraries"></span> | <span id="ctypes-loading-dynamic-link-libraries"></span> | ||
− | === | + | === 加载动态链接库 === |
− | [[#module-ctypes| | + | [[#module-ctypes|ctypes]] 导出 ''cdll'',在 Windows ''windll'' 和 ''oledll'' 对象上,用于加载动态链接库。 |
− | |||
− | + | 您可以通过将库作为这些对象的属性进行访问来加载库。 ''cdll'' 加载使用标准 <code>cdecl</code> 调用约定导出函数的库,而 ''windll'' 库使用 <code>stdcall</code> 调用约定调用函数。 ''oledll'' 也使用 <code>stdcall</code> 调用约定,并假设函数返回 Windows <code>HRESULT</code> 错误代码。 错误代码用于在函数调用失败时自动引发 [[../exceptions#OSError|OSError]] 异常。 | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
<div class="versionchanged"> | <div class="versionchanged"> | ||
− | <span class="versionmodified changed"> | + | <span class="versionmodified changed">3.3 版更改:</span>Windows 错误用于引发 [[../exceptions#WindowsError|WindowsError]],现在是 [[../exceptions#OSError|OSError]] 的别名。 |
− | |||
</div> | </div> | ||
− | + | 下面是一些适用于 Windows 的示例。 请注意,<code>msvcrt</code> 是包含大多数标准 C 函数的 MS 标准 C 库,并使用 cdecl 调用约定: | |
− | |||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第53行: | 第40行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> from ctypes import * |
− | + | >>> print(windll.kernel32) | |
− | + | <WinDLL 'kernel32', handle ... at ...> | |
− | + | >>> print(cdll.msvcrt) | |
− | + | <CDLL 'msvcrt', handle ... at ...> | |
− | + | >>> libc = cdll.msvcrt | |
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | Windows | + | Windows 会自动附加通常的 <code>.dll</code> 文件后缀。 |
<div class="admonition note"> | <div class="admonition note"> | ||
− | + | 笔记 | |
− | + | 通过 <code>cdll.msvcrt</code> 访问标准 C 库将使用该库的过时版本,该版本可能与 Python 使用的版本不兼容。 在可能的情况下,使用本机 Python 功能,或者导入并使用 <code>msvcrt</code> 模块。 | |
− | |||
− | |||
− | |||
</div> | </div> | ||
− | + | 在 Linux 上,需要指定文件名 ''包括'' 扩展名才能加载库,因此不能使用属性访问来加载库。 应该使用 dll 加载器的 <code>LoadLibrary()</code> 方法,或者您应该通过调用构造函数创建 CDLL 的实例来加载库: | |
− | |||
− | <code>LoadLibrary()</code> | ||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第86行: | 第67行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> cdll.LoadLibrary("libc.so.6") |
− | + | <CDLL 'libc.so.6', handle ... at ...> | |
− | + | >>> libc = CDLL("libc.so.6") | |
− | + | >>> libc | |
− | + | <CDLL 'libc.so.6', handle ... at ...> | |
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
第101行: | 第82行: | ||
<span id="ctypes-accessing-functions-from-loaded-dlls"></span> | <span id="ctypes-accessing-functions-from-loaded-dlls"></span> | ||
− | === | + | === 从加载的 dll 访问函数 === |
− | + | 函数作为 dll 对象的属性被访问: | |
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第109行: | 第90行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> from ctypes import * |
− | + | >>> libc.printf | |
− | + | <_FuncPtr object at 0x...> | |
− | + | >>> print(windll.kernel32.GetModuleHandleA) | |
− | + | <_FuncPtr object at 0x...> | |
− | + | >>> print(windll.kernel32.MyOwnFunction) | |
Traceback (most recent call last): | Traceback (most recent call last): | ||
− | File | + | File "<stdin>", line 1, in <module> |
− | File | + | File "ctypes.py", line 239, in __getattr__ |
func = _StdcallFuncPtr(name, self) | func = _StdcallFuncPtr(name, self) | ||
AttributeError: function 'MyOwnFunction' not found | AttributeError: function 'MyOwnFunction' not found | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 请注意,像 <code>kernel32</code> 和 <code>user32</code> 这样的 win32 系统 dll 通常会导出函数的 ANSI 和 UNICODE 版本。 UNICODE 版本在导出时在名称后附加了 <code>W</code>,而 ANSI 版本在导出时在名称后附加了 <code>A</code>。 win32 <code>GetModuleHandle</code> 函数返回给定模块名称的 ''模块句柄'' ,具有以下 C 原型,并使用宏将其中之一公开为 <code>GetModuleHandle</code>取决于是否定义了 UNICODE: | |
− | |||
− | |||
− | |||
− | '' | ||
− | |||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第137行: | 第112行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">/* ANSI version */ |
HMODULE GetModuleHandleA(LPCSTR lpModuleName); | HMODULE GetModuleHandleA(LPCSTR lpModuleName); | ||
/* UNICODE version */ | /* UNICODE version */ | ||
− | HMODULE GetModuleHandleW(LPCWSTR lpModuleName);</ | + | HMODULE GetModuleHandleW(LPCWSTR lpModuleName);</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | ''windll'' | + | ''windll'' 不会尝试通过魔法选择其中之一,您必须通过显式指定 <code>GetModuleHandleA</code> 或 <code>GetModuleHandleW</code> 来访问您需要的版本,然后用字节或字符串调用它对象分别。 |
− | |||
− | |||
− | + | 有时,dll 导出的函数名称不是有效的 Python 标识符,例如 <code>"??2@YAPAXI@Z"</code>。 在这种情况下,您必须使用 [[../functions#getattr|getattr()]] 来检索函数: | |
− | |||
− | [[../functions#getattr| | ||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第157行: | 第128行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> getattr(cdll.msvcrt, "??2@YAPAXI@Z") |
− | + | <_FuncPtr object at 0x...> | |
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 在 Windows 上,一些 dll 不是按名称而是按顺序导出函数。 可以通过使用序号索引 dll 对象来访问这些函数: | |
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第171行: | 第141行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> cdll.kernel32[1] |
− | + | <_FuncPtr object at 0x...> | |
− | + | >>> cdll.kernel32[0] | |
Traceback (most recent call last): | Traceback (most recent call last): | ||
− | File | + | File "<stdin>", line 1, in <module> |
− | File | + | File "ctypes.py", line 310, in __getitem__ |
func = _StdcallFuncPtr(name, self) | func = _StdcallFuncPtr(name, self) | ||
AttributeError: function ordinal 0 not found | AttributeError: function ordinal 0 not found | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
第189行: | 第159行: | ||
<span id="ctypes-calling-functions"></span> | <span id="ctypes-calling-functions"></span> | ||
− | === | + | === 调用函数 === |
− | + | 您可以像调用任何其他 Python 可调用函数一样调用这些函数。 此示例使用 <code>time()</code> 函数,该函数返回自 Unix 纪元以来以秒为单位的系统时间,以及 <code>GetModuleHandleA()</code> 函数,该函数返回一个 win32 模块句柄。 | |
− | |||
− | |||
− | |||
− | + | 此示例使用 <code>NULL</code> 指针调用这两个函数(<code>None</code> 应用作 <code>NULL</code> 指针): | |
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第203行: | 第169行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> print(libc.time(None)) |
1150640792 | 1150640792 | ||
− | + | >>> print(hex(windll.kernel32.GetModuleHandleA(None))) | |
0x1d000000 | 0x1d000000 | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 当您使用 <code>cdecl</code> 调用约定调用 <code>stdcall</code> 函数时,会引发 [[../exceptions#ValueError|ValueError]],反之亦然: | |
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第219行: | 第184行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> cdll.kernel32.GetModuleHandleA(None) |
Traceback (most recent call last): | Traceback (most recent call last): | ||
− | File | + | File "<stdin>", line 1, in <module> |
ValueError: Procedure probably called with not enough arguments (4 bytes missing) | ValueError: Procedure probably called with not enough arguments (4 bytes missing) | ||
− | + | >>> | |
− | + | >>> windll.msvcrt.printf(b"spam") | |
Traceback (most recent call last): | Traceback (most recent call last): | ||
− | File | + | File "<stdin>", line 1, in <module> |
ValueError: Procedure probably called with too many arguments (4 bytes in excess) | ValueError: Procedure probably called with too many arguments (4 bytes in excess) | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 要找出正确的调用约定,您必须查看 C 头文件或要调用的函数的文档。 | |
− | |||
− | + | 在 Windows 上,[[#module-ctypes|ctypes]] 使用 win32 结构化异常处理来防止在使用无效参数值调用函数时由于一般保护错误而导致崩溃: | |
− | |||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第245行: | 第207行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> windll.kernel32.GetModuleHandleA(32) |
Traceback (most recent call last): | Traceback (most recent call last): | ||
− | File | + | File "<stdin>", line 1, in <module> |
OSError: exception: access violation reading 0x00000020 | OSError: exception: access violation reading 0x00000020 | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 然而,有足够的方法可以使 Python 与 [[#module-ctypes|ctypes]] 崩溃,所以无论如何你都应该小心。 [[../faulthandler#module-faulthandler|faulthandler]] 模块有助于调试崩溃(例如 由错误的 C 库调用产生的分段错误)。 | |
− | |||
− | |||
− | |||
− | <code>None</code> | + | <code>None</code>、整数、字节对象和(unicode)字符串是唯一可以在这些函数调用中直接用作参数的原生 Python 对象。 <code>None</code> 作为 C <code>NULL</code> 指针传递,字节对象和字符串作为指向包含其数据的内存块的指针传递(<span class="xref c c-texpr">char*</span> 或 <span class="xref c c-texpr">wchar_t* </span>)。 Python 整数作为平台默认的 C <span class="xref c c-texpr">int</span> 类型传递,它们的值被屏蔽以适应 C 类型。 |
− | Python | ||
− | <code>None</code> | ||
− | |||
− | <span class="xref c c-texpr">wchar_t*</span> | ||
− | <span class="xref c c-texpr">int</span> | ||
− | + | 在继续使用其他参数类型调用函数之前,我们必须了解更多关于 [[#module-ctypes|ctypes]] 数据类型的信息。 | |
− | |||
第274行: | 第227行: | ||
<span id="ctypes-fundamental-data-types"></span> | <span id="ctypes-fundamental-data-types"></span> | ||
− | === | + | === 基本数据类型 === |
− | [[#module-ctypes| | + | [[#module-ctypes|ctypes]] 定义了一些原始的 C 兼容数据类型: |
{| | {| | ||
− | !width="24%"| ctypes | + | !width="24%"| ctypes 类型 |
− | !width="46%"| | + | !width="46%"| C型 |
− | !width="30%"| | + | !width="30%"| 蟒蛇型 |
|- | |- | ||
| [[#ctypes.c_bool|<code>c_bool</code>]] | | [[#ctypes.c_bool|<code>c_bool</code>]] | ||
− | | <span class="xref c c-texpr"> | + | | <span class="xref c c-texpr"><span class="kt">_布尔</span></span> |
− | | | + | | 布尔 (1) |
|- | |- | ||
| [[#ctypes.c_char|<code>c_char</code>]] | | [[#ctypes.c_char|<code>c_char</code>]] | ||
− | | <span class="xref c c-texpr"> | + | | <span class="xref c c-texpr"><span class="kt">字符</span></span> |
− | | 1 | + | | 1 个字符的字节对象 |
|- | |- | ||
| [[#ctypes.c_wchar|<code>c_wchar</code>]] | | [[#ctypes.c_wchar|<code>c_wchar</code>]] | ||
| <code>wchar_t</code> | | <code>wchar_t</code> | ||
− | | 1 | + | | 1 个字符的字符串 |
|- | |- | ||
| [[#ctypes.c_byte|<code>c_byte</code>]] | | [[#ctypes.c_byte|<code>c_byte</code>]] | ||
− | | <span class="xref c c-texpr"> | + | | <span class="xref c c-texpr"><span class="kt">字符</span></span> |
− | | | + | | 整数 |
|- | |- | ||
| [[#ctypes.c_ubyte|<code>c_ubyte</code>]] | | [[#ctypes.c_ubyte|<code>c_ubyte</code>]] | ||
− | | <span class="xref c c-texpr"> | + | | <span class="xref c c-texpr"><span class="kt">无符号的字符</span></span> |
− | | | + | | 整数 |
|- | |- | ||
| [[#ctypes.c_short|<code>c_short</code>]] | | [[#ctypes.c_short|<code>c_short</code>]] | ||
− | | <span class="xref c c-texpr"> | + | | <span class="xref c c-texpr"><span class="kt">短的</span></span> |
− | | | + | | 整数 |
|- | |- | ||
| [[#ctypes.c_ushort|<code>c_ushort</code>]] | | [[#ctypes.c_ushort|<code>c_ushort</code>]] | ||
− | | <span class="xref c c-texpr"> | + | | <span class="xref c c-texpr"><span class="kt">无符号短</span></span> |
− | | | + | | 整数 |
|- | |- | ||
| [[#ctypes.c_int|<code>c_int</code>]] | | [[#ctypes.c_int|<code>c_int</code>]] | ||
− | | <span class="xref c c-texpr"> | + | | <span class="xref c c-texpr"><span class="kt">整数</span></span> |
− | | | + | | 整数 |
|- | |- | ||
| [[#ctypes.c_uint|<code>c_uint</code>]] | | [[#ctypes.c_uint|<code>c_uint</code>]] | ||
− | | <span class="xref c c-texpr"> | + | | <span class="xref c c-texpr"><span class="kt">无符号整数</span></span> |
− | | | + | | 整数 |
|- | |- | ||
| [[#ctypes.c_long|<code>c_long</code>]] | | [[#ctypes.c_long|<code>c_long</code>]] | ||
− | | <span class="xref c c-texpr"> | + | | <span class="xref c c-texpr"><span class="kt">长</span></span> |
− | | | + | | 整数 |
|- | |- | ||
| [[#ctypes.c_ulong|<code>c_ulong</code>]] | | [[#ctypes.c_ulong|<code>c_ulong</code>]] | ||
− | | <span class="xref c c-texpr"> | + | | <span class="xref c c-texpr"><span class="kt">无符号长</span></span> |
− | | | + | | 整数 |
|- | |- | ||
| [[#ctypes.c_longlong|<code>c_longlong</code>]] | | [[#ctypes.c_longlong|<code>c_longlong</code>]] | ||
− | | <code>__int64</code> | + | | <code>__int64</code> 或 <span class="xref c c-texpr">长长</span> |
− | | | + | | 整数 |
|- | |- | ||
| [[#ctypes.c_ulonglong|<code>c_ulonglong</code>]] | | [[#ctypes.c_ulonglong|<code>c_ulonglong</code>]] | ||
− | | <span class="xref c c-texpr">unsigned __int64</span> | + | | <span class="xref c c-texpr">unsigned __int64</span> 或 <span class="xref c c-texpr">unsigned long long</span> |
− | <span class="xref c c-texpr">unsigned long long</span> | + | | 整数 |
− | | | ||
|- | |- | ||
| [[#ctypes.c_size_t|<code>c_size_t</code>]] | | [[#ctypes.c_size_t|<code>c_size_t</code>]] | ||
| <code>size_t</code> | | <code>size_t</code> | ||
− | | | + | | 整数 |
|- | |- | ||
| [[#ctypes.c_ssize_t|<code>c_ssize_t</code>]] | | [[#ctypes.c_ssize_t|<code>c_ssize_t</code>]] | ||
− | | <code>ssize_t</code> | + | | <code>ssize_t</code> 或 <code>Py_ssize_t</code> |
− | <code>Py_ssize_t</code> | + | | 整数 |
− | | | ||
|- | |- | ||
| [[#ctypes.c_float|<code>c_float</code>]] | | [[#ctypes.c_float|<code>c_float</code>]] | ||
− | | <span class="xref c c-texpr"> | + | | <span class="xref c c-texpr"><span class="kt">漂浮</span></span> |
− | | | + | | 漂浮 |
|- | |- | ||
| [[#ctypes.c_double|<code>c_double</code>]] | | [[#ctypes.c_double|<code>c_double</code>]] | ||
− | | <span class="xref c c-texpr"> | + | | <span class="xref c c-texpr"><span class="kt">双倍的</span></span> |
− | | | + | | 漂浮 |
|- | |- | ||
| [[#ctypes.c_longdouble|<code>c_longdouble</code>]] | | [[#ctypes.c_longdouble|<code>c_longdouble</code>]] | ||
− | | <span class="xref c c-texpr"> | + | | <span class="xref c c-texpr"><span class="kt">长双</span></span> |
− | | | + | | 漂浮 |
|- | |- | ||
| [[#ctypes.c_char_p|<code>c_char_p</code>]] | | [[#ctypes.c_char_p|<code>c_char_p</code>]] | ||
− | | <span class="xref c c-texpr">char*</span> | + | | <span class="xref c c-texpr">char*</span>(NUL 终止) |
− | | | + | | 字节对象或 <code>None</code> |
|- | |- | ||
| [[#ctypes.c_wchar_p|<code>c_wchar_p</code>]] | | [[#ctypes.c_wchar_p|<code>c_wchar_p</code>]] | ||
− | | <span class="xref c c-texpr">wchar_t*</span> | + | | <span class="xref c c-texpr">wchar_t*</span>(NUL 终止) |
− | | | + | | 字符串或 <code>None</code> |
|- | |- | ||
| [[#ctypes.c_void_p|<code>c_void_p</code>]] | | [[#ctypes.c_void_p|<code>c_void_p</code>]] | ||
− | | <span class="xref c c-texpr"> | + | | <span class="xref c c-texpr"><span class="kt">空白</span><span class="p">*</span></span> |
− | | | + | | 整数或 <code>None</code> |
|} | |} | ||
− | # | + | # 构造函数接受任何具有真值的对象。 |
− | + | 所有这些类型都可以通过使用正确类型和值的可选初始化程序调用它们来创建: | |
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第379行: | 第329行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> c_int() |
c_long(0) | c_long(0) | ||
− | + | >>> c_wchar_p("Hello, World") | |
c_wchar_p(140018365411392) | c_wchar_p(140018365411392) | ||
− | + | >>> c_ushort(-3) | |
c_ushort(65533) | c_ushort(65533) | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 由于这些类型是可变的,它们的值也可以在之后更改: | |
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第396行: | 第346行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> i = c_int(42) |
− | + | >>> print(i) | |
c_long(42) | c_long(42) | ||
− | + | >>> print(i.value) | |
42 | 42 | ||
− | + | >>> i.value = -99 | |
− | + | >>> print(i.value) | |
-99 | -99 | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 为指针类型 [[#ctypes.c_char_p|c_char_p]]、[[#ctypes.c_wchar_p|c_wchar_p]] 和 [[#ctypes.c_void_p|c_void_p]] 的实例分配一个新值会改变它们指向的 ''内存位置'' , '']不是内存块的内容''(当然不是,因为Python字节对象是不可变的): | |
− | [[#ctypes.c_wchar_p| | ||
− | |||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第418行: | 第365行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> s = "Hello, World" |
− | + | >>> c_s = c_wchar_p(s) | |
− | + | >>> print(c_s) | |
c_wchar_p(139966785747344) | c_wchar_p(139966785747344) | ||
− | + | >>> print(c_s.value) | |
Hello World | Hello World | ||
− | + | >>> c_s.value = "Hi, there" | |
− | + | >>> print(c_s) # the memory location has changed | |
c_wchar_p(139966783348904) | c_wchar_p(139966783348904) | ||
− | + | >>> print(c_s.value) | |
Hi, there | Hi, there | ||
− | + | >>> print(s) # first object is unchanged | |
Hello, World | Hello, World | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 但是,您应该小心,不要将它们传递给需要指向可变内存的指针的函数。 如果您需要可变内存块,ctypes 有一个 [[#ctypes.create_string_buffer|create_string_buffer()]] 函数,它以各种方式创建这些。 可以使用 <code>raw</code> 属性访问(或更改)当前内存块内容; 如果要以 NUL 终止的字符串形式访问它,请使用 <code>value</code> 属性: | |
− | |||
− | [[#ctypes.create_string_buffer| | ||
− | |||
− | |||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第447行: | 第389行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> from ctypes import * |
− | + | >>> p = create_string_buffer(3) # create a 3 byte buffer, initialized to NUL bytes | |
− | + | >>> print(sizeof(p), repr(p.raw)) | |
3 b'\x00\x00\x00' | 3 b'\x00\x00\x00' | ||
− | + | >>> p = create_string_buffer(b"Hello") # create a buffer containing a NUL terminated string | |
− | + | >>> print(sizeof(p), repr(p.raw)) | |
6 b'Hello\x00' | 6 b'Hello\x00' | ||
− | + | >>> print(repr(p.value)) | |
b'Hello' | b'Hello' | ||
− | + | >>> p = create_string_buffer(b"Hello", 10) # create a 10 byte buffer | |
− | + | >>> print(sizeof(p), repr(p.raw)) | |
10 b'Hello\x00\x00\x00\x00\x00' | 10 b'Hello\x00\x00\x00\x00\x00' | ||
− | + | >>> p.value = b"Hi" | |
− | + | >>> print(sizeof(p), repr(p.raw)) | |
10 b'Hi\x00lo\x00\x00\x00\x00\x00' | 10 b'Hi\x00lo\x00\x00\x00\x00\x00' | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | [[#ctypes.create_string_buffer|create_string_buffer()]] 函数替换了 <code>c_buffer()</code> 函数(仍可用作别名),以及早期 ctypes 版本中的 <code>c_string()</code> 函数。 要创建包含 C 类型 <code>wchar_t</code> Unicode 字符的可变内存块,请使用 [[#ctypes.create_unicode_buffer|create_unicode_buffer()]] 函数。 | |
− | |||
− | |||
− | |||
− | [[#ctypes.create_unicode_buffer| | ||
第478行: | 第416行: | ||
<span id="ctypes-calling-functions-continued"></span> | <span id="ctypes-calling-functions-continued"></span> | ||
− | === | + | === 调用函数,续 === |
− | + | 请注意,printf 打印到真正的标准输出通道,''not'' 到 [[../sys#sys|sys.stdout]],因此这些示例仅适用于控制台提示符,而不适用于 ''IDLE''或 ''PythonWin'': | |
− | [[../sys#sys| | ||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第488行: | 第424行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> printf = libc.printf |
− | + | >>> printf(b"Hello, %s\n", b"World!") | |
Hello, World! | Hello, World! | ||
14 | 14 | ||
− | + | >>> printf(b"Hello, %S\n", "World!") | |
Hello, World! | Hello, World! | ||
14 | 14 | ||
− | + | >>> printf(b"%d bottles of beer\n", 42) | |
42 bottles of beer | 42 bottles of beer | ||
19 | 19 | ||
− | + | >>> printf(b"%f bottles of beer\n", 42.5) | |
Traceback (most recent call last): | Traceback (most recent call last): | ||
− | File | + | File "<stdin>", line 1, in <module> |
ArgumentError: argument 2: exceptions.TypeError: Don't know how to convert parameter 2 | ArgumentError: argument 2: exceptions.TypeError: Don't know how to convert parameter 2 | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 如前所述,除整数、字符串和字节对象之外的所有 Python 类型都必须包装在其对应的 [[#module-ctypes|ctypes]] 类型中,以便将它们转换为所需的 C 数据类型: | |
− | |||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第515行: | 第449行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> printf(b"An int %d, a double %f\n", 1234, c_double(3.14)) |
An int 1234, a double 3.140000 | An int 1234, a double 3.140000 | ||
31 | 31 | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
第528行: | 第462行: | ||
<span id="ctypes-calling-functions-with-own-custom-data-types"></span> | <span id="ctypes-calling-functions-with-own-custom-data-types"></span> | ||
− | === | + | === 使用您自己的自定义数据类型调用函数 === |
− | + | 您还可以自定义 [[#module-ctypes|ctypes]] 参数转换,以允许将您自己的类的实例用作函数参数。 [[#module-ctypes|ctypes]] 查找 <code>_as_parameter_</code> 属性并将其用作函数参数。 当然,它必须是整数、字符串或字节之一: | |
− | |||
− | <code>_as_parameter_</code> | ||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第539行: | 第470行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> class Bottles: |
... def __init__(self, number): | ... def __init__(self, number): | ||
... self._as_parameter_ = number | ... self._as_parameter_ = number | ||
... | ... | ||
− | + | >>> bottles = Bottles(42) | |
− | + | >>> printf(b"%d bottles of beer\n", bottles) | |
42 bottles of beer | 42 bottles of beer | ||
19 | 19 | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 如果您不想将实例的数据存储在 <code>_as_parameter_</code> 实例变量中,您可以定义一个 [[../functions#property|属性]] ,使该属性在请求时可用。 | |
− | |||
− | |||
第561行: | 第490行: | ||
<span id="ctypes-specifying-required-argument-types"></span> | <span id="ctypes-specifying-required-argument-types"></span> | ||
− | === | + | === 指定所需的参数类型(函数原型) === |
− | + | 可以通过设置 <code>argtypes</code> 属性来指定从 DLL 导出的函数所需的参数类型。 | |
− | |||
− | <code>argtypes</code> | + | <code>argtypes</code> 必须是 C 数据类型的序列(<code>printf</code> 函数在这里可能不是一个很好的例子,因为它根据格式字符串需要一个变量数和不同类型的参数,在另一方面,这对于试验此功能非常方便): |
− | |||
− | |||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第575行: | 第500行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> printf.argtypes = [c_char_p, c_char_p, c_int, c_double] |
− | + | >>> printf(b"String '%s', Int %d, Double %f\n", b"Hi", 10, 2.2) | |
String 'Hi', Int 10, Double 2.200000 | String 'Hi', Int 10, Double 2.200000 | ||
37 | 37 | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 指定格式可以防止不兼容的参数类型(就像 C 函数的原型一样),并尝试将参数转换为有效类型: | |
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第591行: | 第515行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> printf(b"%d %d %d", 1, 2, 3) |
Traceback (most recent call last): | Traceback (most recent call last): | ||
− | File | + | File "<stdin>", line 1, in <module> |
ArgumentError: argument 2: exceptions.TypeError: wrong type | ArgumentError: argument 2: exceptions.TypeError: wrong type | ||
− | + | >>> printf(b"%s %d %f\n", b"X", 2, 3) | |
X 2 3.000000 | X 2 3.000000 | ||
13 | 13 | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 如果您定义了自己的类并传递给函数调用,则必须实现一个 <code>from_param()</code> 类方法,以便它们能够在 <code>argtypes</code> 序列中使用它们。 <code>from_param()</code> 类方法接收传递给函数调用的 Python 对象,它应该进行类型检查或任何需要以确保该对象是可接受的,然后返回对象本身,它的 <code>_as_parameter_</code>属性,或者在这种情况下您想作为 C 函数参数传递的任何内容。 同样,结果应该是整数、字符串、字节、[[#module-ctypes|ctypes]] 实例或具有 <code>_as_parameter_</code> 属性的对象。 | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | <code>_as_parameter_</code> | ||
第618行: | 第534行: | ||
<span id="ctypes-return-types"></span> | <span id="ctypes-return-types"></span> | ||
− | === | + | === 返回类型 === |
− | + | 默认情况下,函数假定返回 C <span class="xref c c-texpr">int</span> 类型。 其他返回类型可以通过设置函数对象的<code>restype</code>属性来指定。 | |
− | |||
− | |||
− | + | 这是一个更高级的例子,它使用 <code>strchr</code> 函数,它需要一个字符串指针和一个字符,并返回一个指向字符串的指针: | |
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第631行: | 第544行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> strchr = libc.strchr |
− | + | >>> strchr(b"abcdef", ord("d")) | |
8059983 | 8059983 | ||
− | + | >>> strchr.restype = c_char_p # c_char_p is a pointer to a string | |
− | + | >>> strchr(b"abcdef", ord("d")) | |
b'def' | b'def' | ||
− | + | >>> print(strchr(b"abcdef", ord("x"))) | |
None | None | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 如果要避免上面的 <code>ord("x")</code> 调用,可以设置 <code>argtypes</code> 属性,第二个参数将从单个字符的 Python 字节对象转换为 C 字符: | |
− | <code>argtypes</code> | ||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第652行: | 第563行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> strchr.restype = c_char_p |
− | + | >>> strchr.argtypes = [c_char_p, c_char] | |
− | + | >>> strchr(b"abcdef", b"d") | |
'def' | 'def' | ||
− | + | >>> strchr(b"abcdef", b"def") | |
Traceback (most recent call last): | Traceback (most recent call last): | ||
− | File | + | File "<stdin>", line 1, in <module> |
ArgumentError: argument 2: exceptions.TypeError: one character string expected | ArgumentError: argument 2: exceptions.TypeError: one character string expected | ||
− | + | >>> print(strchr(b"abcdef", b"x")) | |
None | None | ||
− | + | >>> strchr(b"abcdef", b"d") | |
'def' | 'def' | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 如果外部函数返回整数,您还可以使用可调用的 Python 对象(例如函数或类)作为 <code>restype</code> 属性。 将使用 C 函数返回的 ''整数'' 调用可调用对象,并且此调用的结果将用作函数调用的结果。 这对于检查错误返回值并自动引发异常很有用: | |
− | |||
− | |||
− | |||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第679行: | 第586行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> GetModuleHandle = windll.kernel32.GetModuleHandleA |
− | + | >>> def ValidHandle(value): | |
... if value == 0: | ... if value == 0: | ||
... raise WinError() | ... raise WinError() | ||
... return value | ... return value | ||
... | ... | ||
− | + | >>> | |
− | + | >>> GetModuleHandle.restype = ValidHandle | |
− | + | >>> GetModuleHandle(None) | |
486539264 | 486539264 | ||
− | + | >>> GetModuleHandle("something silly") | |
Traceback (most recent call last): | Traceback (most recent call last): | ||
− | File | + | File "<stdin>", line 1, in <module> |
− | File | + | File "<stdin>", line 3, in ValidHandle |
OSError: [Errno 126] The specified module could not be found. | OSError: [Errno 126] The specified module could not be found. | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | <code>WinError</code> | + | <code>WinError</code> 是一个函数,它将调用 Windows <code>FormatMessage()</code> api 来获取错误代码的字符串表示,而 ''返回'' 异常。 <code>WinError</code> 带有一个可选的错误代码参数,如果没有使用它,它会调用 [[#ctypes.GetLastError|GetLastError()]] 来检索它。 |
− | |||
− | <code>WinError</code> | ||
− | [[#ctypes.GetLastError| | ||
− | + | 请注意,通过 <code>errcheck</code> 属性可以使用更强大的错误检查机制; 有关详细信息,请参阅参考手册。 | |
− | |||
第712行: | 第615行: | ||
<span id="ctypes-passing-pointers"></span> | <span id="ctypes-passing-pointers"></span> | ||
− | === | + | === 传递指针(或:通过引用传递参数) === |
− | + | 有时一个 C api 函数需要一个指向数据类型的 ''指针'' 作为参数,可能是写入相应的位置,或者如果数据太大而无法通过值传递。 这也称为 ''通过引用'' 传递参数。 | |
− | |||
− | |||
− | [[#module-ctypes| | + | [[#module-ctypes|ctypes]] 导出 [[#ctypes.byref|byref()]] 函数,用于通过引用传递参数。 使用[[#ctypes.pointer|pointer()]]函数也可以达到同样的效果,虽然[[#ctypes.pointer|pointer()]]做了很多工作,因为它构造了一个真正的指针对象,所以使用[[#ctypes.byref|速度更快]byref()]] 如果你不需要 Python 本身的指针对象: |
− | |||
− | |||
− | |||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第728行: | 第625行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> 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'' | 0 0.0 b'' | ||
− | + | >>> libc.sscanf(b"1 3.14 Hello", b"%d %f %s", | |
... byref(i), byref(f), s) | ... byref(i), byref(f), s) | ||
3 | 3 | ||
− | + | >>> print(i.value, f.value, repr(s.value)) | |
1 3.1400001049 b'Hello' | 1 3.1400001049 b'Hello' | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
第748行: | 第645行: | ||
<span id="ctypes-structures-unions"></span> | <span id="ctypes-structures-unions"></span> | ||
− | === | + | === 结构和工会 === |
− | + | 结构和联合必须从 [[#module-ctypes|ctypes]] 模块中定义的 [[#ctypes.Structure|Structure]] 和 [[#ctypes.Union|Union]] 基类派生。 每个子类必须定义一个 <code>_fields_</code> 属性。 <code>_fields_</code> 必须是 ''2 元组'' 的列表,包含 ''字段名称'' 和 ''字段类型'' 。 | |
− | |||
− | |||
− | ''2 | ||
− | + | 字段类型必须是 [[#module-ctypes|ctypes]] 类型,如 [[#ctypes.c_int|c_int]],或任何其他派生的 [[#module-ctypes|ctypes]] 类型:结构、联合、数组、指针。 | |
− | |||
− | + | 下面是一个简单的 POINT 结构示例,其中包含两个名为 ''x'' 和 ''y'' 的整数,并且还展示了如何在构造函数中初始化结构: | |
− | ''x'' | ||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第765行: | 第657行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> from ctypes import * |
− | + | >>> class POINT(Structure): | |
− | ... _fields_ = [( | + | ... _fields_ = [("x", c_int), |
− | ... ( | + | ... ("y", c_int)] |
... | ... | ||
− | + | >>> point = POINT(10, 20) | |
− | + | >>> print(point.x, point.y) | |
10 20 | 10 20 | ||
− | + | >>> point = POINT(y=5) | |
− | + | >>> print(point.x, point.y) | |
0 5 | 0 5 | ||
− | + | >>> POINT(1, 2, 3) | |
Traceback (most recent call last): | Traceback (most recent call last): | ||
− | File | + | File "<stdin>", line 1, in <module> |
TypeError: too many initializers | TypeError: too many initializers | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 但是,您可以构建更复杂的结构。 通过将结构用作字段类型,结构本身可以包含其他结构。 | |
− | |||
− | + | 这是一个 RECT 结构,它包含两个名为 ''upperleft'' 和 ''lowerright'' 的 POINT: | |
− | ''lowerright'' | ||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第795行: | 第685行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> class RECT(Structure): |
− | ... _fields_ = [( | + | ... _fields_ = [("upperleft", POINT), |
− | ... ( | + | ... ("lowerright", POINT)] |
... | ... | ||
− | + | >>> rc = RECT(point) | |
− | + | >>> print(rc.upperleft.x, rc.upperleft.y) | |
0 5 | 0 5 | ||
− | + | >>> print(rc.lowerright.x, rc.lowerright.y) | |
0 0 | 0 0 | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 嵌套结构也可以通过以下几种方式在构造函数中初始化: | |
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第815行: | 第705行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> r = RECT(POINT(1, 2), POINT(3, 4)) |
− | + | >>> r = RECT((1, 2), (3, 4))</syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 字段 [[../../glossary#term-descriptor|描述符]] 可以从 ''类'' 中检索,它们对调试很有用,因为它们可以提供有用的信息: | |
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第828行: | 第717行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> print(POINT.x) |
− | + | <Field type=c_long, ofs=0, size=4> | |
− | + | >>> print(POINT.y) | |
− | + | <Field type=c_long, ofs=4, size=4> | |
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
第841行: | 第730行: | ||
警告 | 警告 | ||
− | [[#module-ctypes| | + | [[#module-ctypes|ctypes]] 不支持将带有位域的联合或结构按值传递给函数。 虽然这可能适用于 32 位 x86,但库不保证在一般情况下工作。 具有位域的联合和结构应始终通过指针传递给函数。 |
− | |||
− | |||
− | |||
第852行: | 第738行: | ||
<div id="structure-union-alignment-and-byte-order" class="section"> | <div id="structure-union-alignment-and-byte-order" class="section"> | ||
− | === | + | === 结构/联合对齐和字节顺序 === |
− | + | 默认情况下,Structure 和 Union 字段的对齐方式与 C 编译器的对齐方式相同。 可以通过在子类定义中指定 <code>_pack_</code> 类属性来覆盖此行为。 这必须设置为正整数并指定字段的最大对齐方式。 这也是 <code>#pragma pack(n)</code> 在 MSVC 中所做的。 | |
− | |||
− | <code>_pack_</code> | ||
− | |||
− | |||
− | [[#module-ctypes| | + | [[#module-ctypes|ctypes]] 使用结构和联合的本机字节顺序。 要构建具有非本机字节顺序的结构,您可以使用 [[#ctypes.BigEndianStructure|BigEndianStructure]]、[[#ctypes.LittleEndianStructure|LittleEndianStructure]]、<code>BigEndianUnion</code> 和 <code>LittleEndianUnion</code> 基类之一。 这些类不能包含指针字段。 |
− | |||
− | [[#ctypes.BigEndianStructure| | ||
− | <code>BigEndianUnion</code> | ||
− | |||
第871行: | 第749行: | ||
<span id="ctypes-bit-fields-in-structures-unions"></span> | <span id="ctypes-bit-fields-in-structures-unions"></span> | ||
− | === | + | === 结构体和联合体中的位域 === |
− | + | 可以创建包含位域的结构和联合。 位域仅适用于整数域,位宽被指定为 <code>_fields_</code> 元组中的第三项: | |
− | |||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第881行: | 第757行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> class Int(Structure): |
− | ... _fields_ = [( | + | ... _fields_ = [("first_16", c_int, 16), |
− | ... ( | + | ... ("second_16", c_int, 16)] |
... | ... | ||
− | + | >>> print(Int.first_16) | |
− | + | <Field type=c_long, ofs=0:0, bits=16> | |
− | + | >>> print(Int.second_16) | |
− | + | <Field type=c_long, ofs=0:16, bits=16> | |
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
第899行: | 第775行: | ||
<span id="ctypes-arrays"></span> | <span id="ctypes-arrays"></span> | ||
− | === | + | === 数组 === |
− | + | 数组是序列,包含固定数量的相同类型的实例。 | |
− | + | 创建数组类型的推荐方法是将数据类型与正整数相乘: | |
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第910行: | 第785行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">TenPointsArrayType = POINT * 10</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 这是一个有点人为的数据类型的示例,该结构包含 4 个 POINT 以及其他内容: | |
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第922行: | 第796行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> from ctypes import * |
− | + | >>> class POINT(Structure): | |
− | ... _fields_ = ( | + | ... _fields_ = ("x", c_int), ("y", c_int) |
... | ... | ||
− | + | >>> class MyStruct(Structure): | |
− | ... _fields_ = [( | + | ... _fields_ = [("a", c_int), |
− | ... ( | + | ... ("b", c_float), |
− | ... ( | + | ... ("point_array", POINT * 4)] |
− | + | >>> | |
− | + | >>> print(len(MyStruct().point_array)) | |
4 | 4 | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 实例以通常的方式创建,通过调用类: | |
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第944行: | 第818行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">arr = TenPointsArrayType() |
for pt in arr: | for pt in arr: | ||
− | print(pt.x, pt.y)</ | + | print(pt.x, pt.y)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 上面的代码打印了一系列 <code>0 0</code> 行,因为数组内容被初始化为零。 | |
− | |||
− | + | 还可以指定正确类型的初始化程序: | |
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第960行: | 第833行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> from ctypes import * |
− | + | >>> TenIntegers = c_int * 10 | |
− | + | >>> ii = TenIntegers(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) | |
− | + | >>> print(ii) | |
− | + | <c_long_Array_10 object at 0x...> | |
− | + | >>> for i in ii: print(i, end=" ") | |
... | ... | ||
1 2 3 4 5 6 7 8 9 10 | 1 2 3 4 5 6 7 8 9 10 | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
第978行: | 第851行: | ||
<span id="ctypes-pointers"></span> | <span id="ctypes-pointers"></span> | ||
− | === | + | === 指针 === |
− | + | 指针实例是通过在 [[#module-ctypes|ctypes]] 类型上调用 [[#ctypes.pointer|pointer()]] 函数来创建的: | |
− | [[# | ||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第987行: | 第859行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> from ctypes import * |
− | + | >>> i = c_int(42) | |
− | + | >>> pi = pointer(i) | |
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 指针实例有一个 [[#ctypes._Pointer.contents|contents]] 属性,它返回指针指向的对象,上面的 <code>i</code> 对象: | |
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第1,002行: | 第873行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> pi.contents |
c_long(42) | c_long(42) | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 请注意, [[#module-ctypes|ctypes]] 没有 OOR(原始对象返回),每次检索属性时它都会构造一个新的等效对象: | |
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第1,016行: | 第886行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> pi.contents is i |
False | False | ||
− | + | >>> pi.contents is pi.contents | |
False | False | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 将另一个 [[#ctypes.c_int|c_int]] 实例分配给指针的内容属性会导致指针指向存储它的内存位置: | |
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第1,032行: | 第901行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> i = c_int(99) |
− | + | >>> pi.contents = i | |
− | + | >>> pi.contents | |
c_long(99) | c_long(99) | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 指针实例也可以用整数索引: | |
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第1,047行: | 第916行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> pi[0] |
99 | 99 | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 分配给整数索引会更改指向的值: | |
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第1,060行: | 第929行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> print(i) |
c_long(99) | c_long(99) | ||
− | + | >>> pi[0] = 22 | |
− | + | >>> print(i) | |
c_long(22) | c_long(22) | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 也可以使用不同于 0 的索引,但您必须知道自己在做什么,就像在 C 中一样:您可以访问或更改任意内存位置。 通常,如果您从 C 函数接收指针,并且您 ''知道'' 该指针实际上指向一个数组而不是单个项目,则您只会使用此功能。 | |
− | |||
− | |||
− | |||
− | |||
− | + | 在幕后,[[#ctypes.pointer|pointer()]] 函数不仅仅是创建指针实例,它还必须首先创建指针 ''types''。 这是通过 [[#ctypes.POINTER|POINTER()]] 函数完成的,该函数接受任何 [[#module-ctypes|ctypes]] 类型,并返回一个新类型: | |
− | |||
− | [[#ctypes.POINTER| | ||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第1,085行: | 第947行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> PI = POINTER(c_int) |
− | + | >>> PI | |
− | + | <class 'ctypes.LP_c_long'> | |
− | + | >>> PI(42) | |
Traceback (most recent call last): | Traceback (most recent call last): | ||
− | File | + | File "<stdin>", line 1, in <module> |
TypeError: expected c_long instead of int | TypeError: expected c_long instead of int | ||
− | + | >>> PI(c_int(42)) | |
− | + | <ctypes.LP_c_long object at 0x...> | |
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 不带参数调用指针类型会创建一个 <code>NULL</code> 指针。 <code>NULL</code> 指针有一个 <code>False</code> 布尔值: | |
− | <code>NULL</code> | ||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第1,106行: | 第967行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> null_ptr = POINTER(c_int)() |
− | + | >>> print(bool(null_ptr)) | |
False | False | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | [[#module-ctypes| | + | [[#module-ctypes|ctypes]] 在解引用指针时检查 <code>NULL</code>(但解引用无效的非 <code>NULL</code> 指针会使 Python 崩溃): |
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第1,121行: | 第981行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> null_ptr[0] |
Traceback (most recent call last): | Traceback (most recent call last): | ||
.... | .... | ||
ValueError: NULL pointer access | ValueError: NULL pointer access | ||
− | + | >>> | |
− | + | >>> null_ptr[0] = 1234 | |
Traceback (most recent call last): | Traceback (most recent call last): | ||
.... | .... | ||
ValueError: NULL pointer access | ValueError: NULL pointer access | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
第1,141行: | 第1,001行: | ||
<span id="ctypes-type-conversions"></span> | <span id="ctypes-type-conversions"></span> | ||
− | === | + | === 类型转换 === |
− | + | 通常,ctypes 会进行严格的类型检查。 这意味着,如果在函数的 <code>argtypes</code> 列表中有 <code>POINTER(c_int)</code> 或作为结构定义中成员字段的类型,则只接受完全相同类型的实例。 此规则有一些例外,其中 ctypes 接受其他对象。 例如,您可以传递兼容的数组实例而不是指针类型。 因此,对于 <code>POINTER(c_int)</code>,ctypes 接受一个 c_int 数组: | |
− | <code> | ||
− | |||
− | |||
− | |||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第1,154行: | 第1,009行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> class Bar(Structure): |
− | ... _fields_ = [( | + | ... _fields_ = [("count", c_int), ("values", POINTER(c_int))] |
... | ... | ||
− | + | >>> bar = Bar() | |
− | + | >>> bar.values = (c_int * 3)(1, 2, 3) | |
− | + | >>> bar.count = 3 | |
− | + | >>> for i in range(bar.count): | |
... print(bar.values[i]) | ... print(bar.values[i]) | ||
... | ... | ||
第1,166行: | 第1,021行: | ||
2 | 2 | ||
3 | 3 | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 此外,如果函数参数在 <code>argtypes</code> 中显式声明为指针类型(例如 <code>POINTER(c_int)</code>),则为指向类型的对象(在本例中为 <code>c_int</code>)可以传递给函数。 在这种情况下,ctypes 将自动应用所需的 [[#ctypes.byref|byref()]] 转换。 | |
− | |||
− | |||
− | |||
− | + | 要将 POINTER 类型字段设置为 <code>NULL</code>,您可以分配 <code>None</code>: | |
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第1,182行: | 第1,034行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> bar.values = None |
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 有时您有不兼容类型的实例。 在 C 中,您可以将一种类型转换为另一种类型。 [[#module-ctypes|ctypes]] 提供了一个 [[#ctypes.cast|cast()]] 函数,可以以同样的方式使用。 上面定义的 <code>Bar</code> 结构接受 <code>POINTER(c_int)</code> 指针或 [[#ctypes.c_int|c_int]] 数组作为其 <code>values</code> 字段,但不接受其他类型的实例: | |
− | |||
− | |||
− | <code>POINTER(c_int)</code> | ||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第1,198行: | 第1,046行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> bar.values = (c_byte * 4)() |
Traceback (most recent call last): | Traceback (most recent call last): | ||
− | File | + | File "<stdin>", line 1, in <module> |
TypeError: incompatible types, c_byte_Array_4 instance instead of LP_c_long instance | TypeError: incompatible types, c_byte_Array_4 instance instead of LP_c_long instance | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 对于这些情况,[[#ctypes.cast|cast()]] 函数很方便。 | |
− | + | [[#ctypes.cast|cast()]] 函数可用于将 ctypes 实例转换为指向不同 ctypes 数据类型的指针。 [[#ctypes.cast|cast()]] 接受两个参数,一个是或可以转换为某种指针的 ctypes 对象,以及一个 ctypes 指针类型。 它返回第二个参数的实例,它引用与第一个参数相同的内存块: | |
− | |||
− | |||
− | |||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第1,219行: | 第1,063行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> a = (c_byte * 4)() |
− | + | >>> cast(a, POINTER(c_int)) | |
− | + | <ctypes.LP_c_long object at ...> | |
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 因此,可以使用 [[#ctypes.cast|cast()]] 将结构体分配给 <code>Bar</code> 的 <code>values</code> 字段: | |
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第1,234行: | 第1,077行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> bar = Bar() |
− | + | >>> bar.values = cast((c_byte * 4)(), POINTER(c_int)) | |
− | + | >>> print(bar.values[0]) | |
0 | 0 | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
第1,248行: | 第1,091行: | ||
<span id="ctypes-incomplete-types"></span> | <span id="ctypes-incomplete-types"></span> | ||
− | === | + | === 不完全类型 === |
− | '' | + | ''不完整类型'' 是结构、联合或数组,其成员尚未指定。 在 C 中,它们由前向声明指定,稍后定义: |
− | |||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第1,258行: | 第1,099行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">struct cell; /* forward declaration */ |
struct cell { | struct cell { | ||
char *name; | char *name; | ||
struct cell *next; | struct cell *next; | ||
− | };</ | + | };</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 直接转换为 ctypes 代码是这样的,但它不起作用: | |
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第1,275行: | 第1,115行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> class cell(Structure): |
− | ... _fields_ = [( | + | ... _fields_ = [("name", c_char_p), |
− | ... ( | + | ... ("next", POINTER(cell))] |
... | ... | ||
Traceback (most recent call last): | Traceback (most recent call last): | ||
− | File | + | File "<stdin>", line 1, in <module> |
− | File | + | File "<stdin>", line 2, in cell |
NameError: name 'cell' is not defined | NameError: name 'cell' is not defined | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 因为新的 <code>class cell</code> 在 class 语句本身中不可用。 在 [[#module-ctypes|ctypes]] 中,我们可以定义 <code>cell</code> 类,然后在 class 语句之后设置 <code>_fields_</code> 属性: | |
− | |||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第1,296行: | 第1,134行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> from ctypes import * |
− | + | >>> class cell(Structure): | |
... pass | ... pass | ||
... | ... | ||
− | + | >>> cell._fields_ = [("name", c_char_p), | |
− | ... ( | + | ... ("next", POINTER(cell))] |
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 让我们试试吧。 我们创建了两个 <code>cell</code> 的实例,并让它们相互指向,最后沿着指针链走几次: | |
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第1,314行: | 第1,151行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> c1 = cell() |
− | + | >>> c1.name = b"foo" | |
− | + | >>> c2 = cell() | |
− | + | >>> c2.name = b"bar" | |
− | + | >>> c1.next = pointer(c2) | |
− | + | >>> c2.next = pointer(c1) | |
− | + | >>> p = c1 | |
− | + | >>> for i in range(8): | |
− | ... print(p.name, end= | + | ... print(p.name, end=" ") |
... p = p.next[0] | ... p = p.next[0] | ||
... | ... | ||
foo bar foo bar foo bar foo bar | foo bar foo bar foo bar foo bar | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
第1,336行: | 第1,173行: | ||
<span id="ctypes-callback-functions"></span> | <span id="ctypes-callback-functions"></span> | ||
− | === | + | === 回调函数 === |
− | [[#module-ctypes| | + | [[#module-ctypes|ctypes]] 允许从 Python 可调用对象创建 C 可调用函数指针。 这些有时称为 ''回调函数'' 。 |
− | |||
− | + | 首先,您必须为回调函数创建一个类。 该类知道调用约定、返回类型以及此函数将接收的参数的数量和类型。 | |
− | |||
− | |||
− | + | [[#ctypes.CFUNCTYPE|CFUNCTYPE()]] 工厂函数使用 <code>cdecl</code> 调用约定为回调函数创建类型。 在 Windows 上,[[#ctypes.WINFUNCTYPE|WINFUNCTYPE()]] 工厂函数使用 <code>stdcall</code> 调用约定为回调函数创建类型。 | |
− | |||
− | |||
− | |||
− | + | 这两个工厂函数都以结果类型作为第一个参数被调用,回调函数期望的参数类型作为剩余的参数。 | |
− | |||
− | |||
− | + | 我将在此处展示一个示例,该示例使用标准 C 库的 <code>qsort()</code> 函数,该函数用于在回调函数的帮助下对项目进行排序。 <code>qsort()</code> 将用于对整数数组进行排序: | |
− | <code>qsort()</code> | ||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第1,362行: | 第1,189行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> IntArray5 = c_int * 5 |
− | + | >>> ia = IntArray5(5, 1, 7, 33, 99) | |
− | + | >>> qsort = libc.qsort | |
− | + | >>> qsort.restype = None | |
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | <code>qsort()</code> | + | <code>qsort()</code> 必须使用指向要排序的数据的指针、数据数组中的项数、一项的大小以及指向比较函数、回调的指针来调用。 然后将使用两个指向项目的指针调用回调,如果第一个项目小于第二个项目,则它必须返回一个负整数,如果它们相等,则返回零,否则返回一个正整数。 |
− | |||
− | |||
− | |||
− | |||
− | + | 所以我们的回调函数接收指向整数的指针,并且必须返回一个整数。 首先我们为回调函数创建<code>type</code>: | |
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第1,384行: | 第1,206行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> CMPFUNC = CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int)) |
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 首先,这是一个简单的回调,显示它传递的值: | |
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第1,397行: | 第1,218行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> def py_cmp_func(a, b): |
− | ... print( | + | ... print("py_cmp_func", a[0], b[0]) |
... return 0 | ... return 0 | ||
... | ... | ||
− | + | >>> cmp_func = CMPFUNC(py_cmp_func) | |
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 结果: | |
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第1,413行: | 第1,234行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> qsort(ia, len(ia), sizeof(c_int), cmp_func) |
py_cmp_func 5 1 | py_cmp_func 5 1 | ||
py_cmp_func 33 99 | py_cmp_func 33 99 | ||
第1,419行: | 第1,240行: | ||
py_cmp_func 5 7 | py_cmp_func 5 7 | ||
py_cmp_func 1 7 | py_cmp_func 1 7 | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 现在我们可以实际比较这两个项目并返回一个有用的结果: | |
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第1,430行: | 第1,251行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> def py_cmp_func(a, b): |
− | ... print( | + | ... print("py_cmp_func", a[0], b[0]) |
... return 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 5 1 | ||
py_cmp_func 33 99 | py_cmp_func 33 99 | ||
第1,441行: | 第1,262行: | ||
py_cmp_func 1 7 | py_cmp_func 1 7 | ||
py_cmp_func 5 7 | py_cmp_func 5 7 | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 正如我们可以轻松检查的那样,我们的数组现在已排序: | |
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第1,452行: | 第1,273行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> for i in ia: print(i, end=" ") |
... | ... | ||
1 5 7 33 99 | 1 5 7 33 99 | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 函数工厂可以用作装饰器工厂,所以我们不妨这样写: | |
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第1,467行: | 第1,287行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> @CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int)) |
... def py_cmp_func(a, b): | ... def py_cmp_func(a, b): | ||
− | ... print( | + | ... print("py_cmp_func", a[0], b[0]) |
... return a[0] - b[0] | ... return a[0] - b[0] | ||
... | ... | ||
− | + | >>> qsort(ia, len(ia), sizeof(c_int), py_cmp_func) | |
py_cmp_func 5 1 | py_cmp_func 5 1 | ||
py_cmp_func 33 99 | py_cmp_func 33 99 | ||
第1,478行: | 第1,298行: | ||
py_cmp_func 1 7 | py_cmp_func 1 7 | ||
py_cmp_func 5 7 | py_cmp_func 5 7 | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
第1,485行: | 第1,305行: | ||
<div class="admonition note"> | <div class="admonition note"> | ||
− | + | 笔记 | |
− | + | 确保保留对 [[#ctypes.CFUNCTYPE|CFUNCTYPE()]] 对象的引用,只要它们从 C 代码中使用即可。 [[#module-ctypes|ctypes]] 没有,如果你不这样做,它们可能会被垃圾收集,在进行回调时使你的程序崩溃。 | |
− | |||
− | |||
− | + | 另外,请注意,如果在 Python 控制之外创建的线程中调用回调函数(例如 通过调用回调的外部代码),ctypes 在每次调用时创建一个新的虚拟 Python 线程。 这种行为在大多数情况下是正确的,但这意味着使用 [[../threading#threading|threading.local]] 存储的值将 ''not'' 在不同的回调中存活,即使这些调用来自同一个 C 线程。 | |
− | |||
− | |||
− | |||
− | [[../threading#threading| | ||
− | |||
第1,505行: | 第1,318行: | ||
<span id="ctypes-accessing-values-exported-from-dlls"></span> | <span id="ctypes-accessing-values-exported-from-dlls"></span> | ||
− | === | + | === 访问从 dll 导出的值 === |
− | + | 一些共享库不仅导出函数,还导出变量。 Python 库本身中的一个示例是 [[../../c-api/init#c|Py_OptimizeFlag]],一个整数设置为 0、1 或 2,具体取决于给定的 [[../../using/cmdline#cmdoption-O|-O]] 或 [[../../using/cmdline#cmdoption-OO|-OO]] 标志在启动时。 | |
− | |||
− | |||
− | |||
− | [[#module-ctypes| | + | [[#module-ctypes|ctypes]] 可以使用该类型的 <code>in_dll()</code> 类方法访问这样的值。 ''pythonapi'' 是一个预定义的符号,可以访问 Python C api: |
− | |||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第1,520行: | 第1,328行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> opt_flag = c_int.in_dll(pythonapi, "Py_OptimizeFlag") |
− | + | >>> print(opt_flag) | |
c_long(0) | c_long(0) | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 如果解释器以 [[../../using/cmdline#cmdoption-O|-O]] 开始,则样本将打印 <code>c_long(1)</code>,或 <code>c_long(2)</code> 如果已指定 [[../../using/cmdline#cmdoption-OO|-OO]]。 | |
− | |||
− | |||
− | + | 一个扩展示例也演示了如何使用指针访问 Python 导出的 [[../../c-api/import#c|PyImport_FrozenModules]] 指针。 | |
− | [[../../c-api/import#c| | ||
− | + | 引用该值的文档: | |
<blockquote><div> | <blockquote><div> | ||
− | + | 这个指针被初始化为指向一个 <span class="xref c c-texpr">struct _frozen</span> 记录数组,由一个成员都是 <code>NULL</code> 或零的记录终止。 导入冻结模块时,将在此表中搜索。 第三方代码可以利用此技巧来提供动态创建的冻结模块集合。 | |
− | |||
− | |||
− | |||
</div></blockquote> | </div></blockquote> | ||
− | + | 所以操纵这个指针甚至可以证明是有用的。 为了限制示例大小,我们仅展示如何使用 [[#module-ctypes|ctypes]] 读取此表: | |
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第1,553行: | 第1,354行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> from ctypes import * |
− | + | >>> | |
− | + | >>> class struct_frozen(Structure): | |
− | ... _fields_ = [( | + | ... _fields_ = [("name", c_char_p), |
− | ... ( | + | ... ("code", POINTER(c_ubyte)), |
− | ... ( | + | ... ("size", c_int)] |
... | ... | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 我们已经定义了 <span class="xref c c-texpr">struct _frozen</span> 数据类型,所以我们可以得到指向表的指针: | |
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第1,572行: | 第1,372行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> FrozenTable = POINTER(struct_frozen) |
− | + | >>> table = FrozenTable.in_dll(pythonapi, "PyImport_FrozenModules") | |
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 由于 <code>table</code> 是 <code>struct_frozen</code> 记录数组的 <code>pointer</code>,我们可以迭代它,但我们只需要确保我们的循环终止,因为指针没有大小. 迟早它可能会因访问冲突或其他原因而崩溃,因此最好在我们点击 <code>NULL</code> 条目时跳出循环: | |
− | |||
− | |||
− | |||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第1,589行: | 第1,385行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> for item in table: |
... if item.name is None: | ... if item.name is None: | ||
... break | ... break | ||
− | ... print(item.name.decode( | + | ... print(item.name.decode("ascii"), item.size) |
... | ... | ||
_frozen_importlib 31764 | _frozen_importlib 31764 | ||
第1,599行: | 第1,395行: | ||
__phello__ -161 | __phello__ -161 | ||
__phello__.spam 161 | __phello__.spam 161 | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 标准 Python 有一个冻结模块和一个冻结包(由负的 <code>size</code> 成员表示)这一事实并不为人所知,它仅用于测试。 例如,尝试使用 <code>import __hello__</code>。 | |
− | |||
− | |||
第1,613行: | 第1,407行: | ||
<span id="ctypes-surprises"></span> | <span id="ctypes-surprises"></span> | ||
− | === | + | === 惊喜 === |
− | + | 在 [[#module-ctypes|ctypes]] 中有一些边缘,你可能会期待一些与实际发生的情况不同的东西。 | |
− | |||
− | + | 考虑以下示例: | |
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第1,624行: | 第1,417行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> from ctypes import * |
− | + | >>> class POINT(Structure): | |
− | ... _fields_ = ( | + | ... _fields_ = ("x", c_int), ("y", c_int) |
... | ... | ||
− | + | >>> class RECT(Structure): | |
− | ... _fields_ = ( | + | ... _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 | 1 2 3 4 | ||
− | + | >>> # now swap the two points | |
− | + | >>> 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 | 3 4 3 4 | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 嗯。 我们当然希望最后一条语句打印 <code>3 4 1 2</code>。 发生了什么? 以下是上面 <code>rc.a, rc.b = rc.b, rc.a</code> 行的步骤: | |
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第1,652行: | 第1,444行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> temp0, temp1 = rc.b, rc.a |
− | + | >>> rc.a = temp0 | |
− | + | >>> rc.b = temp1 | |
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 请注意,<code>temp0</code> 和 <code>temp1</code> 是仍在使用上述 <code>rc</code> 对象的内部缓冲区的对象。 因此,执行 <code>rc.a = temp0</code> 会将 <code>temp0</code> 的缓冲区内容复制到 <code>rc</code> 的缓冲区中。 这反过来又会改变 <code>temp1</code> 的内容。 所以,最后一个赋值 <code>rc.b = temp1</code> 没有达到预期的效果。 | |
− | |||
− | |||
− | |||
− | |||
− | + | 请记住,从结构、联合和数组中检索子对象不会 ''复制'' 子对象,而是检索访问根对象的底层缓冲区的包装器对象。 | |
− | |||
− | |||
− | + | 另一个可能与人们期望的行为不同的示例是: | |
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第1,676行: | 第1,462行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> s = c_char_p() |
− | + | >>> s.value = b"abc def ghi" | |
− | + | >>> s.value | |
b'abc def ghi' | b'abc def ghi' | ||
− | + | >>> s.value is s.value | |
False | False | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
第1,689行: | 第1,475行: | ||
<div class="admonition note"> | <div class="admonition note"> | ||
− | + | 笔记 | |
− | + | 从 [[#ctypes.c_char_p|c_char_p]] 实例化的对象只能将其值设置为字节或整数。 | |
− | |||
</div> | </div> | ||
− | + | 为什么打印 <code>False</code>? ctypes 实例是包含内存块和一些 [[../../glossary#term-descriptor|描述符]] 访问内存内容的对象。 在内存块中存储 Python 对象并不存储对象本身,而是存储对象的 <code>contents</code>。 再次访问内容每次都会构造一个新的 Python 对象! | |
− | |||
− | |||
− | |||
− | |||
第1,707行: | 第1,488行: | ||
<span id="ctypes-variable-sized-data-types"></span> | <span id="ctypes-variable-sized-data-types"></span> | ||
− | === | + | === 可变大小的数据类型 === |
− | [[#module-ctypes| | + | [[#module-ctypes|ctypes]] 为可变大小的数组和结构提供了一些支持。 |
− | + | [[#ctypes.resize|resize()]] 函数可用于调整现有 ctypes 对象的内存缓冲区大小。 该函数将对象作为第一个参数,将请求的字节大小作为第二个参数。 内存块不能小于对象类型指定的自然内存块,如果尝试这样做,则会引发 [[../exceptions#ValueError|ValueError]]: | |
− | |||
− | |||
− | |||
− | [[../exceptions#ValueError| | ||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第1,721行: | 第1,498行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> short_array = (c_short * 4)() |
− | + | >>> print(sizeof(short_array)) | |
8 | 8 | ||
− | + | >>> resize(short_array, 4) | |
Traceback (most recent call last): | Traceback (most recent call last): | ||
... | ... | ||
ValueError: minimum size is 8 | ValueError: minimum size is 8 | ||
− | + | >>> resize(short_array, 32) | |
− | + | >>> sizeof(short_array) | |
32 | 32 | ||
− | + | >>> sizeof(type(short_array)) | |
8 | 8 | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 这很好也很好,但是如何访问包含在这个数组中的附加元素呢? 由于该类型仍然只知道 4 个元素,因此访问其他元素时会出错: | |
− | |||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第1,746行: | 第1,521行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> short_array[:] |
[0, 0, 0, 0] | [0, 0, 0, 0] | ||
− | + | >>> short_array[7] | |
Traceback (most recent call last): | Traceback (most recent call last): | ||
... | ... | ||
IndexError: invalid index | IndexError: invalid index | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 将可变大小数据类型与 [[#module-ctypes|ctypes]] 一起使用的另一种方法是使用 Python 的动态特性,并在已知所需大小后(重新)定义数据类型,具体情况具体分析。 | |
− | |||
− | |||
第1,768行: | 第1,541行: | ||
<span id="ctypes-ctypes-reference"></span> | <span id="ctypes-ctypes-reference"></span> | ||
− | == ctypes | + | == ctypes 参考 == |
<div id="finding-shared-libraries" class="section"> | <div id="finding-shared-libraries" class="section"> | ||
<span id="ctypes-finding-shared-libraries"></span> | <span id="ctypes-finding-shared-libraries"></span> | ||
− | === | + | === 查找共享库 === |
− | + | 使用编译语言编程时,在编译/链接程序以及运行程序时访问共享库。 | |
− | |||
− | + | <code>find_library()</code> 函数的目的是以类似于编译器或运行时加载器所做的方式(在具有多个共享库版本的平台上应加载最新版本)的方式定位库,而 ctypes 库加载器就像程序运行时一样,直接调用运行时加载器。 | |
− | |||
− | |||
− | |||
− | |||
− | + | <code>ctypes.util</code> 模块提供了一个函数,可以帮助确定要加载的库。 | |
− | |||
− | ; < | + | ; <span class="sig-prename descclassname"><span class="pre">ctypes.util.</span></span><span class="sig-name descname"><span class="pre">find_library</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">name</span></span>''<span class="sig-paren">)</span> |
− | : | + | : 尝试查找库并返回路径名。 ''name'' 是没有任何前缀的库名,如 ''lib'',后缀如 <code>.so</code>, <code>.dylib</code> 或版本号(这是用于 posix 的形式链接器选项 <code>-l</code>)。 如果找不到库,则返回 <code>None</code>。 |
− | + | 确切的功能取决于系统。 | |
− | + | 在 Linux 上,<code>find_library()</code> 尝试运行外部程序(<code>/sbin/ldconfig</code>、<code>gcc</code>、<code>objdump</code> 和 <code>ld</code>)来查找库文件。 它返回库文件的文件名。 | |
− | |||
− | |||
<div class="versionchanged"> | <div class="versionchanged"> | ||
− | <span class="versionmodified changed"> | + | <span class="versionmodified changed"> 3.6 版本变更: </span> 在 Linux 上,如果通过任何其他方式无法找到库,则在搜索库时使用环境变量 <code>LD_LIBRARY_PATH</code> 的值。 |
− | |||
</div> | </div> | ||
− | + | 这里有些例子: | |
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第1,809行: | 第1,573行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> from ctypes.util import find_library |
− | + | >>> find_library("m") | |
'libm.so.6' | 'libm.so.6' | ||
− | + | >>> find_library("c") | |
'libc.so.6' | 'libc.so.6' | ||
− | + | >>> find_library("bz2") | |
'libbz2.so.1.0' | 'libbz2.so.1.0' | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 在 macOS 上,<code>find_library()</code> 尝试多个预定义的命名方案和路径来定位库,如果成功则返回完整路径名: | |
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第1,828行: | 第1,591行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> from ctypes.util import find_library |
− | + | >>> find_library("c") | |
'/usr/lib/libc.dylib' | '/usr/lib/libc.dylib' | ||
− | + | >>> find_library("m") | |
'/usr/lib/libm.dylib' | '/usr/lib/libm.dylib' | ||
− | + | >>> find_library("bz2") | |
'/usr/lib/libbz2.dylib' | '/usr/lib/libbz2.dylib' | ||
− | + | >>> find_library("AGL") | |
'/System/Library/Frameworks/AGL.framework/AGL' | '/System/Library/Frameworks/AGL.framework/AGL' | ||
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 在 Windows 上,<code>find_library()</code> 沿着系统搜索路径搜索,并返回完整路径名,但由于没有预定义的命名方案,像 <code>find_library("c")</code> 这样的调用将失败并返回 <code>None</code>。 | |
− | |||
− | |||
− | + | 如果使用 [[#module-ctypes|ctypes]] 包装共享库,''可能'' 更好地在开发时确定共享库名称,并将其硬编码到包装器模块中而不是使用 <code>find_library()</code>在运行时定位库。 | |
− | |||
− | |||
第1,855行: | 第1,614行: | ||
<span id="ctypes-loading-shared-libraries"></span> | <span id="ctypes-loading-shared-libraries"></span> | ||
− | === | + | === 加载共享库 === |
− | + | 有几种方法可以将共享库加载到 Python 进程中。 一种方法是实例化以下类之一: | |
− | |||
<dl> | <dl> | ||
− | <dt>''class'' < | + | <dt>''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">CDLL</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">name</span></span>'', ''<span class="n"><span class="pre">mode</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">DEFAULT_MODE</span></span>'', ''<span class="n"><span class="pre">handle</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>'', ''<span class="n"><span class="pre">use_errno</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">False</span></span>'', ''<span class="n"><span class="pre">use_last_error</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">False</span></span>'', ''<span class="n"><span class="pre">winmode</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">0</span></span>''<span class="sig-paren">)</span></dt> |
− | <dd><p> | + | <dd><p>此类的实例表示加载的共享库。 这些库中的函数使用标准 C 调用约定,并假定返回 <span class="xref c c-texpr">int</span>。</p> |
− | + | <p>在 Windows 上,即使 DLL 名称存在,创建 [[#ctypes.CDLL|CDLL]] 实例也可能会失败。 当未找到加载的 DLL 的依赖 DLL 时,会引发 [[../exceptions#OSError|OSError]] 错误,并显示消息 ''“[WinError 126] 找不到指定的模块”。'' 此错误消息不会不包含丢失的 DLL 的名称,因为 Windows API 不返回此信息,使此错误难以诊断。 要解决此错误并确定未找到哪个 DLL,您需要查找相关 DLL 列表并使用 Windows 调试和跟踪工具确定未找到哪个 DLL。</p></dd></dl> | |
− | <span class="xref c c-texpr">int</span> | ||
− | <p> | ||
− | |||
− | [[../exceptions#OSError| | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
<div class="admonition seealso"> | <div class="admonition seealso"> | ||
− | + | 也可以看看 | |
− | [https://docs.microsoft.com/cpp/build/reference/dependents Microsoft DUMPBIN | + | [https://docs.microsoft.com/cpp/build/reference/dependents Microsoft DUMPBIN 工具] - 查找 DLL 依赖项的工具。 |
− | - | ||
</div> | </div> | ||
<dl> | <dl> | ||
− | <dt>''class'' < | + | <dt>''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">OleDLL</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">name</span></span>'', ''<span class="n"><span class="pre">mode</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">DEFAULT_MODE</span></span>'', ''<span class="n"><span class="pre">handle</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>'', ''<span class="n"><span class="pre">use_errno</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">False</span></span>'', ''<span class="n"><span class="pre">use_last_error</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">False</span></span>'', ''<span class="n"><span class="pre">winmode</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">0</span></span>''<span class="sig-paren">)</span></dt> |
− | <dd><p> | + | <dd><p>仅限 Windows:此类的实例表示加载的共享库,这些库中的函数使用 <code>stdcall</code> 调用约定,并假定返回特定于 Windows 的 [[#ctypes.HRESULT|HRESULT]] 代码。 [[#ctypes.HRESULT|HRESULT]] 值包含指定函数调用是失败还是成功的信息,以及其他错误代码。 如果返回值表示失败,则会自动引发 [[../exceptions#OSError|OSError]]。</p> |
− | |||
− | |||
− | |||
− | |||
− | |||
<div class="versionchanged"> | <div class="versionchanged"> | ||
− | <p><span class="versionmodified changed"> | + | <p><span class="versionmodified changed"> 3.3 版更改: </span>[[../exceptions#WindowsError|WindowsError]] 曾经被提出。</p> |
</div></dd></dl> | </div></dd></dl> | ||
<dl> | <dl> | ||
− | <dt>''class'' < | + | <dt>''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">WinDLL</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">name</span></span>'', ''<span class="n"><span class="pre">mode</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">DEFAULT_MODE</span></span>'', ''<span class="n"><span class="pre">handle</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>'', ''<span class="n"><span class="pre">use_errno</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">False</span></span>'', ''<span class="n"><span class="pre">use_last_error</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">False</span></span>'', ''<span class="n"><span class="pre">winmode</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">0</span></span>''<span class="sig-paren">)</span></dt> |
− | <dd><p> | + | <dd><p>仅限 Windows:此类的实例表示加载的共享库,这些库中的函数使用 <code>stdcall</code> 调用约定,并假定默认返回 <span class="xref c c-texpr">int</span>。</p> |
− | + | <p>在 Windows CE 上仅使用标准调用约定,为方便起见,[[#ctypes.WinDLL|WinDLL]] 和 [[#ctypes.OleDLL|OleDLL]] 使用此平台上的标准调用约定。</p></dd></dl> | |
− | |||
− | <p> | ||
− | [[#ctypes.WinDLL| | ||
− | |||
− | + | Python [[../../glossary#term-global-interpreter-lock|全局解释器锁]]在调用这些库导出的任何函数之前释放,之后重新获取。 | |
− | |||
<dl> | <dl> | ||
− | <dt>''class'' < | + | <dt>''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">PyDLL</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">name</span></span>'', ''<span class="n"><span class="pre">mode</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">DEFAULT_MODE</span></span>'', ''<span class="n"><span class="pre">handle</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>''<span class="sig-paren">)</span></dt> |
− | <dd><p> | + | <dd><p>此类的实例的行为类似于 [[#ctypes.CDLL|CDLL]] 实例,除了 Python GIL 在函数调用期间是 ''不是'' 释放的,并且在函数执行后检查 Python 错误标志。 如果设置了错误标志,则会引发 Python 异常。</p> |
− | Python GIL | + | <p>因此,这仅对直接调用 Python C api 函数有用。</p></dd></dl> |
− | |||
− | |||
− | <p> | ||
− | + | 所有这些类都可以通过使用至少一个参数(共享库的路径名)调用它们来实例化。 如果你有一个已经加载的共享库的现有句柄,它可以作为 <code>handle</code> 命名参数传递,否则底层平台 <code>dlopen</code> 或 <code>LoadLibrary</code> 函数用于加载库进入进程,并得到它的句柄。 | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | ''mode'' 参数可用于指定库的加载方式。 有关详细信息,请参阅 ''dlopen(3)'' 联机帮助页。 在 Windows 上,''mode'' 被忽略。 在 posix 系统上,始终添加 RTLD_NOW,并且不可配置。 | |
− | |||
− | |||
− | |||
− | + | ''use_errno'' 参数设置为 true 时,启用 ctypes 机制,允许以安全的方式访问系统 [[../errno#module-errno|errno]] 错误号。 [[#module-ctypes|ctypes]] 维护系统 [[../errno#module-errno|errno]] 变量的线程本地副本; 如果您调用使用 <code>use_errno=True</code> 创建的外部函数,那么在函数调用与 ctypes 私有副本交换之前的 [[../errno#module-errno|errno]] 值,函数调用后会立即发生同样的情况。 | |
− | |||
− | [[#module-ctypes| | ||
− | |||
− | [[../errno#module-errno| | ||
− | |||
− | + | 函数 [[#ctypes.get_errno|ctypes.get_errno()]] 返回 ctypes 私有副本的值,函数 [[#ctypes.set_errno|ctypes.set_errno()]] 将 ctypes 私有副本更改为新值并返回之前的值. | |
− | |||
− | |||
− | + | ''use_last_error'' 参数设置为 true 时,为由 [[#ctypes.GetLastError|GetLastError()]] 和 <code>SetLastError()</code> Windows API 函数管理的 Windows 错误代码启用相同的机制; [[#ctypes.get_last_error|ctypes.get_last_error()]] 和 [[#ctypes.set_last_error|ctypes.set_last_error()]] 用于请求和更改 windows 错误代码的 ctypes 私有副本。 | |
− | |||
− | <code>SetLastError()</code> Windows API | ||
− | [[#ctypes.set_last_error| | ||
− | |||
− | + | ''winmode'' 参数在 Windows 上用于指定库的加载方式(因为 ''mode'' 被忽略)。 它采用对 Win32 API <code>LoadLibraryEx</code> 标志参数有效的任何值。 省略时,默认使用导致最安全的 DLL 加载的标志来避免诸如 DLL 劫持之类的问题。 将完整路径传递给 DLL 是确保加载正确的库和依赖项的最安全方法。 | |
− | |||
− | <code>LoadLibraryEx</code> | ||
− | |||
− | |||
− | |||
<div class="versionchanged"> | <div class="versionchanged"> | ||
− | <span class="versionmodified changed"> | + | <span class="versionmodified changed"> 3.8 版更改: </span> 添加 ''winmode'' 参数。 |
</div> | </div> | ||
− | ; < | + | ; <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">RTLD_GLOBAL</span></span> |
− | : | + | : 用作 ''mode'' 参数的标志。 在此标志不可用的平台上,它被定义为整数零。 |
− | ; < | + | ; <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">RTLD_LOCAL</span></span> |
− | : | + | : 用作 ''mode'' 参数的标志。 在不可用的平台上,它与 ''RTLD_GLOBAL'' 相同。 |
− | ; < | + | ; <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">DEFAULT_MODE</span></span> |
− | : | + | : 用于加载共享库的默认模式。 在 OSX 10.3 上,这是 ''RTLD_GLOBAL'',否则与 ''RTLD_LOCAL'' 相同。 |
− | + | 这些类的实例没有公共方法。 共享库导出的函数可以作为属性或索引进行访问。 请注意,通过属性访问函数会缓存结果,因此每次重复访问它都会返回相同的对象。 另一方面,通过索引访问它每次都会返回一个新对象: | |
− | |||
− | |||
− | |||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第1,979行: | 第1,685行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> from ctypes import CDLL |
− | + | >>> libc = CDLL("libc.so.6") # On Linux | |
− | + | >>> libc.time == libc.time | |
True | True | ||
− | + | >>> libc['time'] == libc['time'] | |
− | False</ | + | False</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 以下公共属性可用,它们的名称以下划线开头,以免与导出的函数名称冲突: | |
− | |||
− | ; < | + | ; <span class="sig-prename descclassname"><span class="pre">PyDLL.</span></span><span class="sig-name descname"><span class="pre">_handle</span></span> |
− | : | + | : 用于访问库的系统句柄。 |
− | ; < | + | ; <span class="sig-prename descclassname"><span class="pre">PyDLL.</span></span><span class="sig-name descname"><span class="pre">_name</span></span> |
− | : | + | : 在构造函数中传递的库的名称。 |
− | + | 共享库也可以通过使用预制对象之一加载,这些对象是 [[#ctypes.LibraryLoader|LibraryLoader]] 类的实例,通过调用 <code>LoadLibrary()</code> 方法,或通过检索库作为加载器的属性实例。 | |
− | |||
− | <code>LoadLibrary()</code> | ||
− | |||
<dl> | <dl> | ||
− | <dt>''class'' < | + | <dt>''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">LibraryLoader</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">dlltype</span></span>''<span class="sig-paren">)</span></dt> |
− | <dd><p> | + | <dd><p>加载共享库的类。 ''dlltype'' 应该是 [[#ctypes.CDLL|CDLL]]、[[#ctypes.PyDLL|PyDLL]]、[[#ctypes.WinDLL|WinDLL]] 或 [[#ctypes.OleDLL|OleDLL]] 类型之一。</p> |
− | [[#ctypes.CDLL| | + | <p><code>__getattr__()</code> 具有特殊的行为:它允许通过访问共享库作为库加载器实例的属性来加载它。 结果被缓存,因此重复的属性访问每次都返回相同的库。</p> |
− | <p><code>__getattr__()</code> | ||
− | |||
− | |||
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-name descname"><span class="pre">LoadLibrary</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">name</span></span>''<span class="sig-paren">)</span></dt> |
− | <dd><p> | + | <dd><p>将共享库加载到进程中并返回。 此方法始终返回库的新实例。</p></dd></dl> |
− | |||
</dd></dl> | </dd></dl> | ||
− | + | 这些预制库加载器可用: | |
− | ; < | + | ; <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">cdll</span></span> |
− | : | + | : 创建 [[#ctypes.CDLL|CDLL]] 实例。 |
− | ; < | + | ; <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">windll</span></span> |
− | : | + | : 仅限 Windows:创建 [[#ctypes.WinDLL|WinDLL]] 实例。 |
− | ; < | + | ; <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">oledll</span></span> |
− | : | + | : 仅限 Windows:创建 [[#ctypes.OleDLL|OleDLL]] 实例。 |
− | ; < | + | ; <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">pydll</span></span> |
− | : | + | : 创建 [[#ctypes.PyDLL|PyDLL]] 实例。 |
− | + | 为了直接访问 C Python api,可以使用现成的 Python 共享库对象: | |
− | |||
− | ; < | + | ; <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">pythonapi</span></span> |
− | : | + | : [[#ctypes.PyDLL|PyDLL]] 的一个实例,将 Python C API 函数公开为属性。 请注意,所有这些函数都假定返回 C <span class="xref c c-texpr">int</span>,这当然并不总是正确的,因此您必须分配正确的 <code>restype</code> 属性才能使用这些函数。 |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
第2,053行: | 第1,738行: | ||
<span id="ctypes-foreign-functions"></span> | <span id="ctypes-foreign-functions"></span> | ||
− | === | + | === 外部函数 === |
− | + | 如上一节所述,外部函数可以作为加载的共享库的属性进行访问。 以这种方式创建的函数对象默认接受任意数量的参数,接受任何 ctypes 数据实例作为参数,并返回库加载器指定的默认结果类型。 它们是私有类的实例: | |
− | |||
− | |||
− | |||
− | |||
<dl> | <dl> | ||
− | <dt>''class'' < | + | <dt>''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">_FuncPtr</span></span></dt> |
− | <dd><p> | + | <dd><p>C 可调用外部函数的基类。</p> |
− | <p> | + | <p>外部函数的实例也是 C 兼容的数据类型; 它们代表 C 函数指针。</p> |
− | + | <p>这种行为可以通过分配给外部函数对象的特殊属性来定制。</p> | |
− | <p> | ||
− | |||
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-name descname"><span class="pre">restype</span></span></dt> |
− | <dd><p> | + | <dd><p>分配一个 ctypes 类型来指定外部函数的结果类型。 将 <code>None</code> 用于 <span class="xref c c-texpr">void</span>,该函数不返回任何内容。</p> |
− | + | <p>可以分配一个不是 ctypes 类型的可调用 Python 对象,在这种情况下,假定函数返回 C <span class="xref c c-texpr">int</span>,并且将使用此整数调用可调用对象,允许进一步处理或错误检查。 不推荐使用它,为了更灵活的后处理或错误检查,请使用 ctypes 数据类型作为 [[#ctypes._FuncPtr.restype|restype]] 并将可调用对象分配给 [[#ctypes._FuncPtr.errcheck|errcheck]] 属性。</p></dd></dl> | |
− | <p> | ||
− | |||
− | |||
− | |||
− | |||
− | [[#ctypes._FuncPtr.restype| | ||
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-name descname"><span class="pre">argtypes</span></span></dt> |
− | <dd><p> | + | <dd><p>分配一个 ctypes 类型的元组来指定函数接受的参数类型。 使用 <code>stdcall</code> 调用约定的函数只能使用与此元组长度相同数量的参数来调用; 使用 C 调用约定的函数也接受额外的、未指定的参数。</p> |
− | + | <p>当调用外部函数时,每个实参都传递给 [[#ctypes._FuncPtr.argtypes|argtypes]] 元组中项的 <code>from_param()</code> 类方法,该方法允许将实参适配到外部函数接受。 例如, [[#ctypes._FuncPtr.argtypes|argtypes]] 元组中的 [[#ctypes.c_char_p|c_char_p]] 项将使用 ctypes 转换规则将作为参数传递的字符串转换为字节对象。</p> | |
− | + | <p>新:现在可以将项目放入不是 ctypes 类型的 argtypes 中,但每个项目必须有一个 <code>from_param()</code> 方法,该方法返回一个可用作参数的值(整数、字符串、ctypes 实例)。 这允许定义可以适应自定义对象作为函数参数的适配器。</p></dd></dl> | |
− | |||
− | |||
− | <p> | ||
− | <code>from_param()</code> | ||
− | |||
− | |||
− | |||
− | |||
− | <p> | ||
− | |||
− | |||
− | |||
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-name descname"><span class="pre">errcheck</span></span></dt> |
− | <dd><p> | + | <dd><p>为该属性分配一个 Python 函数或另一个可调用函数。 将使用三个或更多参数调用可调用对象:</p> |
− | |||
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-name descname"><span class="pre">callable</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">result</span></span>'', ''<span class="n"><span class="pre">func</span></span>'', ''<span class="n"><span class="pre">arguments</span></span>''<span class="sig-paren">)</span></dt> |
− | <dd><p>''result'' | + | <dd><p>''result'' 是外部函数返回的内容,由 <code>restype</code> 属性指定。</p> |
− | <code>restype</code> | + | <p>''func'' 是外部函数对象本身,这允许重用相同的可调用对象来检查或后处理多个函数的结果。</p> |
− | <p>''func'' | + | <p>''arguments'' 是一个包含最初传递给函数调用的参数的元组,这允许对所用参数的行为进行专门化。</p></dd></dl> |
− | |||
− | |||
− | <p>''arguments'' | ||
− | |||
− | |||
− | <p> | + | <p>此函数返回的对象将从外部函数调用返回,但如果外部函数调用失败,它也可以检查结果值并引发异常。</p></dd></dl> |
− | |||
− | |||
</dd></dl> | </dd></dl> | ||
− | ; | + | ; ''<span class="pre">exception</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">ArgumentError</span></span> |
− | + | : 当外部函数调用无法转换传递的参数之一时,会引发此异常。 | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | < | ||
第2,134行: | 第1,778行: | ||
<span id="ctypes-function-prototypes"></span> | <span id="ctypes-function-prototypes"></span> | ||
− | === | + | === 函数原型 === |
− | + | 也可以通过实例化函数原型来创建外部函数。 函数原型类似于 C 中的函数原型; 它们描述了一个函数(返回类型、参数类型、调用约定)而不定义实现。 工厂函数必须使用所需的结果类型和函数的参数类型来调用,并且可以用作装饰器工厂,因此可以通过 <code>@wrapper</code> 语法应用于函数。 有关示例,请参阅 [[#ctypes-callback-functions|回调函数]] 。 | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | ; < | + | ; <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">CFUNCTYPE</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">restype</span></span>'', ''<span class="o"><span class="pre">*</span></span><span class="n"><span class="pre">argtypes</span></span>'', ''<span class="n"><span class="pre">use_errno</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">False</span></span>'', ''<span class="n"><span class="pre">use_last_error</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">False</span></span>''<span class="sig-paren">)</span> |
− | : | + | : 返回的函数原型创建使用标准 C 调用约定的函数。 该函数将在调用期间释放 GIL。 如果''use_errno''设置为true,系统[[../errno#module-errno|errno]]变量的ctypes私有副本在调用前后与真实的[[../errno#module-errno|errno]]值交换; ''use_last_error'' 对 Windows 错误代码执行相同操作。 |
− | ; < | + | ; <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">WINFUNCTYPE</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">restype</span></span>'', ''<span class="o"><span class="pre">*</span></span><span class="n"><span class="pre">argtypes</span></span>'', ''<span class="n"><span class="pre">use_errno</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">False</span></span>'', ''<span class="n"><span class="pre">use_last_error</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">False</span></span>''<span class="sig-paren">)</span> |
− | : | + | : 仅限 Windows:返回的函数原型创建使用 <code>stdcall</code> 调用约定的函数,但在 Windows CE 上除外,其中 [[#ctypes.WINFUNCTYPE|WINFUNCTYPE()]] 与 [[#ctypes.CFUNCTYPE|CFUNCTYPE()]] 相同。 该函数将在调用期间释放 GIL。 ''use_errno'' 和 ''use_last_error'' 含义同上。 |
− | ; < | + | ; <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">PYFUNCTYPE</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">restype</span></span>'', ''<span class="o"><span class="pre">*</span></span><span class="n"><span class="pre">argtypes</span></span>''<span class="sig-paren">)</span> |
− | : | + | : 返回的函数原型创建使用 Python 调用约定的函数。 该函数将 ''不'' 在调用过程中释放 GIL。 |
− | + | 这些工厂函数创建的函数原型可以通过不同的方式实例化,具体取决于调用中参数的类型和数量: | |
− | |||
<blockquote><div> | <blockquote><div> | ||
− | ; < | + | ; <span class="sig-name descname"><span class="pre">prototype</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">address</span></span>''<span class="sig-paren">)</span> |
− | : | + | : 返回指定地址处的外部函数,该函数必须是整数。 |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">prototype</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">callable</span></span>''<span class="sig-paren">)</span> |
− | : | + | : 从 Python ''callable'' 创建 C 可调用函数(回调函数)。 |
− | ; < | + | ; <span class="sig-name descname"><span class="pre">prototype</span></span><span class="sig-paren">(</span>''<span class="pre">func_spec</span>''<span class="optional">[</span>, ''<span class="pre">paramflags</span>''<span class="optional">]</span><span class="sig-paren">)</span> |
− | : | + | : 返回共享库导出的外部函数。 ''func_spec'' 必须是一个二元组 <code>(name_or_ordinal, library)</code>。 第一项是作为字符串的导出函数的名称,或者作为小整数的导出函数的序数。 第二项是共享库实例。 |
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-name descname"><span class="pre">prototype</span></span><span class="sig-paren">(</span>''<span class="pre">vtbl_index</span>'', ''<span class="pre">name</span>''<span class="optional">[</span>, ''<span class="pre">paramflags</span>''<span class="optional">[</span>, ''<span class="pre">iid</span>''<span class="optional">]</span><span class="optional">]</span><span class="sig-paren">)</span></dt> |
− | <dd><p> | + | <dd><p>返回将调用 COM 方法的外部函数。 ''vtbl_index''是虚函数表的索引,一个小的非负整数。 ''name'' 是 COM 方法的名称。 ''iid'' 是一个可选的指向接口标识符的指针,用于扩展错误报告。</p> |
− | + | <p>COM 方法使用特殊的调用约定:除了 <code>argtypes</code> 元组中指定的那些参数之外,它们还需要一个指向 COM 接口的指针作为第一个参数。</p></dd></dl> | |
− | |||
− | |||
− | <p>COM | ||
− | |||
− | |||
− | + | 可选的 ''paramflags'' 参数创建具有比上述功能更多功能的外部函数包装器。 | |
− | |||
− | ''paramflags'' | + | ''paramflags'' 必须是与 <code>argtypes</code> 长度相同的元组。 |
− | + | 此元组中的每一项都包含有关参数的更多信息,它必须是包含一项、两项或三项的元组。 | |
− | |||
− | + | 第一项是一个整数,包含参数的方向标志组合: | |
− | |||
<blockquote><div> | <blockquote><div> | ||
; 1 | ; 1 | ||
− | : | + | : 指定函数的输入参数。 |
; 2 | ; 2 | ||
− | : | + | : 输出参数。 外部函数填充一个值。 |
; 4 | ; 4 | ||
− | : | + | : 默认为整数零的输入参数。 |
</div></blockquote> | </div></blockquote> | ||
− | + | 可选的第二项是字符串形式的参数名称。 如果指定了此项,则可以使用命名参数调用外部函数。 | |
− | |||
− | + | 可选的第三项是该参数的默认值。 | |
</div></blockquote> | </div></blockquote> | ||
− | + | 此示例演示如何包装 Windows <code>MessageBoxW</code> 函数,以便它支持默认参数和命名参数。 windows 头文件中的 C 声明是这样的: | |
− | |||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第2,214行: | 第1,840行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">WINUSERAPI int WINAPI |
MessageBoxW( | MessageBoxW( | ||
HWND hWnd, | HWND hWnd, | ||
LPCWSTR lpText, | LPCWSTR lpText, | ||
LPCWSTR lpCaption, | LPCWSTR lpCaption, | ||
− | UINT uType);</ | + | UINT uType);</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 这是 [[#module-ctypes|ctypes]] 的包装: | |
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第2,230行: | 第1,856行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> from ctypes import c_int, WINFUNCTYPE, windll |
− | + | >>> from ctypes.wintypes import HWND, LPCWSTR, UINT | |
− | + | >>> prototype = WINFUNCTYPE(c_int, HWND, LPCWSTR, LPCWSTR, UINT) | |
− | + | >>> paramflags = (1, "hwnd", 0), (1, "text", "Hi"), (1, "caption", "Hello from ctypes"), (1, "flags", 0) | |
− | + | >>> MessageBox = prototype(("MessageBoxW", windll.user32), paramflags)</syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | <code>MessageBox</code> 外部函数现在可以通过以下方式调用: | |
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第2,245行: | 第1,871行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> MessageBox() |
− | + | >>> MessageBox(text="Spam, spam, spam") | |
− | + | >>> MessageBox(flags=2, text="foo bar")</syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 第二个示例演示输出参数。 win32 <code>GetWindowRect</code> 函数通过将它们复制到调用者必须提供的 <code>RECT</code> 结构中来检索指定窗口的尺寸。 这是 C 声明: | |
− | |||
− | <code>RECT</code> | ||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第2,260行: | 第1,884行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">WINUSERAPI BOOL WINAPI |
GetWindowRect( | GetWindowRect( | ||
HWND hWnd, | HWND hWnd, | ||
− | LPRECT lpRect);</ | + | LPRECT lpRect);</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 这是 [[#module-ctypes|ctypes]] 的包装: | |
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第2,274行: | 第1,898行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> from ctypes import POINTER, WINFUNCTYPE, windll, WinError |
− | + | >>> from ctypes.wintypes import BOOL, HWND, RECT | |
− | + | >>> prototype = WINFUNCTYPE(BOOL, HWND, POINTER(RECT)) | |
− | + | >>> paramflags = (1, "hwnd"), (2, "lprect") | |
− | + | >>> GetWindowRect = prototype(("GetWindowRect", windll.user32), paramflags) | |
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 如果有一个输出参数值,带有输出参数的函数将自动返回输出参数值,或者当有多个输出参数值时返回一个包含输出参数值的元组,因此 GetWindowRect 函数现在在调用时返回一个 RECT 实例。 | |
− | |||
− | |||
− | RECT | ||
− | + | 输出参数可以结合<code>errcheck</code>协议进行进一步的输出处理和错误检查。 win32 <code>GetWindowRect</code> api 函数返回一个 <code>BOOL</code> 来表示成功或失败,因此该函数可以进行错误检查,并在 api 调用失败时引发异常: | |
− | |||
− | |||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第2,298行: | 第1,916行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> def errcheck(result, func, args): |
... if not result: | ... if not result: | ||
... raise WinError() | ... raise WinError() | ||
... return args | ... return args | ||
... | ... | ||
− | + | >>> GetWindowRect.errcheck = errcheck | |
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
</div> | </div> | ||
− | + | 如果 <code>errcheck</code> 函数返回它接收到的参数元组不变,[[#module-ctypes|ctypes]] 继续它对输出参数所做的正常处理。 如果你想返回一个窗口坐标元组而不是一个 <code>RECT</code> 实例,你可以检索函数中的字段并返回它们,正常处理将不再发生: | |
− | |||
− | |||
− | <code>RECT</code> | ||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第2,319行: | 第1,933行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">>>> def errcheck(result, func, args): |
... if not result: | ... if not result: | ||
... raise WinError() | ... raise WinError() | ||
第2,325行: | 第1,939行: | ||
... return rc.left, rc.top, rc.bottom, rc.right | ... return rc.left, rc.top, rc.bottom, rc.right | ||
... | ... | ||
− | + | >>> GetWindowRect.errcheck = errcheck | |
− | + | >>></syntaxhighlight> | |
</div> | </div> | ||
第2,336行: | 第1,950行: | ||
<span id="ctypes-utility-functions"></span> | <span id="ctypes-utility-functions"></span> | ||
− | === | + | === 实用功能 === |
− | < | + | ; <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">addressof</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">obj</span></span>''<span class="sig-paren">)</span> |
− | < | + | : 以整数形式返回内存缓冲区的地址。 ''obj'' 必须是 ctypes 类型的实例。 |
− | |||
− | |||
− | |||
− | ; < | + | ; <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">alignment</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">obj_or_type</span></span>''<span class="sig-paren">)</span> |
− | : | + | : 返回 ctypes 类型的对齐要求。 ''obj_or_type'' 必须是 ctypes 类型或实例。 |
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">byref</span></span><span class="sig-paren">(</span>''<span class="pre">obj</span>''<span class="optional">[</span>, ''<span class="pre">offset</span>''<span class="optional">]</span><span class="sig-paren">)</span></dt> |
− | <dd><p> | + | <dd><p>返回一个指向 ''obj'' 的轻量级指针,它必须是一个 ctypes 类型的实例。 ''offset'' 默认为零,并且必须是将添加到内部指针值的整数。</p> |
− | ctypes | + | <p><code>byref(obj, offset)</code> 对应这个 C 代码:</p> |
− | |||
− | <p><code>byref(obj, offset)</code> | ||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">(((char *)&obj) + offset)</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | <p> | + | <p>返回的对象只能用作外部函数调用参数。 它的行为类似于 <code>pointer(obj)</code>,但构建速度要快得多。</p></dd></dl> |
− | |||
− | ; < | + | ; <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">cast</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">obj</span></span>'', ''<span class="n"><span class="pre">type</span></span>''<span class="sig-paren">)</span> |
− | : | + | : 此函数类似于 C 中的强制转换运算符。 它返回一个 ''type'' 的新实例,它指向与 ''obj'' 相同的内存块。 ''type'' 必须是指针类型,''obj'' 必须是可以解释为指针的对象。 |
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">create_string_buffer</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">init_or_size</span></span>'', ''<span class="n"><span class="pre">size</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>''<span class="sig-paren">)</span></dt> |
− | <dd><p> | + | <dd><p>此函数创建一个可变字符缓冲区。 返回的对象是 [[#ctypes.c_char|c_char]] 的 ctypes 数组。</p> |
− | + | <p>''init_or_size'' 必须是指定数组大小的整数,或用于初始化数组项的字节对象。</p> | |
− | <p>''init_or_size'' | + | <p>如果将字节对象指定为第一个参数,则缓冲区将比其长度大一个项目,以便数组中的最后一个元素是 NUL 终止字符。 一个整数可以作为第二个参数传递,如果不应该使用字节的长度,它允许指定数组的大小。</p></dd></dl> |
− | |||
− | <p> | ||
− | |||
− | |||
− | |||
− | |||
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">create_unicode_buffer</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">init_or_size</span></span>'', ''<span class="n"><span class="pre">size</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>''<span class="sig-paren">)</span></dt> |
− | <dd><p> | + | <dd><p>此函数创建一个可变的 unicode 字符缓冲区。 返回的对象是 [[#ctypes.c_wchar|c_wchar]] 的 ctypes 数组。</p> |
− | + | <p>''init_or_size'' 必须是指定数组大小的整数,或用于初始化数组项的字符串。</p> | |
− | <p>''init_or_size'' | + | <p>如果将字符串指定为第一个参数,则缓冲区将比字符串的长度大一个项目,以便数组中的最后一个元素是 NUL 终止字符。 一个整数可以作为第二个参数传递,如果不应该使用字符串的长度,它允许指定数组的大小。</p></dd></dl> |
− | |||
− | <p> | ||
− | |||
− | NUL | ||
− | |||
− | |||
− | |||
− | ; < | + | ; <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">DllCanUnloadNow</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span> |
− | : | + | : 仅适用于 Windows:此函数是一个钩子,它允许使用 ctypes 实现进程内 COM 服务器。 它是从 _ctypes 扩展 dll 导出的 DllCanUnloadNow 函数调用的。 |
− | ; < | + | ; <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">DllGetClassObject</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span> |
− | : | + | : 仅适用于 Windows:此函数是一个钩子,它允许使用 ctypes 实现进程内 COM 服务器。 它是从 <code>_ctypes</code> 扩展 dll 导出的 DllGetClassObject 函数调用的。 |
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-prename descclassname"><span class="pre">ctypes.util.</span></span><span class="sig-name descname"><span class="pre">find_library</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">name</span></span>''<span class="sig-paren">)</span></dt> |
− | <dd><p> | + | <dd><p>尝试查找库并返回路径名。 ''name'' 是没有任何前缀的库名,如 <code>lib</code>,后缀如 <code>.so</code>, <code>.dylib</code> 或版本号(这是用于 posix 链接器的形式选项 <code>-l</code>)。 如果找不到库,则返回 <code>None</code>。</p> |
− | + | <p>确切的功能取决于系统。</p></dd></dl> | |
− | |||
− | |||
− | <p> | ||
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-prename descclassname"><span class="pre">ctypes.util.</span></span><span class="sig-name descname"><span class="pre">find_msvcrt</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span></dt> |
− | <dd><p> | + | <dd><p>仅限 Windows:返回 Python 和扩展模块使用的 VC 运行时库的文件名。 如果无法确定库的名称,则返回 <code>None</code>。</p> |
− | + | <p>如果您需要释放内存,例如,由调用 <code>free(void *)</code> 的扩展模块分配,那么使用分配内存的同一库中的函数很重要。</p></dd></dl> | |
− | |||
− | <p> | ||
− | |||
− | |||
− | ; < | + | ; <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">FormatError</span></span><span class="sig-paren">(</span><span class="optional">[</span>''<span class="pre">code</span>''<span class="optional">]</span><span class="sig-paren">)</span> |
− | : | + | : 仅限 Windows:返回错误代码 ''code'' 的文本描述。 如果未指定错误代码,则通过调用 Windows api 函数 GetLastError 使用最后一个错误代码。 |
− | ; < | + | ; <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">GetLastError</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span> |
− | : Windows | + | : 仅限 Windows:返回 Windows 在调用线程中设置的最后一个错误代码。 此函数直接调用Windows GetLastError() 函数,它不返回错误代码的ctypes-private 副本。 |
− | < | + | ; <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">get_errno</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span> |
− | < | + | : 返回调用线程中系统 [[../errno#module-errno|errno]] 变量的 ctypes-private 副本的当前值。 |
− | |||
− | [[../errno#module-errno| | ||
− | |||
− | < | + | ; <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">get_last_error</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span> |
− | < | + | : 仅限 Windows:返回调用线程中系统 <code>LastError</code> 变量的 ctypes-private 副本的当前值。 |
− | < | ||
− | |||
− | |||
− | ; < | + | ; <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">memmove</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">dst</span></span>'', ''<span class="n"><span class="pre">src</span></span>'', ''<span class="n"><span class="pre">count</span></span>''<span class="sig-paren">)</span> |
− | : | + | : 与标准 C memmove 库函数相同:将 ''count'' 个字节从 ''src'' 复制到 ''dst''。 ''dst'' 和 ''src'' 必须是可以转换为指针的整数或 ctypes 实例。 |
− | ; < | + | ; <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">memset</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">dst</span></span>'', ''<span class="n"><span class="pre">c</span></span>'', ''<span class="n"><span class="pre">count</span></span>''<span class="sig-paren">)</span> |
− | : | + | : 与标准 C memset 库函数相同:用 ''count'' 个字节的值 ''c'' 填充地址 ''dst'' 处的内存块。 ''dst'' 必须是指定地址或 ctypes 实例的整数。 |
− | ; < | + | ; <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">POINTER</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">type</span></span>''<span class="sig-paren">)</span> |
− | : | + | : 这个工厂函数创建并返回一个新的 ctypes 指针类型。 指针类型在内部被缓存和重用,所以重复调用这个函数很便宜。 ''type'' 必须是 ctypes 类型。 |
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">pointer</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">obj</span></span>''<span class="sig-paren">)</span></dt> |
− | <dd><p> | + | <dd><p>此函数创建一个新的指针实例,指向 ''obj''。 返回的对象的类型为 <code>POINTER(type(obj))</code>。</p> |
− | + | <p>注意:如果您只想将指向对象的指针传递给外部函数调用,您应该使用 <code>byref(obj)</code>,它要快得多。</p></dd></dl> | |
− | <p> | ||
− | |||
− | ; < | + | ; <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">resize</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">obj</span></span>'', ''<span class="n"><span class="pre">size</span></span>''<span class="sig-paren">)</span> |
− | : | + | : 此函数调整 ''obj'' 的内部内存缓冲区大小,它必须是 ctypes 类型的实例。 不可能使缓冲区小于对象类型的原始大小,如 <code>sizeof(type(obj))</code> 给出的,但可以扩大缓冲区。 |
− | < | + | ; <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">set_errno</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">value</span></span>''<span class="sig-paren">)</span> |
− | < | + | : 将调用线程中系统 [[../errno#module-errno|errno]] 变量的 ctypes-private 副本的当前值设置为 ''value'' 并返回之前的值。 |
− | |||
− | |||
− | |||
− | < | + | ; <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">set_last_error</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">value</span></span>''<span class="sig-paren">)</span> |
− | < | + | : 仅限 Windows:将调用线程中系统 <code>LastError</code> 变量的 ctypes-private 副本的当前值设置为 ''value'' 并返回先前的值。 |
− | |||
− | <code>LastError</code> | ||
− | |||
− | |||
− | ; < | + | ; <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">sizeof</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">obj_or_type</span></span>''<span class="sig-paren">)</span> |
− | : | + | : 返回 ctypes 类型或实例内存缓冲区的大小(以字节为单位)。 与 C <code>sizeof</code> 运算符相同。 |
− | < | + | ; <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">string_at</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">address</span></span>'', ''<span class="n"><span class="pre">size</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">-</span> <span class="pre">1</span></span>''<span class="sig-paren">)</span> |
− | < | + | : 此函数返回从内存地址 ''address'' 开始的 C 字符串作为字节对象。 如果指定了大小,则将其用作大小,否则假定字符串以零结尾。 |
− | |||
− | |||
− | |||
− | |||
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">WinError</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">code</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>'', ''<span class="n"><span class="pre">descr</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span>''<span class="sig-paren">)</span></dt> |
− | <dd><p> | + | <dd><p>仅限 Windows:这个函数可能是 ctypes 中命名最差的东西。 它创建了一个 OSError 实例。 如果未指定 ''code'',则调用 <code>GetLastError</code> 以确定错误代码。 如果未指定 ''descr'',则调用 [[#ctypes.FormatError|FormatError()]] 以获取错误的文本描述。</p> |
− | |||
− | <code>GetLastError</code> | ||
− | |||
− | |||
<div class="versionchanged"> | <div class="versionchanged"> | ||
− | <p><span class="versionmodified changed"> | + | <p><span class="versionmodified changed"> 3.3 版更改: </span> 曾经创建 [[../exceptions#WindowsError|WindowsError]] 的实例。</p> |
</div></dd></dl> | </div></dd></dl> | ||
− | < | + | ; <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">wstring_at</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">address</span></span>'', ''<span class="n"><span class="pre">size</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">-</span> <span class="pre">1</span></span>''<span class="sig-paren">)</span> |
− | < | + | : 此函数以字符串形式返回从内存地址 ''address'' 开始的宽字符串。 如果指定了 ''size'',则将其用作字符串的字符数,否则假定字符串以零结尾。 |
− | |||
− | |||
− | |||
− | |||
− | |||
第2,502行: | 第2,062行: | ||
<span id="ctypes-data-types"></span> | <span id="ctypes-data-types"></span> | ||
− | === | + | === 数据类型 === |
<dl> | <dl> | ||
− | <dt>''class'' < | + | <dt>''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">_CData</span></span></dt> |
− | <dd><p> | + | <dd><p>这个非公共类是所有 ctypes 数据类型的公共基类。 除其他外,所有 ctypes 类型实例都包含一个存储 C 兼容数据的内存块; 内存块的地址由 [[#ctypes.addressof|addressof()]] 辅助函数返回。 另一个实例变量公开为 [[#ctypes._CData._objects|_objects]]; 这包含其他需要保持活动状态的 Python 对象,以防内存块包含指针。</p> |
− | + | <p>ctypes数据类型的常用方法,这些都是类方法(准确的说是[[../../glossary#term-metaclass|元类]]的方法):</p> | |
− | |||
− | [[#ctypes.addressof| | ||
− | [[#ctypes._CData._objects| | ||
− | |||
− | <p> | ||
− | |||
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-name descname"><span class="pre">from_buffer</span></span><span class="sig-paren">(</span>''<span class="pre">source</span>''<span class="optional">[</span>, ''<span class="pre">offset</span>''<span class="optional">]</span><span class="sig-paren">)</span></dt> |
− | <dd><p> | + | <dd><p>此方法返回一个 ctypes 实例,该实例共享 ''source'' 对象的缓冲区。 ''source'' 对象必须支持可写缓冲区接口。 可选的 ''offset'' 参数以字节为单位指定源缓冲区的偏移量; 默认为零。 如果源缓冲区不够大,则会引发 [[../exceptions#ValueError|ValueError]]。</p></dd></dl> |
− | ''source'' | ||
− | |||
− | |||
− | |||
− | |||
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-name descname"><span class="pre">from_buffer_copy</span></span><span class="sig-paren">(</span>''<span class="pre">source</span>''<span class="optional">[</span>, ''<span class="pre">offset</span>''<span class="optional">]</span><span class="sig-paren">)</span></dt> |
− | <dd><p> | + | <dd><p>此方法创建一个 ctypes 实例,从必须可读的 ''source'' 对象缓冲区复制缓冲区。 可选的 ''offset'' 参数以字节为单位指定源缓冲区的偏移量; 默认为零。 如果源缓冲区不够大,则会引发 [[../exceptions#ValueError|ValueError]]。</p></dd></dl> |
− | ''source'' | ||
− | |||
− | |||
− | |||
− | |||
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-name descname"><span class="pre">from_address</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">address</span></span>''<span class="sig-paren">)</span></dt> |
− | <dd><p> | + | <dd><p>此方法使用 ''address'' 指定的内存返回一个 ctypes 类型实例,该内存必须是整数。</p></dd></dl> |
− | ''address'' | ||
− | |||
− | |||
− | |||
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-name descname"><span class="pre">from_param</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">obj</span></span>''<span class="sig-paren">)</span></dt> |
− | <dd><p> | + | <dd><p>此方法使 ''obj'' 适应 ctypes 类型。 当类型存在于外部函数的 <code>argtypes</code> 元组中时,使用外部函数调用中使用的实际对象调用它; 它必须返回一个可以用作函数调用参数的对象。</p> |
− | + | <p>所有 ctypes 数据类型都有此类方法的默认实现,如果它是该类型的实例,则通常返回 ''obj''。 有些类型也接受其他对象。</p></dd></dl> | |
− | |||
− | |||
− | <p> | ||
− | |||
− | |||
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-name descname"><span class="pre">in_dll</span></span><span class="sig-paren">(</span>''<span class="n"><span class="pre">library</span></span>'', ''<span class="n"><span class="pre">name</span></span>''<span class="sig-paren">)</span></dt> |
− | <dd><p> | + | <dd><p>此方法返回共享库导出的 ctypes 类型实例。 ''name''为导出数据的符号名,''library''为加载的共享库。</p></dd></dl> |
− | |||
− | |||
− | <p> | + | <p>ctypes 数据类型的常见实例变量:</p> |
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-name descname"><span class="pre">_b_base_</span></span></dt> |
− | <dd><p> | + | <dd><p>有时 ctypes 数据实例不拥有它们包含的内存块,而是共享基础对象的部分内存块。 [[#ctypes._CData._b_base_|_b_base_]] 只读成员是拥有内存块的根 ctypes 对象。</p></dd></dl> |
− | |||
− | [[#ctypes._CData._b_base_| | ||
− | |||
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-name descname"><span class="pre">_b_needsfree_</span></span></dt> |
− | <dd><p> | + | <dd><p>当 ctypes 数据实例本身分配了内存块时,这个只读变量为真,否则为假。</p></dd></dl> |
− | |||
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-name descname"><span class="pre">_objects</span></span></dt> |
− | <dd><p> | + | <dd><p>该成员是 <code>None</code> 或包含需要保持活动状态的 Python 对象的字典,以便内存块内容保持有效。 此对象仅用于调试; 永远不要修改这本词典的内容。</p></dd></dl> |
− | |||
− | |||
− | |||
</dd></dl> | </dd></dl> | ||
第2,582行: | 第2,108行: | ||
<span id="id1"></span> | <span id="id1"></span> | ||
− | === | + | === 基本数据类型 === |
<dl> | <dl> | ||
− | <dt>''class'' < | + | <dt>''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">_SimpleCData</span></span></dt> |
− | <dd><p> | + | <dd><p>这个非公共类是所有基本 ctypes 数据类型的基类。 在这里提到它是因为它包含基本 ctypes 数据类型的公共属性。 [[#ctypes._SimpleCData|_SimpleCData]] 是 [[#ctypes._CData|_CData]] 的子类,所以继承了它们的方法和属性。 现在可以腌制不包含也不包含指针的 ctypes 数据类型。</p> |
− | + | <p>实例有一个属性:</p> | |
− | |||
− | [[#ctypes._CData| | ||
− | |||
− | <p> | ||
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-name descname"><span class="pre">value</span></span></dt> |
− | <dd><p> | + | <dd><p>该属性包含实例的实际值。 对于整数和指针类型,它是一个整数,对于字符类型,它是一个单字符字节对象或字符串,对于字符指针类型,它是一个 Python 字节对象或字符串。</p> |
− | + | <p>当从 ctypes 实例中检索 <code>value</code> 属性时,通常每次都会返回一个新对象。 [[#module-ctypes|ctypes]] ''不''实现原对象返回,总是构造一个新对象。 对于所有其他 ctypes 对象实例也是如此。</p></dd></dl> | |
− | |||
− | Python | ||
− | <p> | ||
− | |||
− | |||
− | |||
</dd></dl> | </dd></dl> | ||
− | + | 基本数据类型在作为外部函数调用结果返回时,或者例如通过检索结构字段成员或数组项时,会透明地转换为本地 Python 类型。 换句话说,如果一个外部函数有一个 [[#ctypes.c_char_p|c_char_p]] 的 <code>restype</code>,你将总是收到一个 Python 字节对象,''而不是'' 一个 [[#ctypes.c_char_p|c_char_p]] 实例. | |
− | |||
− | |||
− | |||
− | |||
− | + | 基本数据类型的子类 ''不会'' 继承此行为。 因此,如果外部函数 <code>restype</code> 是 [[#ctypes.c_void_p|c_void_p]] 的子类,您将从函数调用中收到该子类的实例。 当然,您可以通过访问 <code>value</code> 属性来获取指针的值。 | |
− | |||
− | |||
− | |||
− | + | 这些是基本的 ctypes 数据类型: | |
− | ; ''class'' < | + | ; ''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">c_byte</span></span> |
− | : | + | : 表示 C <span class="xref c c-texpr">signed char</span> 数据类型,并将值解释为小整数。 构造函数接受一个可选的整数初始化器; 没有进行溢出检查。 |
− | ; ''class'' < | + | ; ''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">c_char</span></span> |
− | : | + | : 表示 C <span class="xref c c-texpr">char</span> 数据类型,并将值解释为单个字符。 构造函数接受一个可选的字符串初始化器,字符串的长度必须正好是一个字符。 |
− | ; ''class'' < | + | ; ''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">c_char_p</span></span> |
− | : | + | : 当它指向以零结尾的字符串时,表示 C <span class="xref c c-texpr">char*</span> 数据类型。 对于也可能指向二进制数据的通用字符指针,必须使用 <code>POINTER(c_char)</code>。 构造函数接受一个整数地址或一个字节对象。 |
− | ; ''class'' < | + | ; ''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">c_double</span></span> |
− | : | + | : 表示 C <span class="xref c c-texpr">double</span> 数据类型。 构造函数接受一个可选的浮点初始化器。 |
− | ; ''class'' < | + | ; ''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">c_longdouble</span></span> |
− | : | + | : 表示 C <span class="xref c c-texpr">long double</span> 数据类型。 构造函数接受一个可选的浮点初始化器。 在 <code>sizeof(long double) == sizeof(double)</code> 它是 [[#ctypes.c_double|c_double]] 的别名的平台上。 |
− | ; ''class'' < | + | ; ''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">c_float</span></span> |
− | : | + | : 表示 C <span class="xref c c-texpr">float</span> 数据类型。 构造函数接受一个可选的浮点初始化器。 |
− | ; ''class'' < | + | ; ''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">c_int</span></span> |
− | : | + | : 表示 C <span class="xref c c-texpr">signed int</span> 数据类型。 构造函数接受一个可选的整数初始化器; 没有进行溢出检查。 在 <code>sizeof(int) == sizeof(long)</code> 它是 [[#ctypes.c_long|c_long]] 的别名的平台上。 |
− | ; ''class'' < | + | ; ''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">c_int8</span></span> |
− | : | + | : 表示 C 8 位 <span class="xref c c-texpr">signed int</span> 数据类型。 通常是 [[#ctypes.c_byte|c_byte]] 的别名。 |
− | ; ''class'' < | + | ; ''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">c_int16</span></span> |
− | : | + | : 表示 C 16 位 <span class="xref c c-texpr">signed int</span> 数据类型。 通常是 [[#ctypes.c_short|c_short]] 的别名。 |
− | ; ''class'' < | + | ; ''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">c_int32</span></span> |
− | : | + | : 表示 C 32 位 <span class="xref c c-texpr">signed int</span> 数据类型。 通常是 [[#ctypes.c_int|c_int]] 的别名。 |
− | ; ''class'' < | + | ; ''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">c_int64</span></span> |
− | : | + | : 表示 C 64 位 <span class="xref c c-texpr">signed int</span> 数据类型。 通常是 [[#ctypes.c_longlong|c_longlong]] 的别名。 |
− | ; ''class'' < | + | ; ''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">c_long</span></span> |
− | : | + | : 表示 C <span class="xref c c-texpr">signed long</span> 数据类型。 构造函数接受一个可选的整数初始化器; 没有进行溢出检查。 |
− | ; ''class'' < | + | ; ''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">c_longlong</span></span> |
− | : | + | : 表示 C <span class="xref c c-texpr">signed long long</span> 数据类型。 构造函数接受一个可选的整数初始化器; 没有进行溢出检查。 |
− | ; ''class'' < | + | ; ''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">c_short</span></span> |
− | : | + | : 表示 C <span class="xref c c-texpr">signed short</span> 数据类型。 构造函数接受一个可选的整数初始化器; 没有进行溢出检查。 |
− | ; ''class'' < | + | ; ''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">c_size_t</span></span> |
− | : | + | : 表示 C <code>size_t</code> 数据类型。 |
<dl> | <dl> | ||
− | <dt>''class'' < | + | <dt>''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">c_ssize_t</span></span></dt> |
− | <dd><p> | + | <dd><p>表示 C <code>ssize_t</code> 数据类型。</p> |
<div class="versionadded"> | <div class="versionadded"> | ||
− | <p><span class="versionmodified added">3.2 | + | <p><span class="versionmodified added">3.2 版中的新功能。</span></p> |
</div></dd></dl> | </div></dd></dl> | ||
− | ; ''class'' < | + | ; ''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">c_ubyte</span></span> |
− | : | + | : 表示 C <span class="xref c c-texpr">unsigned char</span> 数据类型,它将值解释为小整数。 构造函数接受一个可选的整数初始化器; 没有进行溢出检查。 |
− | ; ''class'' < | + | ; ''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">c_uint</span></span> |
− | : | + | : 表示 C <span class="xref c c-texpr">unsigned int</span> 数据类型。 构造函数接受一个可选的整数初始化器; 没有进行溢出检查。 在 <code>sizeof(int) == sizeof(long)</code> 它是 [[#ctypes.c_ulong|c_ulong]] 的别名的平台上。 |
− | ; ''class'' < | + | ; ''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">c_uint8</span></span> |
− | : | + | : 表示 C 8 位 <span class="xref c c-texpr">unsigned int</span> 数据类型。 通常是 [[#ctypes.c_ubyte|c_ubyte]] 的别名。 |
− | ; ''class'' < | + | ; ''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">c_uint16</span></span> |
− | : | + | : 表示 C 16 位 <span class="xref c c-texpr">unsigned int</span> 数据类型。 通常是 [[#ctypes.c_ushort|c_ushort]] 的别名。 |
− | ; ''class'' < | + | ; ''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">c_uint32</span></span> |
− | : | + | : 表示 C 32 位 <span class="xref c c-texpr">unsigned int</span> 数据类型。 通常是 [[#ctypes.c_uint|c_uint]] 的别名。 |
− | ; ''class'' < | + | ; ''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">c_uint64</span></span> |
− | : | + | : 表示 C 64 位 <span class="xref c c-texpr"> unsigned int</span> 数据类型。 通常是 [[#ctypes.c_ulonglong|c_ulonglong]] 的别名。 |
− | ; ''class'' < | + | ; ''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">c_ulong</span></span> |
− | : | + | : 表示 C <span class="xref c c-texpr"> unsigned long</span> 数据类型。 构造函数接受一个可选的整数初始化器; 没有进行溢出检查。 |
− | ; ''class'' < | + | ; ''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">c_ulonglong</span></span> |
− | : | + | : 表示 C <span class="xref c c-texpr"> unsigned long long</span> 数据类型。 构造函数接受一个可选的整数初始化器; 没有进行溢出检查。 |
− | ; ''class'' < | + | ; ''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">c_ushort</span></span> |
− | : | + | : 表示 C <span class="xref c c-texpr"> unsigned short</span> 数据类型。 构造函数接受一个可选的整数初始化器; 没有进行溢出检查。 |
− | ; ''class'' < | + | ; ''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">c_void_p</span></span> |
− | : | + | : 表示 C <span class="xref c c-texpr">void*</span> 类型。 该值表示为整数。 构造函数接受一个可选的整数初始值设定项。 |
− | ; ''class'' < | + | ; ''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">c_wchar</span></span> |
− | : | + | : 表示 C <code>wchar_t</code> 数据类型,并将值解释为单字符 unicode 字符串。 构造函数接受一个可选的字符串初始化器,字符串的长度必须正好是一个字符。 |
− | ; ''class'' < | + | ; ''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">c_wchar_p</span></span> |
− | : | + | : 表示 C <span class="xref c c-texpr">wchar_t*</span> 数据类型,它必须是指向以零结尾的宽字符串的指针。 构造函数接受一个整数地址或字符串。 |
− | ; ''class'' < | + | ; ''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">c_bool</span></span> |
− | : | + | : 表示 C <span class="xref c c-texpr">bool</span> 数据类型(更准确地说,是 C99 中的 <span class="xref c c-texpr">_Bool</span>)。 它的值可以是 <code>True</code> 或 <code>False</code>,构造函数接受任何具有真值的对象。 |
− | ; ''class'' < | + | ; ''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">HRESULT</span></span> |
− | : | + | : 仅限 Windows:表示 <code>HRESULT</code> 值,其中包含函数或方法调用的成功或错误信息。 |
− | ; ''class'' < | + | ; ''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">py_object</span></span> |
− | : | + | : 表示 C <span class="xref c c-texpr">PyObject*</span> 数据类型。 不带参数调用它会创建一个 <code>NULL</code> <span class="xref c c-texpr">PyObject*</span> 指针。 |
− | + | <code>ctypes.wintypes</code> 模块提供了相当多的其他 Windows 特定数据类型,例如 <code>HWND</code>、<code>WPARAM</code> 或 <code>DWORD</code>。 还定义了一些有用的结构,如 <code>MSG</code> 或 <code>RECT</code>。 | |
− | |||
− | |||
第2,725行: | 第2,232行: | ||
<span id="ctypes-structured-data-types"></span> | <span id="ctypes-structured-data-types"></span> | ||
− | === | + | === 结构化数据类型 === |
− | ; ''class'' < | + | ; ''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">Union</span></span><span class="sig-paren">(</span>''<span class="o"><span class="pre">*</span></span><span class="n"><span class="pre">args</span></span>'', ''<span class="o"><span class="pre">**</span></span><span class="n"><span class="pre">kw</span></span>''<span class="sig-paren">)</span> |
− | : | + | : 本机字节顺序的联合的抽象基类。 |
− | ; ''class'' < | + | ; ''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">BigEndianStructure</span></span><span class="sig-paren">(</span>''<span class="o"><span class="pre">*</span></span><span class="n"><span class="pre">args</span></span>'', ''<span class="o"><span class="pre">**</span></span><span class="n"><span class="pre">kw</span></span>''<span class="sig-paren">)</span> |
− | : | + | : ''big endian'' 字节顺序结构的抽象基类。 |
− | ; ''class'' < | + | ; ''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">LittleEndianStructure</span></span><span class="sig-paren">(</span>''<span class="o"><span class="pre">*</span></span><span class="n"><span class="pre">args</span></span>'', ''<span class="o"><span class="pre">**</span></span><span class="n"><span class="pre">kw</span></span>''<span class="sig-paren">)</span> |
− | : | + | : ''little endian'' 字节顺序结构的抽象基类。 |
− | + | 具有非本地字节顺序的结构不能包含指针类型字段,或包含指针类型字段的任何其他数据类型。 | |
− | |||
<dl> | <dl> | ||
− | <dt>''class'' < | + | <dt>''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">Structure</span></span><span class="sig-paren">(</span>''<span class="o"><span class="pre">*</span></span><span class="n"><span class="pre">args</span></span>'', ''<span class="o"><span class="pre">**</span></span><span class="n"><span class="pre">kw</span></span>''<span class="sig-paren">)</span></dt> |
− | <dd><p> | + | <dd><p>''native'' 字节顺序结构的抽象基类。</p> |
− | <p> | + | <p>具体的结构和联合类型必须通过继承这些类型之一来创建,并且至少定义一个 [[#ctypes.Structure._fields_|_fields_]] 类变量。 [[#module-ctypes|ctypes]] 将创建 [[../../glossary#term-descriptor|描述符]] ,允许通过直接属性访问读取和写入字段。 这些是</p> |
− | |||
− | |||
− | |||
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-name descname"><span class="pre">_fields_</span></span></dt> |
− | <dd><p> | + | <dd><p>定义结构字段的序列。 项必须是 2 元组或 3 元组。 第一项是字段名称,第二项指定字段类型; 它可以是任何 ctypes 数据类型。</p> |
− | 3 | + | <p>对于像 [[#ctypes.c_int|c_int]] 这样的整数类型字段,可以给出第三个可选项。 它必须是一个小的正整数,用于定义字段的位宽。</p> |
− | + | <p>字段名称在一个结构或联合中必须是唯一的。 未选中此项,当名称重复时,只能访问一个字段。</p> | |
− | <p> | + | <p>可以在定义 Structure 子类的 class 语句之后定义 [[#ctypes.Structure._fields_|_fields_]] 类变量 ,这允许创建直接或间接引用自身的数据类型:</p> |
− | |||
− | |||
− | <p> | ||
− | |||
− | <p> | ||
− | |||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">class List(Structure): |
pass | pass | ||
− | List._fields_ = [( | + | List._fields_ = [("pnext", POINTER(List)), |
... | ... | ||
− | ]</ | + | ]</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | <p> | + | <p>然而,[[#ctypes.Structure._fields_|_fields_]] 类变量必须在第一次使用类型之前定义(创建一个实例,在它上面调用 [[#ctypes.sizeof|sizeof()]],等等)。 稍后对 [[#ctypes.Structure._fields_|_fields_]] 类变量的赋值将引发 AttributeError。</p> |
− | + | <p>可以定义结构类型的子子类,它们继承基类的字段加上子子类中定义的 [[#ctypes.Structure._fields_|_fields_]](如果有)。</p></dd></dl> | |
− | |||
− | |||
− | <p> | ||
− | |||
− | |||
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-name descname"><span class="pre">_pack_</span></span></dt> |
− | <dd><p> | + | <dd><p>一个可选的小整数,允许覆盖实例中结构字段的对齐方式。 [[#ctypes.Structure._pack_|_pack_]] 必须在分配 [[#ctypes.Structure._fields_|_fields_]] 时已经定义,否则无效。</p></dd></dl> |
− | |||
− | |||
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-name descname"><span class="pre">_anonymous_</span></span></dt> |
− | <dd><p> | + | <dd><p>列出未命名(匿名)字段名称的可选序列。 [[#ctypes.Structure._anonymous_|_anonymous_]] 必须在分配[[#ctypes.Structure._fields_|_fields_]] 时已经定义,否则无效。</p> |
− | [[#ctypes.Structure._anonymous_| | + | <p>此变量中列出的字段必须是结构或联合类型字段。 [[#module-ctypes|ctypes]] 将在允许直接访问嵌套字段的结构类型中创建描述符,而无需创建结构或联合字段。</p> |
− | + | <p>这是一个示例类型(Windows):</p> | |
− | <p> | ||
− | [[#module-ctypes| | ||
− | |||
− | |||
− | <p> | ||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">class _U(Union): |
− | _fields_ = [( | + | _fields_ = [("lptdesc", POINTER(TYPEDESC)), |
− | ( | + | ("lpadesc", POINTER(ARRAYDESC)), |
− | ( | + | ("hreftype", HREFTYPE)] |
class TYPEDESC(Structure): | class TYPEDESC(Structure): | ||
− | _anonymous_ = ( | + | _anonymous_ = ("u",) |
− | _fields_ = [( | + | _fields_ = [("u", _U), |
− | ( | + | ("vt", VARTYPE)]</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | <p> | + | <p><code>TYPEDESC</code> 结构描述了一种 COM 数据类型,<code>vt</code> 字段指定哪一个联合字段是有效的。 由于 <code>u</code> 字段被定义为匿名字段,现在可以直接从 TYPEDESC 实例访问成员。 <code>td.lptdesc</code> 和 <code>td.u.lptdesc</code> 是等价的,但前者更快,因为它不需要创建临时联合实例:</p> |
− | |||
− | |||
− | |||
− | |||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">td = TYPEDESC() |
td.vt = VT_PTR | td.vt = VT_PTR | ||
td.lptdesc = POINTER(some_type) | td.lptdesc = POINTER(some_type) | ||
− | td.u.lptdesc = POINTER(some_type)</ | + | td.u.lptdesc = POINTER(some_type)</syntaxhighlight> |
</div> | </div> | ||
第2,832行: | 第2,311行: | ||
</div></dd></dl> | </div></dd></dl> | ||
− | <p> | + | <p>可以定义结构的子子类,它们继承基类的字段。 如果子类定义具有单独的 [[#ctypes.Structure._fields_|_fields_]] 变量,则在此指定的字段将附加到基类的字段。</p> |
− | + | <p>结构和联合构造函数接受位置参数和关键字参数。 位置参数用于按照它们出现在 [[#ctypes.Structure._fields_|_fields_]] 中的相同顺序初始化成员字段。 构造函数中的关键字参数被解释为属性赋值,因此它们将使用相同的名称初始化 [[#ctypes.Structure._fields_|_fields_]],或者为 [[#ctypes.Structure._fields_|_fields_]] 中不存在的名称创建新属性。</p></dd></dl> | |
− | [[#ctypes.Structure._fields_| | ||
− | |||
− | <p> | ||
− | |||
− | |||
− | |||
− | [[#ctypes.Structure._fields_| | ||
− | |||
第2,848行: | 第2,319行: | ||
<span id="ctypes-arrays-pointers"></span> | <span id="ctypes-arrays-pointers"></span> | ||
− | === | + | === 数组和指针 === |
<dl> | <dl> | ||
− | <dt>''class'' < | + | <dt>''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">Array</span></span><span class="sig-paren">(</span>''<span class="o"><span class="pre">*</span></span><span class="n"><span class="pre">args</span></span>''<span class="sig-paren">)</span></dt> |
− | <dd><p> | + | <dd><p>数组的抽象基类。</p> |
− | <p> | + | <p>创建具体数组类型的推荐方法是将任何 [[#module-ctypes|ctypes]] 数据类型乘以一个正整数。 或者,您可以子类化此类型并定义 [[#ctypes.Array._length_|_length_]] 和 [[#ctypes.Array._type_|_type_]] 类变量。 可以使用标准下标和切片访问来读取和写入数组元素; 对于切片读取,结果对象是 ''不是'' 本身是 [[#ctypes.Array|数组]] 。</p> |
− | [[#module-ctypes| | ||
− | |||
− | |||
− | |||
− | '' | ||
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-name descname"><span class="pre">_length_</span></span></dt> |
− | <dd><p> | + | <dd><p>一个正整数,指定数组中的元素数。 超出范围的下标会导致 [[../exceptions#IndexError|IndexError]]。 将由 [[../functions#len|len()]] 返回。</p></dd></dl> |
− | |||
− | |||
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-name descname"><span class="pre">_type_</span></span></dt> |
− | <dd><p> | + | <dd><p>指定数组中每个元素的类型。</p></dd></dl> |
− | <p> | + | <p>数组子类构造函数接受位置参数,用于按顺序初始化元素。</p></dd></dl> |
− | |||
<dl> | <dl> | ||
− | <dt>''class'' < | + | <dt>''<span class="pre">class</span>'' <span class="sig-prename descclassname"><span class="pre">ctypes.</span></span><span class="sig-name descname"><span class="pre">_Pointer</span></span></dt> |
− | <dd><p> | + | <dd><p>指针的私有抽象基类。</p> |
− | <p> | + | <p>具体的指针类型是通过使用将要指向的类型调用 [[#ctypes.POINTER|POINTER()]] 来创建的; 这是由 [[#ctypes.pointer|pointer()]] 自动完成的。</p> |
− | + | <p>如果指针指向数组,则可以使用标准下标和切片访问读取和写入其元素。 指针对象没有大小,因此 [[../functions#len|len()]] 将引发 [[../exceptions#TypeError|TypeError]]。 负下标将在 ''之前从内存中读取'' 指针(如在 C 中),超出范围的下标可能会因访问冲突而崩溃(如果你很幸运)。</p> | |
− | [[#ctypes.pointer| | ||
− | <p> | ||
− | |||
− | |||
− | |||
− | |||
− | |||
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-name descname"><span class="pre">_type_</span></span></dt> |
− | <dd><p> | + | <dd><p>指定指向的类型。</p></dd></dl> |
<dl> | <dl> | ||
− | <dt>< | + | <dt><span class="sig-name descname"><span class="pre">contents</span></span></dt> |
− | <dd><p> | + | <dd><p>返回指针指向的对象。 分配给该属性会将指针更改为指向所分配的对象。</p></dd></dl> |
− | |||
</dd></dl> | </dd></dl> | ||
第2,898行: | 第2,353行: | ||
</div> | </div> | ||
+ | |||
+ | </div> | ||
+ | <div class="clearer"> | ||
+ | |||
+ | |||
</div> | </div> | ||
− | [[Category:Python 3.9 | + | [[Category:Python 3.9 文档]] |
2021年10月31日 (日) 04:51的最新版本
ctypes — Python 的外部函数库
ctypes 是 Python 的外部函数库。 它提供与 C 兼容的数据类型,并允许调用 DLL 或共享库中的函数。 它可用于将这些库包装在纯 Python 中。
ctypes教程
注意:本教程中的代码示例使用 doctest 来确保它们确实有效。 由于某些代码示例在 Linux、Windows 或 macOS 下的行为不同,因此它们在注释中包含 doctest 指令。
注意:一些代码示例引用了 ctypes c_int 类型。 在 sizeof(long) == sizeof(int)
它是 c_long 的别名的平台上。 因此,如果您期望 c_int 打印 c_long,您不应该感到困惑——它们实际上是相同的类型。
加载动态链接库
ctypes 导出 cdll,在 Windows windll 和 oledll 对象上,用于加载动态链接库。
您可以通过将库作为这些对象的属性进行访问来加载库。 cdll 加载使用标准 cdecl
调用约定导出函数的库,而 windll 库使用 stdcall
调用约定调用函数。 oledll 也使用 stdcall
调用约定,并假设函数返回 Windows HRESULT
错误代码。 错误代码用于在函数调用失败时自动引发 OSError 异常。
3.3 版更改:Windows 错误用于引发 WindowsError,现在是 OSError 的别名。
下面是一些适用于 Windows 的示例。 请注意,msvcrt
是包含大多数标准 C 函数的 MS 标准 C 库,并使用 cdecl 调用约定:
Windows 会自动附加通常的 .dll
文件后缀。
笔记
通过 cdll.msvcrt
访问标准 C 库将使用该库的过时版本,该版本可能与 Python 使用的版本不兼容。 在可能的情况下,使用本机 Python 功能,或者导入并使用 msvcrt
模块。
在 Linux 上,需要指定文件名 包括 扩展名才能加载库,因此不能使用属性访问来加载库。 应该使用 dll 加载器的 LoadLibrary()
方法,或者您应该通过调用构造函数创建 CDLL 的实例来加载库:
从加载的 dll 访问函数
函数作为 dll 对象的属性被访问:
请注意,像 kernel32
和 user32
这样的 win32 系统 dll 通常会导出函数的 ANSI 和 UNICODE 版本。 UNICODE 版本在导出时在名称后附加了 W
,而 ANSI 版本在导出时在名称后附加了 A
。 win32 GetModuleHandle
函数返回给定模块名称的 模块句柄 ,具有以下 C 原型,并使用宏将其中之一公开为 GetModuleHandle
取决于是否定义了 UNICODE:
windll 不会尝试通过魔法选择其中之一,您必须通过显式指定 GetModuleHandleA
或 GetModuleHandleW
来访问您需要的版本,然后用字节或字符串调用它对象分别。
有时,dll 导出的函数名称不是有效的 Python 标识符,例如 "??2@YAPAXI@Z"
。 在这种情况下,您必须使用 getattr() 来检索函数:
在 Windows 上,一些 dll 不是按名称而是按顺序导出函数。 可以通过使用序号索引 dll 对象来访问这些函数:
调用函数
您可以像调用任何其他 Python 可调用函数一样调用这些函数。 此示例使用 time()
函数,该函数返回自 Unix 纪元以来以秒为单位的系统时间,以及 GetModuleHandleA()
函数,该函数返回一个 win32 模块句柄。
此示例使用 NULL
指针调用这两个函数(None
应用作 NULL
指针):
当您使用 cdecl
调用约定调用 stdcall
函数时,会引发 ValueError,反之亦然:
要找出正确的调用约定,您必须查看 C 头文件或要调用的函数的文档。
在 Windows 上,ctypes 使用 win32 结构化异常处理来防止在使用无效参数值调用函数时由于一般保护错误而导致崩溃:
然而,有足够的方法可以使 Python 与 ctypes 崩溃,所以无论如何你都应该小心。 faulthandler 模块有助于调试崩溃(例如 由错误的 C 库调用产生的分段错误)。
None
、整数、字节对象和(unicode)字符串是唯一可以在这些函数调用中直接用作参数的原生 Python 对象。 None
作为 C NULL
指针传递,字节对象和字符串作为指向包含其数据的内存块的指针传递(char* 或 wchar_t* )。 Python 整数作为平台默认的 C int 类型传递,它们的值被屏蔽以适应 C 类型。
在继续使用其他参数类型调用函数之前,我们必须了解更多关于 ctypes 数据类型的信息。
基本数据类型
ctypes 定义了一些原始的 C 兼容数据类型:
ctypes 类型 | C型 | 蟒蛇型 |
---|---|---|
c_bool
|
_布尔 | 布尔 (1) |
c_char
|
字符 | 1 个字符的字节对象 |
c_wchar
|
wchar_t
|
1 个字符的字符串 |
c_byte
|
字符 | 整数 |
c_ubyte
|
无符号的字符 | 整数 |
c_short
|
短的 | 整数 |
c_ushort
|
无符号短 | 整数 |
c_int
|
整数 | 整数 |
c_uint
|
无符号整数 | 整数 |
c_long
|
长 | 整数 |
c_ulong
|
无符号长 | 整数 |
c_longlong
|
__int64 或 长长
|
整数 |
c_ulonglong
|
unsigned __int64 或 unsigned long long | 整数 |
c_size_t
|
size_t
|
整数 |
c_ssize_t
|
ssize_t 或 Py_ssize_t
|
整数 |
c_float
|
漂浮 | 漂浮 |
c_double
|
双倍的 | 漂浮 |
c_longdouble
|
长双 | 漂浮 |
c_char_p
|
char*(NUL 终止) | 字节对象或 None
|
c_wchar_p
|
wchar_t*(NUL 终止) | 字符串或 None
|
c_void_p
|
空白* | 整数或 None
|
- 构造函数接受任何具有真值的对象。
所有这些类型都可以通过使用正确类型和值的可选初始化程序调用它们来创建:
由于这些类型是可变的,它们的值也可以在之后更改:
为指针类型 c_char_p、c_wchar_p 和 c_void_p 的实例分配一个新值会改变它们指向的 内存位置 , ]不是内存块的内容(当然不是,因为Python字节对象是不可变的):
但是,您应该小心,不要将它们传递给需要指向可变内存的指针的函数。 如果您需要可变内存块,ctypes 有一个 create_string_buffer() 函数,它以各种方式创建这些。 可以使用 raw
属性访问(或更改)当前内存块内容; 如果要以 NUL 终止的字符串形式访问它,请使用 value
属性:
create_string_buffer() 函数替换了 c_buffer()
函数(仍可用作别名),以及早期 ctypes 版本中的 c_string()
函数。 要创建包含 C 类型 wchar_t
Unicode 字符的可变内存块,请使用 create_unicode_buffer() 函数。
调用函数,续
请注意,printf 打印到真正的标准输出通道,not 到 sys.stdout,因此这些示例仅适用于控制台提示符,而不适用于 IDLE或 PythonWin:
如前所述,除整数、字符串和字节对象之外的所有 Python 类型都必须包装在其对应的 ctypes 类型中,以便将它们转换为所需的 C 数据类型:
使用您自己的自定义数据类型调用函数
您还可以自定义 ctypes 参数转换,以允许将您自己的类的实例用作函数参数。 ctypes 查找 _as_parameter_
属性并将其用作函数参数。 当然,它必须是整数、字符串或字节之一:
如果您不想将实例的数据存储在 _as_parameter_
实例变量中,您可以定义一个 属性 ,使该属性在请求时可用。
指定所需的参数类型(函数原型)
可以通过设置 argtypes
属性来指定从 DLL 导出的函数所需的参数类型。
argtypes
必须是 C 数据类型的序列(printf
函数在这里可能不是一个很好的例子,因为它根据格式字符串需要一个变量数和不同类型的参数,在另一方面,这对于试验此功能非常方便):
指定格式可以防止不兼容的参数类型(就像 C 函数的原型一样),并尝试将参数转换为有效类型:
如果您定义了自己的类并传递给函数调用,则必须实现一个 from_param()
类方法,以便它们能够在 argtypes
序列中使用它们。 from_param()
类方法接收传递给函数调用的 Python 对象,它应该进行类型检查或任何需要以确保该对象是可接受的,然后返回对象本身,它的 _as_parameter_
属性,或者在这种情况下您想作为 C 函数参数传递的任何内容。 同样,结果应该是整数、字符串、字节、ctypes 实例或具有 _as_parameter_
属性的对象。
返回类型
默认情况下,函数假定返回 C int 类型。 其他返回类型可以通过设置函数对象的restype
属性来指定。
这是一个更高级的例子,它使用 strchr
函数,它需要一个字符串指针和一个字符,并返回一个指向字符串的指针:
如果要避免上面的 ord("x")
调用,可以设置 argtypes
属性,第二个参数将从单个字符的 Python 字节对象转换为 C 字符:
如果外部函数返回整数,您还可以使用可调用的 Python 对象(例如函数或类)作为 restype
属性。 将使用 C 函数返回的 整数 调用可调用对象,并且此调用的结果将用作函数调用的结果。 这对于检查错误返回值并自动引发异常很有用:
WinError
是一个函数,它将调用 Windows FormatMessage()
api 来获取错误代码的字符串表示,而 返回 异常。 WinError
带有一个可选的错误代码参数,如果没有使用它,它会调用 GetLastError() 来检索它。
请注意,通过 errcheck
属性可以使用更强大的错误检查机制; 有关详细信息,请参阅参考手册。
传递指针(或:通过引用传递参数)
有时一个 C api 函数需要一个指向数据类型的 指针 作为参数,可能是写入相应的位置,或者如果数据太大而无法通过值传递。 这也称为 通过引用 传递参数。
ctypes 导出 byref() 函数,用于通过引用传递参数。 使用pointer()函数也可以达到同样的效果,虽然pointer()做了很多工作,因为它构造了一个真正的指针对象,所以使用速度更快]byref() 如果你不需要 Python 本身的指针对象:
结构和工会
结构和联合必须从 ctypes 模块中定义的 Structure 和 Union 基类派生。 每个子类必须定义一个 _fields_
属性。 _fields_
必须是 2 元组 的列表,包含 字段名称 和 字段类型 。
字段类型必须是 ctypes 类型,如 c_int,或任何其他派生的 ctypes 类型:结构、联合、数组、指针。
下面是一个简单的 POINT 结构示例,其中包含两个名为 x 和 y 的整数,并且还展示了如何在构造函数中初始化结构:
但是,您可以构建更复杂的结构。 通过将结构用作字段类型,结构本身可以包含其他结构。
这是一个 RECT 结构,它包含两个名为 upperleft 和 lowerright 的 POINT:
嵌套结构也可以通过以下几种方式在构造函数中初始化:
字段 描述符 可以从 类 中检索,它们对调试很有用,因为它们可以提供有用的信息:
结构/联合对齐和字节顺序
默认情况下,Structure 和 Union 字段的对齐方式与 C 编译器的对齐方式相同。 可以通过在子类定义中指定 _pack_
类属性来覆盖此行为。 这必须设置为正整数并指定字段的最大对齐方式。 这也是 #pragma pack(n)
在 MSVC 中所做的。
ctypes 使用结构和联合的本机字节顺序。 要构建具有非本机字节顺序的结构,您可以使用 BigEndianStructure、LittleEndianStructure、BigEndianUnion
和 LittleEndianUnion
基类之一。 这些类不能包含指针字段。
结构体和联合体中的位域
可以创建包含位域的结构和联合。 位域仅适用于整数域,位宽被指定为 _fields_
元组中的第三项:
数组
数组是序列,包含固定数量的相同类型的实例。
创建数组类型的推荐方法是将数据类型与正整数相乘:
这是一个有点人为的数据类型的示例,该结构包含 4 个 POINT 以及其他内容:
实例以通常的方式创建,通过调用类:
上面的代码打印了一系列 0 0
行,因为数组内容被初始化为零。
还可以指定正确类型的初始化程序:
指针
指针实例是通过在 ctypes 类型上调用 pointer() 函数来创建的:
指针实例有一个 contents 属性,它返回指针指向的对象,上面的 i
对象:
请注意, ctypes 没有 OOR(原始对象返回),每次检索属性时它都会构造一个新的等效对象:
将另一个 c_int 实例分配给指针的内容属性会导致指针指向存储它的内存位置:
指针实例也可以用整数索引:
分配给整数索引会更改指向的值:
也可以使用不同于 0 的索引,但您必须知道自己在做什么,就像在 C 中一样:您可以访问或更改任意内存位置。 通常,如果您从 C 函数接收指针,并且您 知道 该指针实际上指向一个数组而不是单个项目,则您只会使用此功能。
在幕后,pointer() 函数不仅仅是创建指针实例,它还必须首先创建指针 types。 这是通过 POINTER() 函数完成的,该函数接受任何 ctypes 类型,并返回一个新类型:
不带参数调用指针类型会创建一个 NULL
指针。 NULL
指针有一个 False
布尔值:
ctypes 在解引用指针时检查 NULL
(但解引用无效的非 NULL
指针会使 Python 崩溃):
类型转换
通常,ctypes 会进行严格的类型检查。 这意味着,如果在函数的 argtypes
列表中有 POINTER(c_int)
或作为结构定义中成员字段的类型,则只接受完全相同类型的实例。 此规则有一些例外,其中 ctypes 接受其他对象。 例如,您可以传递兼容的数组实例而不是指针类型。 因此,对于 POINTER(c_int)
,ctypes 接受一个 c_int 数组:
此外,如果函数参数在 argtypes
中显式声明为指针类型(例如 POINTER(c_int)
),则为指向类型的对象(在本例中为 c_int
)可以传递给函数。 在这种情况下,ctypes 将自动应用所需的 byref() 转换。
要将 POINTER 类型字段设置为 NULL
,您可以分配 None
:
有时您有不兼容类型的实例。 在 C 中,您可以将一种类型转换为另一种类型。 ctypes 提供了一个 cast() 函数,可以以同样的方式使用。 上面定义的 Bar
结构接受 POINTER(c_int)
指针或 c_int 数组作为其 values
字段,但不接受其他类型的实例:
对于这些情况,cast() 函数很方便。
cast() 函数可用于将 ctypes 实例转换为指向不同 ctypes 数据类型的指针。 cast() 接受两个参数,一个是或可以转换为某种指针的 ctypes 对象,以及一个 ctypes 指针类型。 它返回第二个参数的实例,它引用与第一个参数相同的内存块:
因此,可以使用 cast() 将结构体分配给 Bar
的 values
字段:
不完全类型
不完整类型 是结构、联合或数组,其成员尚未指定。 在 C 中,它们由前向声明指定,稍后定义:
直接转换为 ctypes 代码是这样的,但它不起作用:
因为新的 class cell
在 class 语句本身中不可用。 在 ctypes 中,我们可以定义 cell
类,然后在 class 语句之后设置 _fields_
属性:
让我们试试吧。 我们创建了两个 cell
的实例,并让它们相互指向,最后沿着指针链走几次:
回调函数
ctypes 允许从 Python 可调用对象创建 C 可调用函数指针。 这些有时称为 回调函数 。
首先,您必须为回调函数创建一个类。 该类知道调用约定、返回类型以及此函数将接收的参数的数量和类型。
CFUNCTYPE() 工厂函数使用 cdecl
调用约定为回调函数创建类型。 在 Windows 上,WINFUNCTYPE() 工厂函数使用 stdcall
调用约定为回调函数创建类型。
这两个工厂函数都以结果类型作为第一个参数被调用,回调函数期望的参数类型作为剩余的参数。
我将在此处展示一个示例,该示例使用标准 C 库的 qsort()
函数,该函数用于在回调函数的帮助下对项目进行排序。 qsort()
将用于对整数数组进行排序:
qsort()
必须使用指向要排序的数据的指针、数据数组中的项数、一项的大小以及指向比较函数、回调的指针来调用。 然后将使用两个指向项目的指针调用回调,如果第一个项目小于第二个项目,则它必须返回一个负整数,如果它们相等,则返回零,否则返回一个正整数。
所以我们的回调函数接收指向整数的指针,并且必须返回一个整数。 首先我们为回调函数创建type
:
首先,这是一个简单的回调,显示它传递的值:
结果:
现在我们可以实际比较这两个项目并返回一个有用的结果:
正如我们可以轻松检查的那样,我们的数组现在已排序:
函数工厂可以用作装饰器工厂,所以我们不妨这样写:
笔记
确保保留对 CFUNCTYPE() 对象的引用,只要它们从 C 代码中使用即可。 ctypes 没有,如果你不这样做,它们可能会被垃圾收集,在进行回调时使你的程序崩溃。
另外,请注意,如果在 Python 控制之外创建的线程中调用回调函数(例如 通过调用回调的外部代码),ctypes 在每次调用时创建一个新的虚拟 Python 线程。 这种行为在大多数情况下是正确的,但这意味着使用 threading.local 存储的值将 not 在不同的回调中存活,即使这些调用来自同一个 C 线程。
访问从 dll 导出的值
一些共享库不仅导出函数,还导出变量。 Python 库本身中的一个示例是 Py_OptimizeFlag,一个整数设置为 0、1 或 2,具体取决于给定的 -O 或 -OO 标志在启动时。
ctypes 可以使用该类型的 in_dll()
类方法访问这样的值。 pythonapi 是一个预定义的符号,可以访问 Python C api:
如果解释器以 -O 开始,则样本将打印 c_long(1)
,或 c_long(2)
如果已指定 -OO。
一个扩展示例也演示了如何使用指针访问 Python 导出的 PyImport_FrozenModules 指针。
引用该值的文档:
这个指针被初始化为指向一个 struct _frozen 记录数组,由一个成员都是
NULL
或零的记录终止。 导入冻结模块时,将在此表中搜索。 第三方代码可以利用此技巧来提供动态创建的冻结模块集合。
所以操纵这个指针甚至可以证明是有用的。 为了限制示例大小,我们仅展示如何使用 ctypes 读取此表:
我们已经定义了 struct _frozen 数据类型,所以我们可以得到指向表的指针:
由于 table
是 struct_frozen
记录数组的 pointer
,我们可以迭代它,但我们只需要确保我们的循环终止,因为指针没有大小. 迟早它可能会因访问冲突或其他原因而崩溃,因此最好在我们点击 NULL
条目时跳出循环:
标准 Python 有一个冻结模块和一个冻结包(由负的 size
成员表示)这一事实并不为人所知,它仅用于测试。 例如,尝试使用 import __hello__
。
惊喜
在 ctypes 中有一些边缘,你可能会期待一些与实际发生的情况不同的东西。
考虑以下示例:
嗯。 我们当然希望最后一条语句打印 3 4 1 2
。 发生了什么? 以下是上面 rc.a, rc.b = rc.b, rc.a
行的步骤:
请注意,temp0
和 temp1
是仍在使用上述 rc
对象的内部缓冲区的对象。 因此,执行 rc.a = temp0
会将 temp0
的缓冲区内容复制到 rc
的缓冲区中。 这反过来又会改变 temp1
的内容。 所以,最后一个赋值 rc.b = temp1
没有达到预期的效果。
请记住,从结构、联合和数组中检索子对象不会 复制 子对象,而是检索访问根对象的底层缓冲区的包装器对象。
另一个可能与人们期望的行为不同的示例是:
为什么打印 False
? ctypes 实例是包含内存块和一些 描述符 访问内存内容的对象。 在内存块中存储 Python 对象并不存储对象本身,而是存储对象的 contents
。 再次访问内容每次都会构造一个新的 Python 对象!
可变大小的数据类型
ctypes 为可变大小的数组和结构提供了一些支持。
resize() 函数可用于调整现有 ctypes 对象的内存缓冲区大小。 该函数将对象作为第一个参数,将请求的字节大小作为第二个参数。 内存块不能小于对象类型指定的自然内存块,如果尝试这样做,则会引发 ValueError:
这很好也很好,但是如何访问包含在这个数组中的附加元素呢? 由于该类型仍然只知道 4 个元素,因此访问其他元素时会出错:
将可变大小数据类型与 ctypes 一起使用的另一种方法是使用 Python 的动态特性,并在已知所需大小后(重新)定义数据类型,具体情况具体分析。
ctypes 参考
外部函数
如上一节所述,外部函数可以作为加载的共享库的属性进行访问。 以这种方式创建的函数对象默认接受任意数量的参数,接受任何 ctypes 数据实例作为参数,并返回库加载器指定的默认结果类型。 它们是私有类的实例:
- class ctypes._FuncPtr
C 可调用外部函数的基类。
外部函数的实例也是 C 兼容的数据类型; 它们代表 C 函数指针。
这种行为可以通过分配给外部函数对象的特殊属性来定制。
- restype
分配一个 ctypes 类型来指定外部函数的结果类型。 将
None
用于 void,该函数不返回任何内容。可以分配一个不是 ctypes 类型的可调用 Python 对象,在这种情况下,假定函数返回 C int,并且将使用此整数调用可调用对象,允许进一步处理或错误检查。 不推荐使用它,为了更灵活的后处理或错误检查,请使用 ctypes 数据类型作为 restype 并将可调用对象分配给 errcheck 属性。
- argtypes
分配一个 ctypes 类型的元组来指定函数接受的参数类型。 使用
stdcall
调用约定的函数只能使用与此元组长度相同数量的参数来调用; 使用 C 调用约定的函数也接受额外的、未指定的参数。当调用外部函数时,每个实参都传递给 argtypes 元组中项的
from_param()
类方法,该方法允许将实参适配到外部函数接受。 例如, argtypes 元组中的 c_char_p 项将使用 ctypes 转换规则将作为参数传递的字符串转换为字节对象。新:现在可以将项目放入不是 ctypes 类型的 argtypes 中,但每个项目必须有一个
from_param()
方法,该方法返回一个可用作参数的值(整数、字符串、ctypes 实例)。 这允许定义可以适应自定义对象作为函数参数的适配器。
- errcheck
为该属性分配一个 Python 函数或另一个可调用函数。 将使用三个或更多参数调用可调用对象:
- callable(result, func, arguments)
result 是外部函数返回的内容,由
restype
属性指定。func 是外部函数对象本身,这允许重用相同的可调用对象来检查或后处理多个函数的结果。
arguments 是一个包含最初传递给函数调用的参数的元组,这允许对所用参数的行为进行专门化。
此函数返回的对象将从外部函数调用返回,但如果外部函数调用失败,它也可以检查结果值并引发异常。
- exception ctypes.ArgumentError
- 当外部函数调用无法转换传递的参数之一时,会引发此异常。
函数原型
也可以通过实例化函数原型来创建外部函数。 函数原型类似于 C 中的函数原型; 它们描述了一个函数(返回类型、参数类型、调用约定)而不定义实现。 工厂函数必须使用所需的结果类型和函数的参数类型来调用,并且可以用作装饰器工厂,因此可以通过 @wrapper
语法应用于函数。 有关示例,请参阅 回调函数 。
- ctypes.CFUNCTYPE(restype, *argtypes, use_errno=False, use_last_error=False)
- 返回的函数原型创建使用标准 C 调用约定的函数。 该函数将在调用期间释放 GIL。 如果use_errno设置为true,系统errno变量的ctypes私有副本在调用前后与真实的errno值交换; use_last_error 对 Windows 错误代码执行相同操作。
- ctypes.WINFUNCTYPE(restype, *argtypes, use_errno=False, use_last_error=False)
- 仅限 Windows:返回的函数原型创建使用
stdcall
调用约定的函数,但在 Windows CE 上除外,其中 WINFUNCTYPE() 与 CFUNCTYPE() 相同。 该函数将在调用期间释放 GIL。 use_errno 和 use_last_error 含义同上。
- ctypes.PYFUNCTYPE(restype, *argtypes)
- 返回的函数原型创建使用 Python 调用约定的函数。 该函数将 不 在调用过程中释放 GIL。
这些工厂函数创建的函数原型可以通过不同的方式实例化,具体取决于调用中参数的类型和数量:
- prototype(address)
- 返回指定地址处的外部函数,该函数必须是整数。
- prototype(callable)
- 从 Python callable 创建 C 可调用函数(回调函数)。
- prototype(func_spec[, paramflags])
- 返回共享库导出的外部函数。 func_spec 必须是一个二元组
(name_or_ordinal, library)
。 第一项是作为字符串的导出函数的名称,或者作为小整数的导出函数的序数。 第二项是共享库实例。
- prototype(vtbl_index, name[, paramflags[, iid]])
返回将调用 COM 方法的外部函数。 vtbl_index是虚函数表的索引,一个小的非负整数。 name 是 COM 方法的名称。 iid 是一个可选的指向接口标识符的指针,用于扩展错误报告。
COM 方法使用特殊的调用约定:除了
argtypes
元组中指定的那些参数之外,它们还需要一个指向 COM 接口的指针作为第一个参数。可选的 paramflags 参数创建具有比上述功能更多功能的外部函数包装器。
paramflags 必须是与
argtypes
长度相同的元组。此元组中的每一项都包含有关参数的更多信息,它必须是包含一项、两项或三项的元组。
第一项是一个整数,包含参数的方向标志组合:
- 1
- 指定函数的输入参数。
- 2
- 输出参数。 外部函数填充一个值。
- 4
- 默认为整数零的输入参数。
可选的第二项是字符串形式的参数名称。 如果指定了此项,则可以使用命名参数调用外部函数。
可选的第三项是该参数的默认值。
此示例演示如何包装 Windows MessageBoxW
函数,以便它支持默认参数和命名参数。 windows 头文件中的 C 声明是这样的:
这是 ctypes 的包装:
MessageBox
外部函数现在可以通过以下方式调用:
第二个示例演示输出参数。 win32 GetWindowRect
函数通过将它们复制到调用者必须提供的 RECT
结构中来检索指定窗口的尺寸。 这是 C 声明:
这是 ctypes 的包装:
如果有一个输出参数值,带有输出参数的函数将自动返回输出参数值,或者当有多个输出参数值时返回一个包含输出参数值的元组,因此 GetWindowRect 函数现在在调用时返回一个 RECT 实例。
输出参数可以结合errcheck
协议进行进一步的输出处理和错误检查。 win32 GetWindowRect
api 函数返回一个 BOOL
来表示成功或失败,因此该函数可以进行错误检查,并在 api 调用失败时引发异常:
如果 errcheck
函数返回它接收到的参数元组不变,ctypes 继续它对输出参数所做的正常处理。 如果你想返回一个窗口坐标元组而不是一个 RECT
实例,你可以检索函数中的字段并返回它们,正常处理将不再发生:
实用功能
- ctypes.addressof(obj)
- 以整数形式返回内存缓冲区的地址。 obj 必须是 ctypes 类型的实例。
- ctypes.alignment(obj_or_type)
- 返回 ctypes 类型的对齐要求。 obj_or_type 必须是 ctypes 类型或实例。
- ctypes.byref(obj[, offset])
返回一个指向 obj 的轻量级指针,它必须是一个 ctypes 类型的实例。 offset 默认为零,并且必须是将添加到内部指针值的整数。
byref(obj, offset)
对应这个 C 代码:返回的对象只能用作外部函数调用参数。 它的行为类似于
pointer(obj)
,但构建速度要快得多。
- ctypes.cast(obj, type)
- 此函数类似于 C 中的强制转换运算符。 它返回一个 type 的新实例,它指向与 obj 相同的内存块。 type 必须是指针类型,obj 必须是可以解释为指针的对象。
- ctypes.create_string_buffer(init_or_size, size=None)
此函数创建一个可变字符缓冲区。 返回的对象是 c_char 的 ctypes 数组。
init_or_size 必须是指定数组大小的整数,或用于初始化数组项的字节对象。
如果将字节对象指定为第一个参数,则缓冲区将比其长度大一个项目,以便数组中的最后一个元素是 NUL 终止字符。 一个整数可以作为第二个参数传递,如果不应该使用字节的长度,它允许指定数组的大小。
- ctypes.create_unicode_buffer(init_or_size, size=None)
此函数创建一个可变的 unicode 字符缓冲区。 返回的对象是 c_wchar 的 ctypes 数组。
init_or_size 必须是指定数组大小的整数,或用于初始化数组项的字符串。
如果将字符串指定为第一个参数,则缓冲区将比字符串的长度大一个项目,以便数组中的最后一个元素是 NUL 终止字符。 一个整数可以作为第二个参数传递,如果不应该使用字符串的长度,它允许指定数组的大小。
- ctypes.DllCanUnloadNow()
- 仅适用于 Windows:此函数是一个钩子,它允许使用 ctypes 实现进程内 COM 服务器。 它是从 _ctypes 扩展 dll 导出的 DllCanUnloadNow 函数调用的。
- ctypes.DllGetClassObject()
- 仅适用于 Windows:此函数是一个钩子,它允许使用 ctypes 实现进程内 COM 服务器。 它是从
_ctypes
扩展 dll 导出的 DllGetClassObject 函数调用的。
- ctypes.util.find_library(name)
尝试查找库并返回路径名。 name 是没有任何前缀的库名,如
lib
,后缀如.so
,.dylib
或版本号(这是用于 posix 链接器的形式选项-l
)。 如果找不到库,则返回None
。确切的功能取决于系统。
- ctypes.util.find_msvcrt()
仅限 Windows:返回 Python 和扩展模块使用的 VC 运行时库的文件名。 如果无法确定库的名称,则返回
None
。如果您需要释放内存,例如,由调用
free(void *)
的扩展模块分配,那么使用分配内存的同一库中的函数很重要。
- ctypes.FormatError([code])
- 仅限 Windows:返回错误代码 code 的文本描述。 如果未指定错误代码,则通过调用 Windows api 函数 GetLastError 使用最后一个错误代码。
- ctypes.GetLastError()
- 仅限 Windows:返回 Windows 在调用线程中设置的最后一个错误代码。 此函数直接调用Windows GetLastError() 函数,它不返回错误代码的ctypes-private 副本。
- ctypes.get_errno()
- 返回调用线程中系统 errno 变量的 ctypes-private 副本的当前值。
- ctypes.get_last_error()
- 仅限 Windows:返回调用线程中系统
LastError
变量的 ctypes-private 副本的当前值。
- ctypes.memmove(dst, src, count)
- 与标准 C memmove 库函数相同:将 count 个字节从 src 复制到 dst。 dst 和 src 必须是可以转换为指针的整数或 ctypes 实例。
- ctypes.memset(dst, c, count)
- 与标准 C memset 库函数相同:用 count 个字节的值 c 填充地址 dst 处的内存块。 dst 必须是指定地址或 ctypes 实例的整数。
- ctypes.POINTER(type)
- 这个工厂函数创建并返回一个新的 ctypes 指针类型。 指针类型在内部被缓存和重用,所以重复调用这个函数很便宜。 type 必须是 ctypes 类型。
- ctypes.pointer(obj)
此函数创建一个新的指针实例,指向 obj。 返回的对象的类型为
POINTER(type(obj))
。注意:如果您只想将指向对象的指针传递给外部函数调用,您应该使用
byref(obj)
,它要快得多。
- ctypes.resize(obj, size)
- 此函数调整 obj 的内部内存缓冲区大小,它必须是 ctypes 类型的实例。 不可能使缓冲区小于对象类型的原始大小,如
sizeof(type(obj))
给出的,但可以扩大缓冲区。
- ctypes.set_errno(value)
- 将调用线程中系统 errno 变量的 ctypes-private 副本的当前值设置为 value 并返回之前的值。
- ctypes.set_last_error(value)
- 仅限 Windows:将调用线程中系统
LastError
变量的 ctypes-private 副本的当前值设置为 value 并返回先前的值。
- ctypes.sizeof(obj_or_type)
- 返回 ctypes 类型或实例内存缓冲区的大小(以字节为单位)。 与 C
sizeof
运算符相同。
- ctypes.string_at(address, size=- 1)
- 此函数返回从内存地址 address 开始的 C 字符串作为字节对象。 如果指定了大小,则将其用作大小,否则假定字符串以零结尾。
- ctypes.WinError(code=None, descr=None)
仅限 Windows:这个函数可能是 ctypes 中命名最差的东西。 它创建了一个 OSError 实例。 如果未指定 code,则调用
GetLastError
以确定错误代码。 如果未指定 descr,则调用 FormatError() 以获取错误的文本描述。3.3 版更改: 曾经创建 WindowsError 的实例。
- ctypes.wstring_at(address, size=- 1)
- 此函数以字符串形式返回从内存地址 address 开始的宽字符串。 如果指定了 size,则将其用作字符串的字符数,否则假定字符串以零结尾。
数据类型
- class ctypes._CData
这个非公共类是所有 ctypes 数据类型的公共基类。 除其他外,所有 ctypes 类型实例都包含一个存储 C 兼容数据的内存块; 内存块的地址由 addressof() 辅助函数返回。 另一个实例变量公开为 _objects; 这包含其他需要保持活动状态的 Python 对象,以防内存块包含指针。
ctypes数据类型的常用方法,这些都是类方法(准确的说是元类的方法):
- from_buffer(source[, offset])
此方法返回一个 ctypes 实例,该实例共享 source 对象的缓冲区。 source 对象必须支持可写缓冲区接口。 可选的 offset 参数以字节为单位指定源缓冲区的偏移量; 默认为零。 如果源缓冲区不够大,则会引发 ValueError。
- from_buffer_copy(source[, offset])
此方法创建一个 ctypes 实例,从必须可读的 source 对象缓冲区复制缓冲区。 可选的 offset 参数以字节为单位指定源缓冲区的偏移量; 默认为零。 如果源缓冲区不够大,则会引发 ValueError。
- from_address(address)
此方法使用 address 指定的内存返回一个 ctypes 类型实例,该内存必须是整数。
- from_param(obj)
此方法使 obj 适应 ctypes 类型。 当类型存在于外部函数的
argtypes
元组中时,使用外部函数调用中使用的实际对象调用它; 它必须返回一个可以用作函数调用参数的对象。所有 ctypes 数据类型都有此类方法的默认实现,如果它是该类型的实例,则通常返回 obj。 有些类型也接受其他对象。
- in_dll(library, name)
此方法返回共享库导出的 ctypes 类型实例。 name为导出数据的符号名,library为加载的共享库。
ctypes 数据类型的常见实例变量:
- _b_base_
有时 ctypes 数据实例不拥有它们包含的内存块,而是共享基础对象的部分内存块。 _b_base_ 只读成员是拥有内存块的根 ctypes 对象。
- _b_needsfree_
当 ctypes 数据实例本身分配了内存块时,这个只读变量为真,否则为假。
- _objects
该成员是
None
或包含需要保持活动状态的 Python 对象的字典,以便内存块内容保持有效。 此对象仅用于调试; 永远不要修改这本词典的内容。
基本数据类型
- class ctypes._SimpleCData
这个非公共类是所有基本 ctypes 数据类型的基类。 在这里提到它是因为它包含基本 ctypes 数据类型的公共属性。 _SimpleCData 是 _CData 的子类,所以继承了它们的方法和属性。 现在可以腌制不包含也不包含指针的 ctypes 数据类型。
实例有一个属性:
- value
该属性包含实例的实际值。 对于整数和指针类型,它是一个整数,对于字符类型,它是一个单字符字节对象或字符串,对于字符指针类型,它是一个 Python 字节对象或字符串。
当从 ctypes 实例中检索
value
属性时,通常每次都会返回一个新对象。 ctypes 不实现原对象返回,总是构造一个新对象。 对于所有其他 ctypes 对象实例也是如此。
基本数据类型在作为外部函数调用结果返回时,或者例如通过检索结构字段成员或数组项时,会透明地转换为本地 Python 类型。 换句话说,如果一个外部函数有一个 c_char_p 的 restype
,你将总是收到一个 Python 字节对象,而不是 一个 c_char_p 实例.
基本数据类型的子类 不会 继承此行为。 因此,如果外部函数 restype
是 c_void_p 的子类,您将从函数调用中收到该子类的实例。 当然,您可以通过访问 value
属性来获取指针的值。
这些是基本的 ctypes 数据类型:
- class ctypes.c_byte
- 表示 C signed char 数据类型,并将值解释为小整数。 构造函数接受一个可选的整数初始化器; 没有进行溢出检查。
- class ctypes.c_char
- 表示 C char 数据类型,并将值解释为单个字符。 构造函数接受一个可选的字符串初始化器,字符串的长度必须正好是一个字符。
- class ctypes.c_char_p
- 当它指向以零结尾的字符串时,表示 C char* 数据类型。 对于也可能指向二进制数据的通用字符指针,必须使用
POINTER(c_char)
。 构造函数接受一个整数地址或一个字节对象。
- class ctypes.c_double
- 表示 C double 数据类型。 构造函数接受一个可选的浮点初始化器。
- class ctypes.c_longdouble
- 表示 C long double 数据类型。 构造函数接受一个可选的浮点初始化器。 在
sizeof(long double) == sizeof(double)
它是 c_double 的别名的平台上。
- class ctypes.c_float
- 表示 C float 数据类型。 构造函数接受一个可选的浮点初始化器。
- class ctypes.c_int
- 表示 C signed int 数据类型。 构造函数接受一个可选的整数初始化器; 没有进行溢出检查。 在
sizeof(int) == sizeof(long)
它是 c_long 的别名的平台上。
- class ctypes.c_int8
- 表示 C 8 位 signed int 数据类型。 通常是 c_byte 的别名。
- class ctypes.c_int16
- 表示 C 16 位 signed int 数据类型。 通常是 c_short 的别名。
- class ctypes.c_int32
- 表示 C 32 位 signed int 数据类型。 通常是 c_int 的别名。
- class ctypes.c_int64
- 表示 C 64 位 signed int 数据类型。 通常是 c_longlong 的别名。
- class ctypes.c_long
- 表示 C signed long 数据类型。 构造函数接受一个可选的整数初始化器; 没有进行溢出检查。
- class ctypes.c_longlong
- 表示 C signed long long 数据类型。 构造函数接受一个可选的整数初始化器; 没有进行溢出检查。
- class ctypes.c_short
- 表示 C signed short 数据类型。 构造函数接受一个可选的整数初始化器; 没有进行溢出检查。
- class ctypes.c_size_t
- 表示 C
size_t
数据类型。
- class ctypes.c_ssize_t
表示 C
ssize_t
数据类型。3.2 版中的新功能。
- class ctypes.c_ubyte
- 表示 C unsigned char 数据类型,它将值解释为小整数。 构造函数接受一个可选的整数初始化器; 没有进行溢出检查。
- class ctypes.c_uint
- 表示 C unsigned int 数据类型。 构造函数接受一个可选的整数初始化器; 没有进行溢出检查。 在
sizeof(int) == sizeof(long)
它是 c_ulong 的别名的平台上。
- class ctypes.c_uint8
- 表示 C 8 位 unsigned int 数据类型。 通常是 c_ubyte 的别名。
- class ctypes.c_uint16
- 表示 C 16 位 unsigned int 数据类型。 通常是 c_ushort 的别名。
- class ctypes.c_uint32
- 表示 C 32 位 unsigned int 数据类型。 通常是 c_uint 的别名。
- class ctypes.c_uint64
- 表示 C 64 位 unsigned int 数据类型。 通常是 c_ulonglong 的别名。
- class ctypes.c_ulong
- 表示 C unsigned long 数据类型。 构造函数接受一个可选的整数初始化器; 没有进行溢出检查。
- class ctypes.c_ulonglong
- 表示 C unsigned long long 数据类型。 构造函数接受一个可选的整数初始化器; 没有进行溢出检查。
- class ctypes.c_ushort
- 表示 C unsigned short 数据类型。 构造函数接受一个可选的整数初始化器; 没有进行溢出检查。
- class ctypes.c_void_p
- 表示 C void* 类型。 该值表示为整数。 构造函数接受一个可选的整数初始值设定项。
- class ctypes.c_wchar
- 表示 C
wchar_t
数据类型,并将值解释为单字符 unicode 字符串。 构造函数接受一个可选的字符串初始化器,字符串的长度必须正好是一个字符。
- class ctypes.c_wchar_p
- 表示 C wchar_t* 数据类型,它必须是指向以零结尾的宽字符串的指针。 构造函数接受一个整数地址或字符串。
- class ctypes.c_bool
- 表示 C bool 数据类型(更准确地说,是 C99 中的 _Bool)。 它的值可以是
True
或False
,构造函数接受任何具有真值的对象。
- class ctypes.HRESULT
- 仅限 Windows:表示
HRESULT
值,其中包含函数或方法调用的成功或错误信息。
- class ctypes.py_object
- 表示 C PyObject* 数据类型。 不带参数调用它会创建一个
NULL
PyObject* 指针。
ctypes.wintypes
模块提供了相当多的其他 Windows 特定数据类型,例如 HWND
、WPARAM
或 DWORD
。 还定义了一些有用的结构,如 MSG
或 RECT
。
结构化数据类型
- class ctypes.Union(*args, **kw)
- 本机字节顺序的联合的抽象基类。
- class ctypes.BigEndianStructure(*args, **kw)
- big endian 字节顺序结构的抽象基类。
- class ctypes.LittleEndianStructure(*args, **kw)
- little endian 字节顺序结构的抽象基类。
具有非本地字节顺序的结构不能包含指针类型字段,或包含指针类型字段的任何其他数据类型。
- class ctypes.Structure(*args, **kw)
native 字节顺序结构的抽象基类。
具体的结构和联合类型必须通过继承这些类型之一来创建,并且至少定义一个 _fields_ 类变量。 ctypes 将创建 描述符 ,允许通过直接属性访问读取和写入字段。 这些是
- _fields_
定义结构字段的序列。 项必须是 2 元组或 3 元组。 第一项是字段名称,第二项指定字段类型; 它可以是任何 ctypes 数据类型。
对于像 c_int 这样的整数类型字段,可以给出第三个可选项。 它必须是一个小的正整数,用于定义字段的位宽。
字段名称在一个结构或联合中必须是唯一的。 未选中此项,当名称重复时,只能访问一个字段。
可以在定义 Structure 子类的 class 语句之后定义 _fields_ 类变量 ,这允许创建直接或间接引用自身的数据类型:
然而,_fields_ 类变量必须在第一次使用类型之前定义(创建一个实例,在它上面调用 sizeof(),等等)。 稍后对 _fields_ 类变量的赋值将引发 AttributeError。
可以定义结构类型的子子类,它们继承基类的字段加上子子类中定义的 _fields_(如果有)。
- _anonymous_
列出未命名(匿名)字段名称的可选序列。 _anonymous_ 必须在分配_fields_ 时已经定义,否则无效。
此变量中列出的字段必须是结构或联合类型字段。 ctypes 将在允许直接访问嵌套字段的结构类型中创建描述符,而无需创建结构或联合字段。
这是一个示例类型(Windows):
TYPEDESC
结构描述了一种 COM 数据类型,vt
字段指定哪一个联合字段是有效的。 由于u
字段被定义为匿名字段,现在可以直接从 TYPEDESC 实例访问成员。td.lptdesc
和td.u.lptdesc
是等价的,但前者更快,因为它不需要创建临时联合实例:
可以定义结构的子子类,它们继承基类的字段。 如果子类定义具有单独的 _fields_ 变量,则在此指定的字段将附加到基类的字段。
结构和联合构造函数接受位置参数和关键字参数。 位置参数用于按照它们出现在 _fields_ 中的相同顺序初始化成员字段。 构造函数中的关键字参数被解释为属性赋值,因此它们将使用相同的名称初始化 _fields_,或者为 _fields_ 中不存在的名称创建新属性。
数组和指针
- class ctypes.Array(*args)
数组的抽象基类。
创建具体数组类型的推荐方法是将任何 ctypes 数据类型乘以一个正整数。 或者,您可以子类化此类型并定义 _length_ 和 _type_ 类变量。 可以使用标准下标和切片访问来读取和写入数组元素; 对于切片读取,结果对象是 不是 本身是 数组 。
- _length_
一个正整数,指定数组中的元素数。 超出范围的下标会导致 IndexError。 将由 len() 返回。
- _type_
指定数组中每个元素的类型。
数组子类构造函数接受位置参数,用于按顺序初始化元素。
- class ctypes._Pointer
指针的私有抽象基类。
具体的指针类型是通过使用将要指向的类型调用 POINTER() 来创建的; 这是由 pointer() 自动完成的。
如果指针指向数组,则可以使用标准下标和切片访问读取和写入其元素。 指针对象没有大小,因此 len() 将引发 TypeError。 负下标将在 之前从内存中读取 指针(如在 C 中),超出范围的下标可能会因访问冲突而崩溃(如果你很幸运)。
- _type_
指定指向的类型。
- contents
返回指针指向的对象。 分配给该属性会将指针更改为指向所分配的对象。