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呼び出し規約を使用していることに注意してください。
>>> from ctypes import *
>>> print(windll.kernel32)
<WinDLL 'kernel32', handle ... at ...>
>>> print(cdll.msvcrt)
<CDLL 'msvcrt', handle ... at ...>
>>> libc = cdll.msvcrt
>>>
Windowsは、通常の.dll
ファイルサフィックスを自動的に追加します。
ノート
cdll.msvcrt
を介して標準Cライブラリにアクセスすると、Pythonで使用されているものと互換性がない可能性のある古いバージョンのライブラリが使用されます。 可能な場合は、ネイティブのPython機能を使用するか、msvcrt
モジュールをインポートして使用します。
Linuxでは、ライブラリをロードするために拡張子を含むファイル名を指定する必要があるため、属性アクセスを使用してライブラリをロードすることはできません。 dllローダーのLoadLibrary()
メソッドを使用するか、コンストラクターを呼び出してCDLLのインスタンスを作成してライブラリをロードする必要があります。
>>> cdll.LoadLibrary("libc.so.6")
<CDLL 'libc.so.6', handle ... at ...>
>>> libc = CDLL("libc.so.6")
>>> libc
<CDLL 'libc.so.6', handle ... at ...>
>>>
ロードされたdllから関数にアクセスする
関数は、dllオブジェクトの属性としてアクセスされます。
>>> 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):
File "<stdin>", line 1, in <module>
File "ctypes.py", line 239, in __getattr__
func = _StdcallFuncPtr(name, self)
AttributeError: function 'MyOwnFunction' not found
>>>
kernel32
やuser32
などのwin32システムdllは、関数のUNICODEバージョンだけでなくANSIバージョンもエクスポートすることが多いことに注意してください。 UNICODEバージョンは名前にW
が追加されてエクスポートされ、ANSIバージョンは名前にA
が追加されてエクスポートされます。 指定されたモジュール名のモジュールハンドルを返すwin32 GetModuleHandle
関数には、次のCプロトタイプがあり、マクロを使用してそれらの1つをGetModuleHandle
として公開します。 UNICODEが定義されているかどうかに応じて:
/* ANSI version */
HMODULE GetModuleHandleA(LPCSTR lpModuleName);
/* UNICODE version */
HMODULE GetModuleHandleW(LPCWSTR lpModuleName);
windll は魔法でそれらの1つを選択しようとはしません。GetModuleHandleA
またはGetModuleHandleW
を明示的に指定して必要なバージョンにアクセスし、バイトまたは文字列で呼び出す必要がありますそれぞれオブジェクト。
"??2@YAPAXI@Z"
のように、dllが有効なPython識別子ではない名前で関数をエクスポートすることがあります。 この場合、関数を取得するには getattr()を使用する必要があります。
>>> getattr(cdll.msvcrt, "??2@YAPAXI@Z")
<_FuncPtr object at 0x...>
>>>
Windowsでは、一部のdllは、名前ではなく序数で関数をエクスポートします。 これらの関数には、dllオブジェクトに序数のインデックスを付けることでアクセスできます。
>>> cdll.kernel32[1]
<_FuncPtr object at 0x...>
>>> cdll.kernel32[0]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "ctypes.py", line 310, in __getitem__
func = _StdcallFuncPtr(name, self)
AttributeError: function ordinal 0 not found
>>>
関数の呼び出し
これらの関数は、他のPython呼び出し可能オブジェクトと同じように呼び出すことができます。 この例では、Unixエポックからのシステム時間を秒単位で返すtime()
関数と、win32モジュールハンドルを返すGetModuleHandleA()
関数を使用しています。
この例では、NULL
ポインターを使用して両方の関数を呼び出します(None
はNULL
ポインターとして使用する必要があります)。
>>> print(libc.time(None))
1150640792
>>> print(hex(windll.kernel32.GetModuleHandleA(None)))
0x1d000000
>>>
ValueError は、stdcall
関数をcdecl
呼び出し規約で呼び出すと発生します。その逆も同様です。
>>> cdll.kernel32.GetModuleHandleA(None)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: Procedure probably called with not enough arguments (4 bytes missing)
>>>
>>> windll.msvcrt.printf(b"spam")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: Procedure probably called with too many arguments (4 bytes in excess)
>>>
正しい呼び出し規約を見つけるには、Cヘッダーファイルまたは呼び出したい関数のドキュメントを調べる必要があります。
Windowsでは、 ctypes はwin32構造化例外処理を使用して、関数が無効な引数値で呼び出された場合の一般保護違反によるクラッシュを防ぎます。
>>> windll.kernel32.GetModuleHandleA(32)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OSError: exception: access violation reading 0x00000020
>>>
ただし、Pythonを ctypes でクラッシュさせるには十分な方法があるため、とにかく注意する必要があります。 faulthandler モジュールは、クラッシュのデバッグに役立ちます(例: 誤ったCライブラリ呼び出しによって生成されたセグメンテーション違反から)。
None
、整数、バイトオブジェクト、および(Unicode)文字列は、これらの関数呼び出しでパラメーターとして直接使用できる唯一のネイティブPythonオブジェクトです。 None
はC NULL
ポインターとして渡され、バイトオブジェクトと文字列は、データを含むメモリブロック( char * または wchar_t *)へのポインターとして渡されます。 )。 Python整数は、プラットフォームのデフォルトのC int 型として渡され、その値はC型に収まるようにマスクされます。
他のパラメーター型を使用した関数の呼び出しに進む前に、 ctypes データ型について詳しく学ぶ必要があります。
基本的なデータ型
ctypes は、いくつかのプリミティブC互換データ型を定義します。
ctypesタイプ | Cタイプ | Pythonタイプ |
---|---|---|
c_bool
|
_Bool | ブール(1) |
c_char
|
char | 1文字バイトオブジェクト |
c_wchar
|
wchar_t
|
1文字の文字列 |
c_byte
|
char | int |
c_ubyte
|
unsigned char | int |
c_short
|
短い | int |
c_ushort
|
署名されていない短い | int |
c_int
|
int | int |
c_uint
|
unsigned int | int |
c_long
|
長さ | int |
c_ulong
|
unsigned long | int |
c_longlong
|
__int64 または long long
|
int |
c_ulonglong
|
unsigned __int64 または unsigned long long | int |
c_size_t
|
size_t
|
int |
c_ssize_t
|
ssize_t またはPy_ssize_t
|
int |
c_float
|
浮く | 浮く |
c_double
|
ダブル | 浮く |
c_longdouble
|
ロングダブル | 浮く |
c_char_p
|
char * (NUL終了) | バイトオブジェクトまたはNone
|
c_wchar_p
|
wchar_t * (NUL終了) | 文字列またはNone
|
c_void_p
|
空所* | intまたはNone
|
- コンストラクターは、真理値を持つすべてのオブジェクトを受け入れます。
これらのタイプはすべて、正しいタイプと値のオプションのイニシャライザーを使用して呼び出すことで作成できます。
>>> c_int()
c_long(0)
>>> c_wchar_p("Hello, World")
c_wchar_p(140018365411392)
>>> c_ushort(-3)
c_ushort(65533)
>>>
これらのタイプは変更可能であるため、後で値を変更することもできます。
>>> i = c_int(42)
>>> print(i)
c_long(42)
>>> print(i.value)
42
>>> i.value = -99
>>> print(i.value)
-99
>>>
ポインタ型 c_char_p 、 c_wchar_p 、および c_void_p のインスタンスに新しい値を割り当てると、それらが指すメモリ位置が変更されます[X169X ]メモリブロックの内容ではありません(もちろん、Pythonバイトオブジェクトは不変であるため、そうではありません):
>>> s = "Hello, World"
>>> c_s = c_wchar_p(s)
>>> print(c_s)
c_wchar_p(139966785747344)
>>> print(c_s.value)
Hello World
>>> c_s.value = "Hi, there"
>>> print(c_s) # the memory location has changed
c_wchar_p(139966783348904)
>>> print(c_s.value)
Hi, there
>>> print(s) # first object is unchanged
Hello, World
>>>
ただし、可変メモリへのポインタを期待する関数にそれらを渡さないように注意する必要があります。 可変メモリブロックが必要な場合、ctypesには create_string_buffer()関数があり、さまざまな方法でこれらを作成します。 現在のメモリブロックの内容は、raw
プロパティを使用してアクセス(または変更)できます。 NULで終了する文字列としてアクセスする場合は、value
プロパティを使用します。
>>> 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'
>>> p = create_string_buffer(b"Hello") # create a buffer containing a NUL terminated string
>>> print(sizeof(p), repr(p.raw))
6 b'Hello\x00'
>>> print(repr(p.value))
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'
>>> p.value = b"Hi"
>>> print(sizeof(p), repr(p.raw))
10 b'Hi\x00lo\x00\x00\x00\x00\x00'
>>>
create_string_buffer()関数は、c_buffer()
関数(エイリアスとして引き続き使用可能)、および以前のctypesリリースのc_string()
関数を置き換えます。 Cタイプwchar_t
のUnicode文字を含む可変メモリブロックを作成するには、 create_unicode_buffer()関数を使用します。
関数の呼び出し、続き
printfは実際の標準出力チャネルではなくから sys.stdout に出力するため、これらの例はコンソールプロンプトでのみ機能し、 IDLE 内からは機能しないことに注意してください。または PythonWin :
>>> printf = libc.printf
>>> printf(b"Hello, %s\n", b"World!")
Hello, World!
14
>>> printf(b"Hello, %S\n", "World!")
Hello, World!
14
>>> printf(b"%d bottles of beer\n", 42)
42 bottles of beer
19
>>> printf(b"%f bottles of beer\n", 42.5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ArgumentError: argument 2: exceptions.TypeError: Don't know how to convert parameter 2
>>>
前に述べたように、整数、文字列、バイトオブジェクトを除くすべてのPython型は、必要なCデータ型に変換できるように、対応する ctypes 型でラップする必要があります。
>>> printf(b"An int %d, a double %f\n", 1234, c_double(3.14))
An int 1234, a double 3.140000
31
>>>
独自のカスタムデータ型を使用した関数の呼び出し
ctypes 引数の変換をカスタマイズして、独自のクラスのインスタンスを関数の引数として使用できるようにすることもできます。 ctypes は_as_parameter_
属性を探し、これを関数の引数として使用します。 もちろん、整数、文字列、またはバイトのいずれかである必要があります。
>>> class Bottles:
... def __init__(self, number):
... self._as_parameter_ = number
...
>>> bottles = Bottles(42)
>>> printf(b"%d bottles of beer\n", bottles)
42 bottles of beer
19
>>>
インスタンスのデータを_as_parameter_
インスタンス変数に保存したくない場合は、プロパティを定義して、要求に応じて属性を使用できるようにすることができます。
必要な引数タイプの指定(関数プロトタイプ)
argtypes
属性を設定することにより、DLLからエクスポートされる関数の必須引数タイプを指定することができます。
argtypes
はCデータ型のシーケンスである必要があります(printf
関数は、フォーマット文字列に応じて可変数とさまざまなタイプのパラメーターを受け取るため、ここではおそらく良い例ではありません。一方、これはこの機能を試すのに非常に便利です):
>>> 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
37
>>>
形式を指定すると、互換性のない引数タイプから保護され(C関数のプロトタイプと同様)、引数を有効なタイプに変換しようとします。
>>> printf(b"%d %d %d", 1, 2, 3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ArgumentError: argument 2: exceptions.TypeError: wrong type
>>> printf(b"%s %d %f\n", b"X", 2, 3)
X 2 3.000000
13
>>>
関数呼び出しに渡す独自のクラスを定義した場合、それらをargtypes
シーケンスで使用できるようにするには、from_param()
クラスメソッドを実装する必要があります。 from_param()
クラスメソッドは、関数呼び出しに渡されたPythonオブジェクトを受け取ります。タイプチェックなど、このオブジェクトが受け入れ可能であることを確認するために必要な処理を実行してから、オブジェクト自体、その_as_parameter_
を返します。属性、またはこの場合はC関数の引数として渡したいもの。 この場合も、結果は整数、文字列、バイト、 ctypes インスタンス、または_as_parameter_
属性を持つオブジェクトになります。
返品タイプ
デフォルトでは、関数はC int タイプを返すと想定されています。 関数オブジェクトのrestype
属性を設定することにより、他の戻りタイプを指定できます。
より高度な例を次に示します。strchr
関数を使用します。この関数は、文字列ポインターと文字を予期し、文字列へのポインターを返します。
>>> strchr = libc.strchr
>>> strchr(b"abcdef", ord("d"))
8059983
>>> strchr.restype = c_char_p # c_char_p is a pointer to a string
>>> strchr(b"abcdef", ord("d"))
b'def'
>>> print(strchr(b"abcdef", ord("x")))
None
>>>
上記のord("x")
呼び出しを回避したい場合は、argtypes
属性を設定すると、2番目の引数が1文字のPythonバイトオブジェクトからC文字に変換されます。
>>> strchr.restype = c_char_p
>>> strchr.argtypes = [c_char_p, c_char]
>>> strchr(b"abcdef", b"d")
'def'
>>> strchr(b"abcdef", b"def")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ArgumentError: argument 2: exceptions.TypeError: one character string expected
>>> print(strchr(b"abcdef", b"x"))
None
>>> strchr(b"abcdef", b"d")
'def'
>>>
外部関数が整数を返す場合は、呼び出し可能なPythonオブジェクト(関数やクラスなど)をrestype
属性として使用することもできます。 呼び出し可能オブジェクトは、C関数が返す integer で呼び出され、この呼び出しの結果が関数呼び出しの結果として使用されます。 これは、エラーの戻り値をチェックし、自動的に例外を発生させるのに役立ちます。
>>> GetModuleHandle = windll.kernel32.GetModuleHandleA
>>> def ValidHandle(value):
... if value == 0:
... raise WinError()
... return value
...
>>>
>>> GetModuleHandle.restype = ValidHandle
>>> GetModuleHandle(None)
486539264
>>> GetModuleHandle("something silly")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in ValidHandle
OSError: [Errno 126] The specified module could not be found.
>>>
WinError
は、Windows FormatMessage()
apiを呼び出してエラーコードの文字列表現を取得する関数であり、は例外を返します。 WinError
は、オプションのエラーコードパラメータを取ります。誰も使用されていない場合は、 GetLastError()を呼び出して取得します。
errcheck
属性を使用すると、はるかに強力なエラーチェックメカニズムを利用できることに注意してください。 詳細については、リファレンスマニュアルを参照してください。
ポインターの受け渡し(または:参照によるパラメーターの受け渡し)
C api関数は、データ型へのポインターをパラメーターとして期待する場合があります。おそらく、対応する場所に書き込むか、データが大きすぎて値を渡すことができない場合です。 これは、参照によるパラメータの受け渡しとも呼ばれます。
ctypes は、参照によってパラメーターを渡すために使用される byref()関数をエクスポートします。 pointer()関数でも同じ効果が得られますが、 pointer()は実際のポインターオブジェクトを構築するため、より多くの作業を行うため、を使用する方が高速です。 ] byref() Python自体にポインタオブジェクトが必要ない場合:
>>> i = c_int()
>>> f = c_float()
>>> s = create_string_buffer(b'\000' * 32)
>>> print(i.value, f.value, repr(s.value))
0 0.0 b''
>>> libc.sscanf(b"1 3.14 Hello", b"%d %f %s",
... byref(i), byref(f), s)
3
>>> print(i.value, f.value, repr(s.value))
1 3.1400001049 b'Hello'
>>>
構造体と共用体
構造体と共用体は、 ctypes モジュールで定義されている Structure および Union 基本クラスから派生する必要があります。 各サブクラスは、_fields_
属性を定義する必要があります。 _fields_
は、フィールド名とフィールドタイプを含む 2タプルのリストである必要があります。
フィールドタイプは、 c_int のような ctypes タイプ、またはその他の派生 ctypes タイプ(構造体、共用体、配列、ポインター)である必要があります。
これは、 x および y という名前の2つの整数を含むPOINT構造体の簡単な例であり、コンストラクターで構造体を初期化する方法も示しています。
>>> from ctypes import *
>>> class POINT(Structure):
... _fields_ = [("x", c_int),
... ("y", c_int)]
...
>>> point = POINT(10, 20)
>>> print(point.x, point.y)
10 20
>>> point = POINT(y=5)
>>> print(point.x, point.y)
0 5
>>> POINT(1, 2, 3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: too many initializers
>>>
ただし、はるかに複雑な構造を構築することはできます。 構造体をフィールドタイプとして使用することにより、構造体自体に他の構造体を含めることができます。
これは、左上と右下という名前の2つのポイントを含むRECT構造です。
>>> class RECT(Structure):
... _fields_ = [("upperleft", POINT),
... ("lowerright", POINT)]
...
>>> rc = RECT(point)
>>> print(rc.upperleft.x, rc.upperleft.y)
0 5
>>> print(rc.lowerright.x, rc.lowerright.y)
0 0
>>>
ネストされた構造体は、コンストラクターでいくつかの方法で初期化することもできます。
>>> r = RECT(POINT(1, 2), POINT(3, 4))
>>> r = RECT((1, 2), (3, 4))
フィールド記述子はクラスから取得できます。これらは有用な情報を提供できるため、デバッグに役立ちます。
>>> print(POINT.x)
<Field type=c_long, ofs=0, size=4>
>>> print(POINT.y)
<Field type=c_long, ofs=4, size=4>
>>>
警告
ctypes は、ビットフィールドを持つユニオンまたは構造体を値で関数に渡すことをサポートしていません。 これは32ビットx86で機能する可能性がありますが、一般的なケースで機能することがライブラリによって保証されていません。 ビットフィールドを持つ共用体と構造体は、常にポインタによって関数に渡される必要があります。
構造体/共用体の配置とバイト順序
デフォルトでは、StructureフィールドとUnionフィールドは、Cコンパイラと同じ方法で配置されます。 サブクラス定義で_pack_
クラス属性を指定することにより、この動作をオーバーライドすることができます。 これは正の整数に設定する必要があり、フィールドの最大配置を指定します。 これは、#pragma pack(n)
がMSVCでも行うことです。
ctypes は、構造体と共用体のネイティブバイト順序を使用します。 非ネイティブバイトオーダーの構造を構築するには、 BigEndianStructure 、 LittleEndianStructure 、BigEndianUnion
、およびLittleEndianUnion
基本クラスのいずれかを使用できます。 これらのクラスにポインタフィールドを含めることはできません。
構造体と共用体のビットフィールド
ビットフィールドを含む構造体と共用体を作成することが可能です。 ビットフィールドは整数フィールドでのみ可能です。ビット幅は、_fields_
タプルの3番目の項目として指定されます。
>>> class Int(Structure):
... _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>
>>>
配列
配列はシーケンスであり、同じタイプのインスタンスが固定数含まれています。
配列型を作成するための推奨される方法は、データ型に正の整数を乗算することです。
TenPointsArrayType = POINT * 10
これはやや人工的なデータ型の例であり、他のものの中でも特に4つのPOINTを含む構造です。
>>> from ctypes import *
>>> class POINT(Structure):
... _fields_ = ("x", c_int), ("y", c_int)
...
>>> class MyStruct(Structure):
... _fields_ = [("a", c_int),
... ("b", c_float),
... ("point_array", POINT * 4)]
>>>
>>> print(len(MyStruct().point_array))
4
>>>
インスタンスは、クラスを呼び出すことにより、通常の方法で作成されます。
arr = TenPointsArrayType()
for pt in arr:
print(pt.x, pt.y)
上記のコードは、配列の内容がゼロに初期化されているため、一連の0 0
行を出力します。
正しいタイプのイニシャライザーも指定できます。
>>> 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
>>>
ポインタ
ポインタインスタンスは、 ctypes タイプで pointer()関数を呼び出すことによって作成されます。
>>> from ctypes import *
>>> i = c_int(42)
>>> pi = pointer(i)
>>>
ポインタインスタンスには、ポインタが指すオブジェクト、上記のi
オブジェクトを返す contents 属性があります。
>>> pi.contents
c_long(42)
>>>
ctypes にはOOR(元のオブジェクトの戻り値)がないことに注意してください。属性を取得するたびに、新しい同等のオブジェクトが作成されます。
>>> pi.contents is i
False
>>> pi.contents is pi.contents
False
>>>
別の c_int インスタンスをポインターのcontents属性に割り当てると、ポインターはこれが格納されているメモリ位置を指すようになります。
>>> i = c_int(99)
>>> pi.contents = i
>>> pi.contents
c_long(99)
>>>
ポインタインスタンスは、整数でインデックスを付けることもできます。
>>> pi[0]
99
>>>
整数インデックスに割り当てると、ポイントされる値が変更されます。
>>> print(i)
c_long(99)
>>> pi[0] = 22
>>> print(i)
c_long(22)
>>>
0以外のインデックスを使用することもできますが、Cの場合と同様に、何をしているのかを知っている必要があります。任意のメモリ位置にアクセスまたは変更できます。 通常、この機能を使用するのは、C関数からポインターを受け取り、ポインターが実際には単一の項目ではなく配列を指していることを知っている場合のみです。
舞台裏では、 pointer()関数は、単にポインターインスタンスを作成するだけでなく、最初にポインタータイプを作成する必要があります。 これは、 POINTER()関数を使用して実行されます。この関数は、任意の ctypes 型を受け入れ、新しい型を返します。
>>> PI = POINTER(c_int)
>>> PI
<class 'ctypes.LP_c_long'>
>>> PI(42)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: expected c_long instead of int
>>> PI(c_int(42))
<ctypes.LP_c_long object at 0x...>
>>>
引数なしでポインタ型を呼び出すと、NULL
ポインタが作成されます。 NULL
ポインターのブール値はFalse
です。
>>> null_ptr = POINTER(c_int)()
>>> print(bool(null_ptr))
False
>>>
ctypes は、ポインターを逆参照するときにNULL
をチェックします(ただし、無効な非NULL
ポインターを逆参照すると、Pythonがクラッシュします)。
>>> null_ptr[0]
Traceback (most recent call last):
....
ValueError: NULL pointer access
>>>
>>> null_ptr[0] = 1234
Traceback (most recent call last):
....
ValueError: NULL pointer access
>>>
型変換
通常、ctypesは厳密な型チェックを行います。 つまり、関数のargtypes
リストにPOINTER(c_int)
がある場合、または構造体定義のメンバーフィールドのタイプとしてある場合、まったく同じタイプのインスタンスのみが受け入れられます。 このルールにはいくつかの例外があり、ctypesは他のオブジェクトを受け入れます。 たとえば、ポインタ型の代わりに互換性のある配列インスタンスを渡すことができます。 したがって、POINTER(c_int)
の場合、ctypesはc_intの配列を受け入れます。
>>> class Bar(Structure):
... _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])
...
1
2
3
>>>
また、argtypes
で関数の引数がポインタ型(POINTER(c_int)
など)として明示的に宣言されている場合、指定型のオブジェクト(この場合はc_int
)関数に渡すことができます。 この場合、ctypesは必要な byref()変換を自動的に適用します。
POINTERタイプフィールドをNULL
に設定するには、None
を割り当てることができます。
>>> bar.values = None
>>>
互換性のないタイプのインスタンスがある場合があります。 Cでは、あるタイプを別のタイプにキャストできます。 ctypes は、同じように使用できる cast()関数を提供します。 上で定義されたBar
構造体は、values
フィールドにPOINTER(c_int)
ポインターまたは c_int 配列を受け入れますが、他のタイプのインスタンスは受け入れません。
>>> bar.values = (c_byte * 4)()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: incompatible types, c_byte_Array_4 instance instead of LP_c_long instance
>>>
このような場合、 cast()関数が便利です。
cast()関数を使用して、ctypesインスタンスを別のctypesデータ型へのポインターにキャストできます。 cast()は、ある種のポインターに変換される、または変換できるctypesオブジェクトと、ctypesポインター型の2つのパラメーターを取ります。 これは、最初の引数と同じメモリブロックを参照する2番目の引数のインスタンスを返します。
>>> a = (c_byte * 4)()
>>> cast(a, POINTER(c_int))
<ctypes.LP_c_long object at ...>
>>>
したがって、 cast()を使用して、Bar
のvalues
フィールドに構造を割り当てることができます。
>>> bar = Bar()
>>> bar.values = cast((c_byte * 4)(), POINTER(c_int))
>>> print(bar.values[0])
0
>>>
不完全なタイプ
不完全な型は、メンバーがまだ指定されていない構造体、共用体、または配列です。 Cでは、これらは後で定義される前方宣言によって指定されます。
struct cell; /* forward declaration */
struct cell {
char *name;
struct cell *next;
};
ctypesコードへの簡単な変換は次のようになりますが、機能しません。
>>> class cell(Structure):
... _fields_ = [("name", c_char_p),
... ("next", POINTER(cell))]
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in cell
NameError: name 'cell' is not defined
>>>
新しいclass cell
はクラスステートメント自体では使用できないためです。 ctypes では、cell
クラスを定義し、後でクラスステートメントの後に_fields_
属性を設定できます。
>>> from ctypes import *
>>> class cell(Structure):
... pass
...
>>> cell._fields_ = [("name", c_char_p),
... ("next", POINTER(cell))]
>>>
試してみよう。 cell
の2つのインスタンスを作成し、それらを相互にポイントさせ、最後にポインターチェーンを数回たどります。
>>> 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=" ")
... p = p.next[0]
...
foo bar foo bar foo bar foo bar
>>>
コールバック関数
ctypes を使用すると、Python呼び出し可能オブジェクトからC呼び出し可能関数ポインターを作成できます。 これらは、コールバック関数と呼ばれることもあります。
まず、コールバック関数のクラスを作成する必要があります。 このクラスは、呼び出し規約、戻り値の型、およびこの関数が受け取る引数の数と型を認識しています。
CFUNCTYPE()ファクトリ関数は、cdecl
呼び出し規約を使用してコールバック関数の型を作成します。 Windowsでは、 WINFUNCTYPE()ファクトリ関数は、stdcall
呼び出し規約を使用してコールバック関数の型を作成します。
これらのファクトリ関数は両方とも、最初の引数として結果タイプを使用して呼び出され、コールバック関数は残りの引数として引数タイプを予期していました。
ここでは、標準Cライブラリのqsort()
関数を使用する例を示します。この関数は、コールバック関数を使用してアイテムを並べ替えるために使用されます。 qsort()
は、整数の配列をソートするために使用されます。
>>> IntArray5 = c_int * 5
>>> ia = IntArray5(5, 1, 7, 33, 99)
>>> qsort = libc.qsort
>>> qsort.restype = None
>>>
qsort()
は、並べ替えるデータへのポインター、データ配列内のアイテムの数、1つのアイテムのサイズ、および比較関数へのポインター、コールバックを使用して呼び出す必要があります。 次に、コールバックはアイテムへの2つのポインターを使用して呼び出され、最初のアイテムが2番目のアイテムよりも小さい場合は負の整数、等しい場合はゼロ、それ以外の場合は正の整数を返す必要があります。
したがって、コールバック関数は整数へのポインタを受け取り、整数を返す必要があります。 まず、コールバック関数のtype
を作成します。
>>> CMPFUNC = CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
>>>
開始するには、渡される値を示す簡単なコールバックを次に示します。
>>> def py_cmp_func(a, b):
... print("py_cmp_func", a[0], b[0])
... return 0
...
>>> cmp_func = CMPFUNC(py_cmp_func)
>>>
結果:
>>> qsort(ia, len(ia), sizeof(c_int), cmp_func)
py_cmp_func 5 1
py_cmp_func 33 99
py_cmp_func 7 33
py_cmp_func 5 7
py_cmp_func 1 7
>>>
これで、実際に2つの項目を比較して、有用な結果を返すことができます。
>>> def py_cmp_func(a, b):
... print("py_cmp_func", a[0], b[0])
... return a[0] - b[0]
...
>>>
>>> qsort(ia, len(ia), sizeof(c_int), CMPFUNC(py_cmp_func))
py_cmp_func 5 1
py_cmp_func 33 99
py_cmp_func 7 33
py_cmp_func 1 7
py_cmp_func 5 7
>>>
簡単に確認できるように、配列は次のように並べ替えられています。
>>> for i in ia: print(i, end=" ")
...
1 5 7 33 99
>>>
関数ファクトリはデコレータファクトリとして使用できるため、次のように記述した方がよいでしょう。
>>> @CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
... def py_cmp_func(a, b):
... print("py_cmp_func", a[0], b[0])
... return a[0] - b[0]
...
>>> qsort(ia, len(ia), sizeof(c_int), py_cmp_func)
py_cmp_func 5 1
py_cmp_func 33 99
py_cmp_func 7 33
py_cmp_func 1 7
py_cmp_func 5 7
>>>
ノート
CFUNCTYPE()オブジェクトへの参照は、Cコードから使用されている限り保持してください。 ctypes はそうではなく、そうでない場合、それらはガベージコレクションされ、コールバックが行われたときにプログラムをクラッシュさせる可能性があります。
また、コールバック関数がPythonの制御外で作成されたスレッドで呼び出された場合(例: コールバックを呼び出す外部コードによって)、ctypesはすべての呼び出しで新しいダミーのPythonスレッドを作成します。 この動作はほとんどの目的で正しいですが、 threading.local で保存された値は、同じCスレッドから呼び出された場合でも、異なるコールバック間で not 存続します。
DLLからエクスポートされた値へのアクセス
一部の共有ライブラリは、関数をエクスポートするだけでなく、変数もエクスポートします。 Pythonライブラリ自体の例は、 Py_OptimizeFlag です。これは、指定された -O または -OO フラグに応じて、0、1、または2に設定された整数です。始めるとき。
ctypes は、このタイプのin_dll()
クラスメソッドを使用してこのような値にアクセスできます。 pythonapi は、PythonCAPIへのアクセスを提供する事前定義されたシンボルです。
>>> opt_flag = c_int.in_dll(pythonapi, "Py_OptimizeFlag")
>>> print(opt_flag)
c_long(0)
>>>
インタプリタが -O で開始された場合、サンプルはc_long(1)
を印刷し、 -OO が指定された場合はc_long(2)
を印刷します。
ポインターの使用法も示す拡張例は、Pythonによってエクスポートされた PyImport_FrozenModules ポインターにアクセスします。
その値のドキュメントを引用する:
このポインターは、 struct _frozen レコードの配列を指すように初期化され、メンバーがすべて
NULL
またはゼロである配列で終了します。 フリーズされたモジュールがインポートされると、このテーブルで検索されます。 サードパーティのコードはこれを悪用して、動的に作成された凍結モジュールのコレクションを提供する可能性があります。
したがって、このポインタを操作することも有用であることがわかります。 サンプルサイズを制限するために、このテーブルを ctypes で読み取る方法のみを示します。
>>> from ctypes import *
>>>
>>> class struct_frozen(Structure):
... _fields_ = [("name", c_char_p),
... ("code", POINTER(c_ubyte)),
... ("size", c_int)]
...
>>>
struct _frozen データ型を定義したので、テーブルへのポインターを取得できます。
>>> FrozenTable = POINTER(struct_frozen)
>>> table = FrozenTable.in_dll(pythonapi, "PyImport_FrozenModules")
>>>
table
はstruct_frozen
レコードの配列に対するpointer
であるため、反復処理できますが、ポインターにはサイズがないため、ループが終了することを確認する必要があります。 。 遅かれ早かれ、アクセス違反などでクラッシュする可能性があるため、NULL
エントリに到達したときにループから抜け出すことをお勧めします。
>>> for item in table:
... if item.name is None:
... break
... print(item.name.decode("ascii"), item.size)
...
_frozen_importlib 31764
_frozen_importlib_external 41499
__hello__ 161
__phello__ -161
__phello__.spam 161
>>>
標準のPythonにフリーズされたモジュールとフリーズされたパッケージ(負のsize
メンバーで示される)があるという事実はよく知られておらず、テストにのみ使用されます。 たとえば、import __hello__
で試してみてください。
サプライズ
ctypes には、実際に起こること以外の何かを期待する可能性のあるいくつかのエッジがあります。
次の例を考えてみましょう。
>>> from ctypes import *
>>> class POINT(Structure):
... _fields_ = ("x", c_int), ("y", c_int)
...
>>> class RECT(Structure):
... _fields_ = ("a", POINT), ("b", POINT)
...
>>> p1 = POINT(1, 2)
>>> p2 = POINT(3, 4)
>>> rc = RECT(p1, p2)
>>> print(rc.a.x, rc.a.y, rc.b.x, rc.b.y)
1 2 3 4
>>> # now 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 1 2
を出力することを期待していました。 どうしたの? 上記のrc.a, rc.b = rc.b, rc.a
行の手順は次のとおりです。
>>> temp0, temp1 = rc.b, rc.a
>>> rc.a = temp0
>>> rc.b = temp1
>>>
temp0
とtemp1
は、上記のrc
オブジェクトの内部バッファーを引き続き使用しているオブジェクトであることに注意してください。 したがって、rc.a = temp0
を実行すると、temp0
のバッファ内容がrc
のバッファにコピーされます。 これにより、temp1
の内容が変更されます。 したがって、最後の割り当てrc.b = temp1
は、期待される効果がありません。
Structure、Unions、およびArraysからサブオブジェクトを取得しても、サブオブジェクトはコピーされません。代わりに、ルートオブジェクトの基になるバッファにアクセスするラッパーオブジェクトが取得されます。
予想とは異なる動作をする可能性のある別の例は次のとおりです。
>>> s = c_char_p()
>>> s.value = b"abc def ghi"
>>> s.value
b'abc def ghi'
>>> s.value is s.value
False
>>>
False
を印刷するのはなぜですか? ctypesインスタンスは、メモリブロックと、メモリの内容にアクセスするいくつかの記述子を含むオブジェクトです。 Pythonオブジェクトをメモリブロックに保存しても、オブジェクト自体は保存されません。代わりに、オブジェクトのcontents
が保存されます。 コンテンツに再度アクセスすると、毎回新しいPythonオブジェクトが作成されます。
可変サイズのデータ型
ctypes は、可変サイズの配列と構造のサポートを提供します。
resize()関数を使用して、既存のctypesオブジェクトのメモリバッファーのサイズを変更できます。 この関数は、オブジェクトを最初の引数として受け取り、要求されたサイズ(バイト単位)を2番目の引数として受け取ります。 メモリブロックをオブジェクトタイプで指定された自然メモリブロックより小さくすることはできません。これを実行しようとすると、 ValueError が発生します。
>>> short_array = (c_short * 4)()
>>> print(sizeof(short_array))
8
>>> resize(short_array, 4)
Traceback (most recent call last):
...
ValueError: minimum size is 8
>>> resize(short_array, 32)
>>> sizeof(short_array)
32
>>> sizeof(type(short_array))
8
>>>
これは素晴らしいことですが、この配列に含まれる追加の要素にどのようにアクセスしますか? タイプはまだ4つの要素しか認識していないため、他の要素にアクセスするとエラーが発生します。
>>> short_array[:]
[0, 0, 0, 0]
>>> short_array[7]
Traceback (most recent call last):
...
IndexError: invalid index
>>>
ctypes で可変サイズのデータ型を使用する別の方法は、Pythonの動的な性質を使用し、必要なサイズがすでにわかった後で、ケースバイケースでデータ型を(再)定義することです。
ctypesリファレンス
外国の機能
前のセクションで説明したように、外部関数は、ロードされた共有ライブラリの属性としてアクセスできます。 この方法で作成された関数オブジェクトは、デフォルトで任意の数の引数を受け入れ、任意のctypesデータインスタンスを引数として受け入れ、ライブラリローダーによって指定されたデフォルトの結果タイプを返します。 これらはプライベートクラスのインスタンスです。
- class ctypes._FuncPtr
C呼び出し可能外部関数の基本クラス。
外部関数のインスタンスもC互換のデータ型です。 それらはC関数ポインタを表します。
この動作は、外部関数オブジェクトの特別な属性に割り当てることでカスタマイズできます。
- restype
ctypesタイプを割り当てて、外部関数の結果タイプを指定します。 void には
None
を使用します。これは、何も返さない関数です。ctypes型ではない呼び出し可能なPythonオブジェクトを割り当てることができます。この場合、関数はC int を返すと想定され、呼び出し可能なものはこの整数で呼び出され、さらなる処理またはエラーが可能になります。チェック中。 これを使用することは非推奨です。より柔軟な後処理またはエラーチェックのために、ctypesデータ型を restype として使用し、callableを errcheck 属性に割り当てます。
- argtypes
ctypesタイプのタプルを割り当てて、関数が受け入れる引数タイプを指定します。
stdcall
呼び出し規約を使用する関数は、このタプルの長さと同じ数の引数でのみ呼び出すことができます。 C呼び出し規約を使用する関数は、追加の不特定の引数も受け入れます。外部関数が呼び出されると、各実際の引数が argtypes タプル内のアイテムの
from_param()
クラスメソッドに渡されます。このメソッドを使用すると、実際の引数を外部関数が使用するオブジェクトに適合させることができます。を受け入れます。 たとえば、 argtypes タプルの c_char_p アイテムは、ctypes変換ルールを使用して、引数として渡された文字列をバイトオブジェクトに変換します。New:ctypesタイプではないargtypesにアイテムを配置できるようになりましたが、各アイテムには、引数として使用可能な値(整数、文字列、ctypesインスタンス)を返す
from_param()
メソッドが必要です。 これにより、カスタムオブジェクトを関数パラメーターとして適合させることができるアダプターを定義できます。
- errcheck
Python関数または別の呼び出し可能オブジェクトをこの属性に割り当てます。 呼び出し可能オブジェクトは、3つ以上の引数で呼び出されます。
- callable(result, func, arguments)
result は、
restype
属性で指定されているように、外部関数が返すものです。func は外部関数オブジェクト自体です。これにより、同じ呼び出し可能オブジェクトを再利用して、複数の関数の結果をチェックまたは後処理することができます。
arguments は、関数呼び出しに最初に渡されたパラメーターを含むタプルです。これにより、使用される引数の動作を特殊化できます。
この関数が返すオブジェクトは、外部関数呼び出しから返されますが、結果値を確認して、外部関数呼び出しが失敗した場合に例外を発生させることもできます。
- exception ctypes.ArgumentError
- この例外は、外部関数呼び出しが渡された引数の1つを変換できない場合に発生します。
関数プロトタイプ
関数プロトタイプをインスタンス化することにより、外部関数を作成することもできます。 関数プロトタイプは、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のみ:返された関数プロトタイプは、 WINFUNCTYPE()が CFUNCTYPE()と同じであるWindows CEを除いて、
stdcall
呼び出し規約を使用する関数を作成します。 この関数は、呼び出し中に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 は2タプル
(name_or_ordinal, library)
である必要があります。 最初の項目は、エクスポートされた関数の名前(文字列)、またはエクスポートされた関数の序数(小整数)です。 2番目の項目は共有ライブラリインスタンスです。
- prototype(vtbl_index, name[, paramflags[, iid]])
COMメソッドを呼び出す外部関数を返します。 vtbl_index は、仮想関数テーブルへのインデックスであり、小さな非負の整数です。 name はCOMメソッドの名前です。 iid は、拡張エラー報告で使用されるインターフェース識別子へのオプションのポインターです。
COMメソッドは、特別な呼び出し規約を使用します。
argtypes
タプルで指定されているパラメーターに加えて、最初の引数としてCOMインターフェイスへのポインターが必要です。オプションの paramflags パラメーターは、上記の機能よりもはるかに多くの機能を備えた外部関数ラッパーを作成します。
paramflags は、
argtypes
と同じ長さのタプルである必要があります。このタプルの各項目には、パラメーターに関する詳細情報が含まれています。これは、1つ、2つ、または3つの項目を含むタプルである必要があります。
最初の項目は、パラメーターの方向フラグの組み合わせを含む整数です。
- 1
- 関数への入力パラメーターを指定します。
- 2
- 出力パラメータ。 外部関数は値を入力します。
- 4
- デフォルトで整数ゼロになる入力パラメーター。
オプションの2番目の項目は、文字列としてのパラメータ名です。 これを指定すると、名前付きパラメーターを使用して外部関数を呼び出すことができます。
オプションの3番目の項目は、このパラメーターのデフォルト値です。
この例は、Windows MessageBoxW
関数をラップして、デフォルトのパラメーターと名前付き引数をサポートする方法を示しています。 WindowsヘッダーファイルからのC宣言は次のとおりです。
WINUSERAPI int WINAPI
MessageBoxW(
HWND hWnd,
LPCWSTR lpText,
LPCWSTR lpCaption,
UINT uType);
ctypes によるラッピングは次のとおりです。
>>> 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)
MessageBox
外部関数は、次の方法で呼び出すことができるようになりました。
>>> MessageBox()
>>> MessageBox(text="Spam, spam, spam")
>>> MessageBox(flags=2, text="foo bar")
2番目の例は、出力パラメーターを示しています。 win32 GetWindowRect
関数は、呼び出し元が指定する必要のあるRECT
構造体にコピーすることにより、指定されたウィンドウのサイズを取得します。 C宣言は次のとおりです。
WINUSERAPI BOOL WINAPI
GetWindowRect(
HWND hWnd,
LPRECT lpRect);
ctypes によるラッピングは次のとおりです。
>>> 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)
>>>
出力パラメーターを持つ関数は、出力パラメーター値が1つある場合は自動的に返し、複数ある場合は出力パラメーター値を含むタプルを返すため、GetWindowRect関数は呼び出されたときにRECTインスタンスを返すようになりました。
出力パラメータをerrcheck
プロトコルと組み合わせて、さらに出力処理とエラーチェックを行うことができます。 win32 GetWindowRect
api関数はBOOL
を返し、成功または失敗を通知します。そのため、この関数はエラーチェックを実行し、api呼び出しが失敗したときに例外を発生させます。
>>> def errcheck(result, func, args):
... if not result:
... raise WinError()
... return args
...
>>> GetWindowRect.errcheck = errcheck
>>>
errcheck
関数が受け取った引数タプルを変更せずに返す場合、 ctypes は出力パラメーターに対して行う通常の処理を続行します。 RECT
インスタンスの代わりにウィンドウ座標のタプルを返したい場合は、関数内のフィールドを取得して返すことができます。通常の処理は実行されなくなります。
>>> def errcheck(result, func, args):
... if not result:
... raise WinError()
... rc = args[1]
... return rc.left, rc.top, rc.bottom, rc.right
...
>>> GetWindowRect.errcheck = errcheck
>>>
ユーティリティ機能
- 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コードに対応します。(((char *)&obj) + offset)
返されたオブジェクトは、外部関数呼び出しパラメーターとしてのみ使用できます。
pointer(obj)
と同様に動作しますが、構築ははるかに高速です。
- ctypes.cast(obj, type)
- この関数は、Cのキャスト演算子に似ています。 obj と同じメモリブロックを指す type の新しいインスタンスを返します。 type はポインター型である必要があり、 obj はポインターとして解釈できるオブジェクトである必要があります。
- ctypes.create_string_buffer(init_or_size, size=None)
この関数は、可変文字バッファーを作成します。 返されるオブジェクトは、 c_char のctypes配列です。
init_or_size は、配列のサイズを指定する整数、または配列項目を初期化するために使用されるバイトオブジェクトである必要があります。
バイトオブジェクトが最初の引数として指定されている場合、配列の最後の要素がNUL終了文字になるように、バッファーはその長さより1項目大きくなります。 バイトの長さを使用しない場合は、配列のサイズを指定できる2番目の引数として整数を渡すことができます。
- ctypes.create_unicode_buffer(init_or_size, size=None)
この関数は、可変のUnicode文字バッファを作成します。 返されるオブジェクトは、 c_wchar のctypes配列です。
init_or_size は、配列のサイズを指定する整数、または配列項目を初期化するために使用される文字列である必要があります。
文字列が最初の引数として指定されている場合、バッファは文字列の長さより1項目大きくなるため、配列の最後の要素はNUL終了文字になります。 文字列の長さを使用しない場合は、配列のサイズを指定できる2番目の引数として整数を渡すことができます。
- 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のみ:エラーコードコードのテキストによる説明を返します。 エラーコードが指定されていない場合、最後のエラーコードは、WindowsAPI関数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)
- 標準のCmemmoveライブラリ関数と同じです。 count バイトを src から dst にコピーします。 dst および src は、ポインターに変換できる整数またはctypesインスタンスである必要があります。
- ctypes.memset(dst, c, count)
- 標準のCmemsetライブラリ関数と同じです。アドレス dst のメモリブロックを値 c の count バイトで埋めます。 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)
- この関数は、メモリアドレスアドレスで始まるC文字列をバイトオブジェクトとして返します。 サイズが指定されている場合はサイズとして使用され、指定されていない場合、文字列はゼロで終了していると見なされます。
- ctypes.WinError(code=None, descr=None)
Windowsのみ:この関数はおそらくctypesで最悪の名前のものです。 OSErrorのインスタンスを作成します。 code が指定されていない場合、
GetLastError
が呼び出されてエラーコードが判別されます。 descr が指定されていない場合、 FormatError()が呼び出され、エラーのテキストによる説明が取得されます。バージョン3.3で変更: WindowsError のインスタンスが以前作成されていました。
- ctypes.wstring_at(address, size=- 1)
- この関数は、メモリアドレスアドレスで始まるワイド文字列を文字列として返します。 size が指定されている場合は、文字列の文字数として使用されます。指定されていない場合、文字列はゼロで終了していると見なされます。
データ型
- class ctypes._CData
この非公開クラスは、すべてのctypesデータ型に共通の基本クラスです。 特に、すべてのctypesタイプのインスタンスには、C互換データを保持するメモリブロックが含まれています。 メモリブロックのアドレスは、 addressof()ヘルパー関数によって返されます。 別のインスタンス変数は _objects として公開されています。 これには、メモリブロックにポインタが含まれている場合に備えて存続させる必要がある他のPythonオブジェクトが含まれています。
ctypesデータ型の一般的なメソッド。これらはすべてクラスメソッドです(正確には、 metaclass のメソッドです)。
- from_buffer(source[, offset])
このメソッドは、 source オブジェクトのバッファーを共有するctypesインスタンスを返します。 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データインスタンスがメモリブロック自体を割り当てた場合はtrue、それ以外の場合はfalseです。
- _objects
このメンバーは、
None
か、メモリブロックの内容を有効に保つために存続させる必要のあるPythonオブジェクトを含むディクショナリのいずれかです。 このオブジェクトはデバッグ用にのみ公開されています。 この辞書の内容は絶対に変更しないでください。
基本的なデータ型
- class ctypes._SimpleCData
この非公開クラスは、すべての基本的なctypesデータ型の基本クラスです。 基本的なctypesデータ型の共通属性が含まれているため、ここで説明します。 _SimpleCData は _CData のサブクラスであるため、メソッドと属性を継承します。 ポインタを含まない、または含まないctypesデータ型をpickle化できるようになりました。
インスタンスには1つの属性があります。
- value
この属性には、インスタンスの実際の値が含まれます。 整数型およびポインタ型の場合は整数、文字型の場合は単一文字バイトのオブジェクトまたは文字列、文字型ポインタ型の場合はPythonバイトのオブジェクトまたは文字列です。
value
属性がctypesインスタンスから取得されると、通常、毎回新しいオブジェクトが返されます。 ctypes は、元のオブジェクトの戻りを実装しません。常に新しいオブジェクトが構築されます。 同じことが他のすべてのctypesオブジェクトインスタンスにも当てはまります。
基本的なデータ型は、外部関数呼び出しの結果として返される場合、またはたとえば構造体フィールドのメンバーや配列項目を取得することによって返される場合、ネイティブのPython型に透過的に変換されます。 つまり、外部関数のrestype
が c_char_p の場合、常にPythonバイトオブジェクトを受け取ります。ではなく c_char_p インスタンスを受け取ります。 。
基本的なデータ型のサブクラスは、この動作を継承しませんしません。 したがって、外部関数restype
が c_void_p のサブクラスである場合、関数呼び出しからこのサブクラスのインスタンスを受け取ります。 もちろん、value
属性にアクセスすることで、ポインターの値を取得できます。
これらは基本的なctypesデータ型です。
- class ctypes.c_byte
- C signed char データ型を表し、値を小整数として解釈します。 コンストラクターは、オプションの整数初期化子を受け入れます。 オーバーフローチェックは行われません。
- class ctypes.c_char
- C char データ型を表し、値を1文字として解釈します。 コンストラクターはオプションの文字列初期化子を受け入れます。文字列の長さは正確に1文字でなければなりません。
- class ctypes.c_char_p
- ゼロで終了する文字列を指す場合、C char * データ型を表します。 バイナリデータも指す可能性のある一般的な文字ポインタの場合、
POINTER(c_char)
を使用する必要があります。 コンストラクターは、整数アドレスまたはバイトオブジェクトを受け入れます。
- class ctypes.c_double
- C double データ型を表します。 コンストラクターは、オプションのfloat初期化子を受け入れます。
- class ctypes.c_longdouble
- C long double データ型を表します。 コンストラクターは、オプションのfloat初期化子を受け入れます。
sizeof(long double) == sizeof(double)
が c_double のエイリアスであるプラットフォーム。
- class ctypes.c_float
- C float データ型を表します。 コンストラクターは、オプションのfloat初期化子を受け入れます。
- class ctypes.c_int
- C signed int データ型を表します。 コンストラクターは、オプションの整数初期化子を受け入れます。 オーバーフローチェックは行われません。
sizeof(int) == sizeof(long)
のプラットフォームでは、 c_long のエイリアスです。
- class ctypes.c_int8
- C8ビット signed int データ型を表します。 通常、 c_byte のエイリアス。
- class ctypes.c_int16
- C16ビット signed int データ型を表します。 通常、 c_short のエイリアス。
- class ctypes.c_int32
- C32ビット signed int データ型を表します。 通常、 c_int のエイリアス。
- class ctypes.c_int64
- C64ビット signed int データ型を表します。 通常、 c_longlong のエイリアス。
- class ctypes.c_long
- C signed long データ型を表します。 コンストラクターは、オプションの整数初期化子を受け入れます。 オーバーフローチェックは行われません。
- class ctypes.c_longlong
- C signed long long データ型を表します。 コンストラクターは、オプションの整数初期化子を受け入れます。 オーバーフローチェックは行われません。
- class ctypes.c_short
- C 符号付き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
- C8ビット unsigned int データ型を表します。 通常、 c_ubyte のエイリアス。
- class ctypes.c_uint16
- C16ビット unsigned int データ型を表します。 通常、 c_ushort のエイリアス。
- class ctypes.c_uint32
- C32ビット unsigned int データ型を表します。 通常、 c_uint のエイリアス。
- class ctypes.c_uint64
- C64ビット 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
データ型を表し、値を1文字のUnicode文字列として解釈します。 コンストラクターはオプションの文字列初期化子を受け入れます。文字列の長さは正確に1文字でなければなりません。
- 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
モジュールは、HWND
、WPARAM
、DWORD
など、他のWindows固有のデータ型を提供します。 MSG
やRECT
のようないくつかの便利な構造も定義されています。
構造化データ型
- class ctypes.Union(*args, **kw)
- ネイティブバイトオーダーのユニオンの抽象基本クラス。
- class ctypes.BigEndianStructure(*args, **kw)
- ビッグエンディアンバイトオーダーの構造体の抽象基本クラス。
- class ctypes.LittleEndianStructure(*args, **kw)
- リトルエンディアンバイトオーダーの構造体の抽象基本クラス。
非ネイティブバイトオーダーの構造体には、ポインター型フィールド、またはポインター型フィールドを含むその他のデータ型を含めることはできません。
- class ctypes.Structure(*args, **kw)
native バイトオーダーの構造体の抽象基本クラス。
具体的な構造体と共用体の型は、これらの型の1つをサブクラス化して作成し、少なくとも _fields_ クラス変数を定義する必要があります。 ctypes は、直接属性アクセスによるフィールドの読み取りと書き込みを可能にする記述子を作成します。 これらは
- _fields_
構造フィールドを定義するシーケンス。 アイテムは2タプルまたは3タプルである必要があります。 最初の項目はフィールドの名前であり、2番目の項目はフィールドのタイプを指定します。 任意のctypesデータ型にすることができます。
c_int のような整数型フィールドの場合、3番目のオプション項目を指定できます。 フィールドのビット幅を定義する小さな正の整数である必要があります。
フィールド名は、1つの構造体または共用体内で一意である必要があります。 これはチェックされていません。名前が繰り返される場合、アクセスできるフィールドは1つだけです。
_fields_ クラス変数 after をStructureサブクラスを定義するクラスステートメントで定義することができます。これにより、直接または間接的に自身を参照するデータ型を作成できます。
class List(Structure): pass List._fields_ = [("pnext", POINTER(List)), ... ]
ただし、 _fields_ クラス変数は、型を最初に使用する前に定義する必要があります(インスタンスが作成され、 sizeof()が呼び出されるなど)。 後で _fields_ クラス変数に割り当てると、AttributeErrorが発生します。
構造タイプのサブサブクラスを定義することができます。これらは、基本クラスのフィールドに加えて、サブサブクラスで定義されている _fields_ を継承します。
- _pack_
インスタンス内の構造フィールドの配置をオーバーライドできるようにするオプションの小整数。 _pack_ は、 _fields_ が割り当てられているときにすでに定義されている必要があります。定義されていない場合、効果はありません。
- _anonymous_
名前のない(匿名の)フィールドの名前をリストするオプションのシーケンス。 _anonymous_ は、 _fields_ が割り当てられているときにすでに定義されている必要があります。定義されていない場合、効果はありません。
この変数にリストされるフィールドは、構造体または共用体タイプのフィールドである必要があります。 ctypes は、構造体または共用体フィールドを作成する必要なしに、ネストされたフィールドに直接アクセスできるようにする構造体タイプの記述子を作成します。
タイプの例を次に示します(Windows)。
class _U(Union): _fields_ = [("lptdesc", POINTER(TYPEDESC)), ("lpadesc", POINTER(ARRAYDESC)), ("hreftype", HREFTYPE)] class TYPEDESC(Structure): _anonymous_ = ("u",) _fields_ = [("u", _U), ("vt", VARTYPE)]
TYPEDESC
構造体はCOMデータ型を記述し、vt
フィールドはユニオンフィールドのどれが有効であるかを指定します。u
フィールドは匿名フィールドとして定義されているため、TYPEDESCインスタンスから直接メンバーにアクセスできるようになりました。td.lptdesc
とtd.u.lptdesc
は同等ですが、一時的なユニオンインスタンスを作成する必要がないため、前者の方が高速です。td = TYPEDESC() td.vt = VT_PTR td.lptdesc = POINTER(some_type) td.u.lptdesc = POINTER(some_type)
構造体のサブサブクラスを定義することが可能であり、それらは基本クラスのフィールドを継承します。 サブクラス定義に個別の _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
ポインタが指すオブジェクトを返します。 この属性に割り当てると、割り当てられたオブジェクトを指すようにポインタが変更されます。