16.16. ctypes --- Pythonのための外部関数ライブラリ


ctypes は Python のための外部関数ライブラリです。このライブラリは C と互換性のあるデータ型を提供し、動的リンク/共有ライブラリ内の関数呼び出しを可能にします。動的リンク/共有ライブラリを純粋な Python でラップするために使うことができます。

16.16.1. ctypesチュートリアル

注意: このチュートリアルのコードサンプルは動作確認のために doctest を使います。コードサンプルの中には Linux、 Windows、あるいは Mac OS X 上で異なる動作をするものがあるため、サンプルのコメントに doctest 命令を入れてあります。

注意: いくつかのコードサンプルで ctypes の c_int 型を参照しています。 sizeof(long) == sizeof(int) であるようなプラットフォームでは、この型は c_long のエイリアスです。そのため、 c_int 型を想定しているときに c_long が表示されたとしても、混乱しないようにしてください --- 実際には同じ型なのです。

16.16.1.2. ロードした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
>>>

kernel32user32 のような win32 システム dll は、多くの場合関数の UNICODE バージョンに加えて ANSI バージョンもエクスポートすることに注意してください。 UNICODE バージョンは後ろに W が付いた名前でエクスポートされ、 ANSI バージョンは A が付いた名前でエクスポートされます。与えられたモジュールの モジュールハンドル を返す win32 GetModuleHandle 関数は次のような C プロトタイプを持ちます。 UNICODE バージョンが定義されているかどうかにより GetModuleHandle としてどちらか一つを公開するためにマクロが使われます:

/* ANSI version */
HMODULE GetModuleHandleA(LPCSTR lpModuleName);
/* UNICODE version */
HMODULE GetModuleHandleW(LPCWSTR lpModuleName);

windll は魔法を使ってどちらか一つを選ぶようなことはしません。GetModuleHandleA もしくは GetModuleHandleW を明示的に指定して必要とするバージョンにアクセスし、バイト列か文字列を使ってそれぞれ呼び出さなければなりません。

時には、 dll が関数を "??2@YAPAXI@Z" のような 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
>>>

16.16.1.3. 関数を呼び出す

これらの関数は他の Python 呼び出し可能オブジェクトと同じように呼び出すことができます。この例では time() 関数 (Unixエポックからのシステム時間を秒単位で返す) と、 GetModuleHandleA() 関数 (win32モジュールハンドルを返す) を使います。

この例は両方の関数を NULL ポインタとともに呼び出します (None を NULL ポインタとして使う必要があります):

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

注釈

不正な数の引数が渡されたことを検知した場合、 ctypes は関数を呼び出した後に ValueError を送出することがあります。 この動作には依存するべきではありません。 この動作は 3.6.2 で非推奨であり、 3.7 で削除予定です。

cdecl 呼び出し規約を使って stdcall 関数を呼び出したときには、 ValueError が送出されます。逆の場合も同様です:

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

しかしそれでも他に ctypes で Python がクラッシュする状況はあるので、どちらにせよ気を配るべきです。クラッシュのデバッグには、 faulthandler モジュールが役に立つ場合があります (例えば、誤った C ライブラリ呼び出しによって引き起こされたセグメンテーション違反) 。

None 、整数、バイト列オブジェクトおよび (Unicode) 文字列だけが、こうした関数呼び出しにおいてパラメータとして直接使えるネイティブの Python オブジェクトです。 None は C の NULL ポインタとして渡され、バイト文字列とユニコード文字列はそのデータを含むメモリブロックへのポインタ (char * または wchar_t *) として渡されます。 Python 整数はプラットホームのデフォルトの C int 型として渡され、その値は C int 型に合うようにマスクされます。

他のパラメータ型をもつ関数呼び出しに移る前に、 ctypes データ型についてさらに学ぶ必要があります。

16.16.1.4. 基本データ型

ctypes ではいくつもの C 互換のプリミティブなデータ型を定義しています:

ctypes の型 C の型 Python の型
c_bool _Bool bool (1)
c_char char 1文字のバイト列オブジェクト
c_wchar wchar_t 1文字の文字列
c_byte char int
c_ubyte unsigned char int
c_short short int
c_ushort unsigned short int
c_int int int
c_uint unsigned int int
c_long 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 float 浮動小数点数
c_double double 浮動小数点数
c_longdouble long double 浮動小数点数
c_char_p char * (NUL 終端) バイト列オブジェクトまたは None
c_wchar_p wchar_t * (NUL 終端) 文字列または None
c_void_p void * 整数または None
  1. コンストラクタは任意のオブジェクトをその真偽値として受け取ります。

これら全ての型はその型を呼び出すことによって作成でき、オプションとして型と値が合っている初期化子を指定することができます:

>>> 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 のインスタンスへ代入すると、変わるのは指している メモリ位置 であって、メモリブロックの 内容ではありません (これは当然で、なぜなら、 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() 関数は初期の ctypes リリースにあった c_string() 関数だけでなく、 (エイリアスとしてはまだ利用できる) c_buffer() 関数をも置き換えるものです。 C の型 wchar_t の Unicode 文字を含む変更可能なメモリブロックを作成するには、 create_unicode_buffer() 関数を使ってください。

16.16.1.5. 続・関数を呼び出す

printf は sys.stdout では なく 、本物の標準出力チャンネルへプリントすることに注意してください。したがって、これらの例はコンソールプロンプトでのみ動作し、 IDLEPythonWin では動作しません。:

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

前に述べたように、必要な C のデータ型へ変換できるようにするためには、整数、文字列およびバイト列オブジェクトを除くすべての Python 型を対応する ctypes 型でラップしなければなりません:

>>> printf(b"An int %d, a double %f\n", 1234, c_double(3.14))
An int 1234, a double 3.140000
31
>>>

16.16.1.6. 自作のデータ型とともに関数を呼び出す

自作のクラスのインスタンスを関数引数として使えるように、 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_ インスタンス変数にインスタンスのデータを保持したくない場合は、必要に応じて利用できる属性を作る property を定義しても構いません。

16.16.1.7. 要求される引数の型を指定する (関数プロトタイプ)

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_ 属性をもつオブジェクトであるべきです。

16.16.1.8. 戻り値の型

デフォルトでは、関数は C int を返すと仮定されます。他の戻り値の型を指定するには、関数オブジェクトの restype 属性に設定します。

さらに高度な例として、 strchr 関数を使います。この関数は文字列ポインタと char を受け取り、文字列へのポインタを返します。:

>>> 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 属性を設定することができます。二番目の引数が一文字の Python バイト列オブジェクトから C の char へ変換されます:

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

外部関数が整数を返す場合は、 restype 属性として呼び出し可能な Python オブジェクト (例えば、関数またはクラス) を使うこともできます。呼び出し可能オブジェクトは C 関数が返す 整数 とともに呼び出され、この呼び出しの結果は関数呼び出しの結果として使われるでしょう。これはエラーの戻り値をチェックして自動的に例外を送出させるために役に立ちます。:

>>> 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 属性によってもっと強力なエラーチェック機構を利用できることに注意してください。詳細はリファレンスマニュアルを参照してください。

16.16.1.9. ポインタを渡す(または、パラメータの参照渡し)

時には、 C api 関数がパラメータのデータ型として ポインタ を想定していることがあります。おそらくパラメータと同一の場所に書き込むためか、もしくはそのデータが大きすぎて値渡しできない場合です。これは パラメータの参照渡し としても知られています。

ctypesbyref() 関数をエクスポートしており、パラメータを参照渡しするために使用します。 pointer() 関数を使っても同じ効果が得られます。しかし、 pointer() は本当のポインタオブジェクトを構築するためより多くの処理を行うことから、 Python 側でポインタオブジェクト自体を必要としないならば byref() を使う方がより高速です。:

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

16.16.1.10. 構造体と共用体

構造体と共用体は ctypes モジュールに定義されている Structure および Union ベースクラスからの派生クラスでなければなりません。それぞれのサブクラスは _fields_ 属性を定義する必要があります。 _fields_フィールド名フィールド型 を持つ 2要素タプル のリストでなければなりません。

フィールド型は c_int か他の ctypes 型 (構造体、共用体、配列、ポインタ) から派生した ctypes 型である必要があります。

以下は、 xy という名前の二つの整数からなる簡単な 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>
ValueError: too many initializers
>>>

しかし、もっと複雑な構造体を構築することもできます。ある構造体は、他の構造体をフィールド型として使うことで、他の構造体を含むことができます。

upperleftlowerright という名前の二つの POINT を持つ 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))

フィールド descriptor (記述子)は クラス から取り出せます。デバッグするときに役に立つ情報を得ることができます:

>>> print(POINT.x)
<Field type=c_long, ofs=0, size=4>
>>> print(POINT.y)
<Field type=c_long, ofs=4, size=4>
>>>

警告

ctypes では、ビットフィールドのある共用体や構造体の関数への値渡しはサポートしていません。これは 32-bit の x86 環境では動くかもしれませんが、このライブラリでは一般の場合に動作することは保証していません。

16.16.1.11. 構造体/共用体アライメントとバイトオーダー

デフォルトでは、構造体 (Structure) と共用体(Union) のフィールドは C コンパイラが行うのと同じ方法でアライメントされています。サブクラスを定義するときに _pack_ クラス属性を指定することでこの動作を変えることは可能です。このクラス属性には正の整数を設定する必要があり、フィールドの最大アライメントを指定します。これは MSVC で #pragma pack(n) が行っていること同じです。

ctypes は Structure と Union に対してネイティブのバイトオーダーを使います。ネイティブではないバイトオーダーの構造体を作成するには、 BigEndianStructure, LittleEndianStructure, BigEndianUnion および LittleEndianUnion ベースクラスの中の一つを使います。これらのクラスにポインタフィールドを持たせることはできません。

16.16.1.12. 構造体と共用体におけるビットフィールド

ビットフィールドを含む構造体と共用体を作ることができます。ビットフィールドは整数フィールドに対してのみ作ることができ、ビット幅は _fields_ タプルの第三要素で指定します。:

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

16.16.1.13. 配列

配列 (Array) はシーケンスであり、決まった数の同じ型のインスタンスを持ちます。

推奨されている配列の作成方法はデータ型に正の整数を掛けることです。:

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

16.16.1.14. ポインタ

ポインタのインスタンスは ctypes 型に対して pointer() 関数を呼び出して作成します。:

>>> from ctypes import *
>>> i = c_int(42)
>>> pi = pointer(i)
>>>

次のように、ポインタインスタンスは、ポインタが指すオブジェクト (上の例では i) を返す contents 属性を持ちます:

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

ctypes は OOR (original object return 、元のオブジェクトを返すこと) ではないことに注意してください。属性を取り出す度に、新しい同等のオブジェクトを作成しているのです。:

>>> 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() 関数は単にポインタインスタンスを作成するという以上のことを行っています。はじめにポインタ を作成する必要があります。これは任意の ctypes 型を受け取る POINTER() 関数を使って行われ、新しい型を返します:

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

16.16.1.15. 型変換

たいていの場合、 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 構造体は POINTER(c_int) ポインタまたは c_int 配列を values フィールドに対して受け取り、他の型のインスタンスは受け取りません:

>>> 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 ポインタ型を取ります。そして、第二引数のインスタンスを返します。このインスタンスは第一引数と同じメモリブロックを参照しています:

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

16.16.1.16. 不完全型

不完全型 はメンバーがまだ指定されていない構造体、共用体もしくは配列です。 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 のインスタンスを二つ作り、互いに参照し合うようにします。最後に、つながったポインタを何度かたどります。:

>>> c1 = cell()
>>> c1.name = "foo"
>>> c2 = cell()
>>> c2.name = "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
>>>

16.16.1.17. コールバック関数

ctypes は C の呼び出し可能な関数ポインタを Python 呼び出し可能オブジェクトから作成できるようにします。これらは コールバック関数 と呼ばれることがあります。

最初に、コールバック関数のためのクラスを作る必要があります。そのクラスには呼び出し規約、戻り値の型およびこの関数が受け取る引数の数と型についての情報があります。

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() はソートするデータを指すポインタ、データ配列の要素の数、要素の一つの大きさ、およびコールバック関数である比較関数へのポインタを引数に渡して呼び出さなければなりません。そして、コールバック関数は要素を指す二つのポインタを渡されて呼び出され、一番目が二番目より小さいなら負の数を、等しいならゼロを、それ以外なら正の数を返さなければなりません。

コールバック関数は整数へのポインタを受け取り、整数を返す必要があります。まず、コールバック関数のための 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
>>>

注釈

C コードから CFUNCTYPE() オブジェクトが使用される限り、そのオブジェクトへの参照を確実に保持してください。 ctypes は参照を保持しないため、あなたが参照を保持しないと、オブジェクトはガベージコレクションの対象となり、コールバックが行われたときにプログラムをクラッシュさせる場合があります。

同様に、コールバック関数が Python の管理外 (例えば、コールバックを呼び出す外部のコード) で作られたスレッドで呼び出された場合、 ctypes は全ての呼び出しごとに新しいダミーの Python スレッドを作成することに注意してください。 この動作はほとんどの目的に対して正しいものですが、同じ C スレッドからの呼び出しだったとしても、 threading.local で格納された値は異なるコールバックをまたいで生存は しません

16.16.1.18. dllからエクスポートされた値へアクセスする

共有ライブラリの一部は関数だけでなく変数もエクスポートしています。 Python ライブラリにある例としては Py_OptimizeFlag 、起動時の -O または -OO フラグに依存して、 0 , 1 または 2 が設定される整数があります。

ctypes は型の in_dll() クラスメソッドを使ってこのように値にアクセスできます。 pythonapi はPython C api へアクセスできるようにするための予め定義されたシンボルです。:

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

tablestruct_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 はフローズンモジュールとフローズンパッケージ (負のサイズのメンバーで表されています) を持っているという事実はあまり知られておらず、テストにだけ使われています。例えば、 import __hello__ を試してみてください。

16.16.1.19. びっくり仰天

There are some edges in ctypes where you might expect something other than what actually happens.

次に示す例について考えてみてください。:

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

temp0temp1 は前記の rc オブジェクトの内部バッファでまだ使われているオブジェクトです。したがって、 rc.a = temp0 を実行すると temp0 のバッファ内容が rc のバッファへコピーされます。さらに、これは temp1 の内容を変更します。そのため、最後の代入 rc.b = temp1 は、期待する結果にはならないのです。

Structure 、 Union および Array のサブオブジェクトを取り出しても、そのサブオブジェクトが コピー されるわけではなく、ルートオブジェクトの内部バッファにアクセスするラッパーオブジェクトを取り出すことを覚えておいてください。

期待とは違う振る舞いをする別の例はこれです。:

>>> s = c_char_p()
>>> s.value = "abc def ghi"
>>> s.value
'abc def ghi'
>>> s.value is s.value
False
>>>

なぜ False と表示されるのでしょうか? ctypes インスタンスはメモリと、メモリの内容にアクセスするいくつかの descriptor (記述子)を含むオブジェクトです。メモリブロックに Python オブジェクトを保存してもオブジェクト自身が保存される訳ではなく、オブジェクトの contents が保存されます。その contents に再アクセスすると新しい Python オブジェクトがその度に作られます。

16.16.1.20. 可変サイズのデータ型

ctypes は可変サイズの配列と構造体をサポートしています。

resize() 関数は既存の ctypes オブジェクトのメモリバッファのサイズを変更したい場合に使えます。この関数は第一引数にオブジェクト、第二引数に要求されたサイズをバイト単位で指定します。メモリブロックはオブジェクト型で指定される通常のメモリブロックより小さくすることはできません。これをやろうとすると、 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 の動的性質を使って一つ一つデータ型を(再)定義することです。

16.16.2. ctypesリファレンス

16.16.2.1. 共有ライブラリを見つける

コンパイルされる言語でプログラミングしている場合、共有ライブラリはプログラムをコンパイル/リンクしているときと、そのプログラムが動作しているときにアクセスされます。

ctypes ライブラリローダーはプログラムが動作しているときのように振る舞い、ランタイムローダーを直接呼び出すのに対し、 find_library() 関数の目的はコンパイラまたはランタイムローダーが行うのと似た方法でライブラリを探し出すことです。 (複数のバージョンの共有ライブラリがあるプラットホームでは、一番最近に見つかったものがロードされます)。

ctypes.util モジュールはロードするライブラリを決めるのに役立つ関数を提供します。

ctypes.util.find_library(name)

ライブラリを見つけてパス名を返そうと試みます。 namelib のような接頭辞、 .so, .dylib のような接尾辞、あるいは、バージョン番号が何も付いていないライブラリの名前です (これは posix リンカのオプション -l に使われている形式です)。 ライブラリが見つからないときは None を返します。

厳密な機能はシステムに依存します。

Linux では、 find_library() はライブラリファイルを見つけるために外部プログラム (/sbin/ldconfig, gcc, objdumpld) を実行しようとします。ライブラリファイルのファイル名を返します。

バージョン 3.6 で変更: Linux では、ライブラリを検索する際に、他の方法でライブラリが見つけられない場合は、 LD_LIBRARY_PATH 環境変数の値が使われます

ここに例があります:

>>> from ctypes.util import find_library
>>> find_library("m")
'libm.so.6'
>>> find_library("c")
'libc.so.6'
>>> find_library("bz2")
'libbz2.so.1.0'
>>>

OS X では、 find_library() はライブラリの位置を探すために、予め定義された複数の命名方法とパスを試し、成功すればフルパスを返します。:

>>> from ctypes.util import find_library
>>> find_library("c")
'/usr/lib/libc.dylib'
>>> find_library("m")
'/usr/lib/libm.dylib'
>>> find_library("bz2")
'/usr/lib/libbz2.dylib'
>>> find_library("AGL")
'/System/Library/Frameworks/AGL.framework/AGL'
>>>

Windows では、 find_library() はシステムの探索パスに沿って探し、フルパスを返します。しかし、予め定義された命名方法がないため、 find_library("c") のような呼び出しは失敗し、 None を返します。

ctypes で共有ライブラリをラップする場合、 find_library() を使って実行時にライブラリの場所を特定するのではなく、共有ライブラリの名前を開発時に決めておいて、ラッパーモジュールにハードコードする方が良い かもしれません

16.16.2.2. 共有ライブラリをロードする

共有ライブラリを Python プロセスへロードする方法はいくつかあります。一つの方法は下記のクラスの一つをインスタンス化することです:

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

このクラスのインスタンスはロードされた共有ライブラリをあらわします。これらのライブラリの関数は標準 C 呼び出し規約を使用し、 int を返すと仮定されます。

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

Windows 用: このクラスのインスタンスはロードされた共有ライブラリをあらわします。これらのライブラリの関数は stdcall 呼び出し規約を使用し、 windows 固有の HRESULT コードを返すと仮定されます。 HRESULT 値には関数呼び出しが失敗したのか成功したのかを特定する情報とともに、補足のエラーコードが含まれます。戻り値が失敗を知らせたならば、 OSError が自動的に送出されます。

バージョン 3.3 で変更: 以前は WindowsError を送出していました。

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

Windows 用: このクラスのインスタンスはロードされた共有ライブラリをあらわします。これらのライブラリの関数は stdcall 呼び出し規約を使用し、デフォルトでは int を返すと仮定されます。

Windows CE では標準呼び出し規約だけが使われます。便宜上、このプラットフォームでは、 WinDLLOleDLL が標準呼び出し規約を使用します。

これらのライブラリがエクスポートするどの関数でも呼び出す前に Python GIL (global interpreter lock) は解放され、後でまた獲得されます。

class ctypes.PyDLL(name, mode=DEFAULT_MODE, handle=None)

Python GIL が関数呼び出しの間解放 されず 、関数実行の後に Python エラーフラグがチェックされるということを除けば、このクラスのインスタンスは CDLL インスタンスのように振る舞います。エラーフラグがセットされた場合、 Python 例外が送出されます。

要するに、これは Python C api 関数を直接呼び出すのに便利だというだけです。

これらすべてのクラスは少なくとも一つの引数、すなわちロードする共有ライブラリのパスを渡して呼び出すことでインスタンス化されます。すでにロード済みの共有ライブラリへのハンドルがあるなら、 handle 名前付き引数として渡すことができます。土台となっているプラットフォームの dlopen または LoadLibrary 関数がプロセスへライブラリをロードするために使われ、そのライブラリに対するハンドルを得ます。

mode パラメータを使うと、ライブラリがどうやってロードされたかを特定できます。 詳細は dlopen(3) マニュアルページを参考にしてください。 Windows では mode は無視されます。 POSIX システムでは RTLD_NOW が常に追加され、設定変更はできません。

use_errno 変数が真に設定されたとき、システムの errno エラーナンバーに安全にアクセスする ctypes の仕組みが有効化されます。 ctypes はシステムの errno 変数のスレッド限定のコピーを管理します。もし、 use_errno=True の状態で作られた外部関数を呼び出したなら、関数呼び出し前の errno 変数は ctypes のプライベートコピーと置き換えられ、同じことが関数呼び出しの直後にも発生します。

ctypes.get_errno() 関数は ctypes のプライベートコピーの値を返します。そして、 ctypes.set_errno() 関数は ctypes のプライベートコピーを置き換え、以前の値を返します。

use_last_error パラメータは、真に設定されたとき、 GetLastError()SetLastError() Windows API によって管理される Windows エラーコードに対するのと同じ仕組みが有効化されます。 ctypes.get_last_error()ctypes.set_last_error() は Windows エラーコードの ctypes プライベートコピーを変更したり要求したりするのに使われます。

ctypes.RTLD_GLOBAL

mode パラメータとして使うフラグ。このフラグが利用できないプラットフォームでは、整数のゼロと定義されています。

ctypes.RTLD_LOCAL

mode パラメータとして使うフラグ。これが利用できないプラットフォームでは、 RTLD_GLOBAL と同様です。

ctypes.DEFAULT_MODE

共有ライブラリをロードするために使われるデフォルトモード。 OSX 10.3 では RTLD_GLOBAL であり、そうでなければ RTLD_LOCAL と同じです。

これらのクラスのインスタンスには公開メソッドはありません。共有ライブラリからエクスポートされた関数は、属性として、もしくは添字でアクセスできます。属性を通した関数へのアクセスは結果がキャッシュされ、従って繰り返しアクセスされると毎回同じオブジェクトを返すことに注意してください。それとは反対に、添字を通したアクセスは毎回新しいオブジェクトを返します:

>>> libc.time == libc.time
True
>>> libc['time'] == libc['time']
False

次に述べる公開属性が利用できます。それらの名前はエクスポートされた関数名に衝突しないように下線で始まります。:

PyDLL._handle

ライブラリへのアクセスに用いられるシステムハンドル。

PyDLL._name

コンストラクタに渡されたライブラリの名前。

共有ライブラリは (LibraryLoader クラスのインスタンスである) 前もって作られたオブジェクトの一つを使うことによってロードすることもできます。それらの LoadLibrary() メソッドを呼び出すか、ローダーインスタンスの属性としてライブラリを取り出すかのどちらかによりロードします。

class ctypes.LibraryLoader(dlltype)

共有ライブラリをロードするクラス。 dlltypeCDLLPyDLLWinDLL もしくは OleDLL 型の一つであるべきです。

__getattr__() は次のような特別なはたらきをします。ライブラリローダーインスタンスの属性として共有ライブラリにアクセスするとそれがロードされるということを可能にします。結果はキャッシュされます。そのため、繰り返し属性アクセスを行うといつも同じライブラリが返されます。

LoadLibrary(name)

共有ライブラリをプロセスへロードし、それを返します。このメソッドはライブラリの新しいインスタンスを常に返します。

これらの前もって作られたライブラリローダーを利用することができます。:

ctypes.cdll

CDLL インスタンスを作ります。

ctypes.windll

Windows 用: WinDLL インスタンスを作ります。

ctypes.oledll

Windows 用: OleDLL インスタンスを作ります。

ctypes.pydll

PyDLL インスタンスを作ります。

C Python api に直接アクセスするために、すぐに使用できる Python 共有ライブラリオブジェクトが次のように用意されています。

ctypes.pythonapi

属性として Python C api 関数を公開する PyDLL のインスタンス。これらすべての関数は C int を返すと仮定されますが、もちろん常に正しいとは限りません。そのため、これらの関数を使うためには正しい restype 属性を代入しなければなりません。

16.16.2.3. 外部関数

前節で説明した通り、外部関数はロードされた共有ライブラリの属性としてアクセスできます。デフォルトではこの方法で作成された関数オブジェクトはどんな数の引数でも受け取り、引数としてどんな ctypes データのインスタンスをも受け取り、そして、ライブラリローダーが指定したデフォルトの結果の値の型を返します。関数オブジェクトはプライベートクラスのインスタンスです。:

class ctypes._FuncPtr

C の呼び出し可能外部関数のためのベースクラス。

外部関数のインスタンスも C 互換データ型です。それらは C の関数ポインタを表しています。

この振る舞いは外部関数オブジェクトの特別な属性に代入することによって、カスタマイズすることができます。

restype

外部関数の結果の型を指定するために ctypes 型を代入する。何も返さない関数を表す void に対しては None を使います。

ctypes 型ではない呼び出し可能な Python オブジェクトを代入することは可能です。このような場合、関数が C int を返すと仮定され、呼び出し可能オブジェクトはこの整数を引数に呼び出されます。さらに処理を行ったり、エラーチェックをしたりできるようにするためです。これの使用は推奨されません。より柔軟な後処理やエラーチェックのためには restype として ctypes 型を使い、 errcheck 属性へ呼び出し可能オブジェクトを代入してください。

argtypes

関数が受け取る引数の型を指定するために ctypes 型のタプルを代入します。stdcall 呼び出し規約を使う関数はこのタプルの長さと同じ数の引数で呼び出されます。C 呼び出し規約を使う関数は、追加の不特定の引数も取ります。

外部関数が呼ばれたとき、それぞれの実引数は argtypes タプルの要素の from_param() クラスメソッドへ渡されます。このメソッドは実引数を外部関数が受け取るオブジェクトに合わせて変えられるようにします。例えば、 argtypes タプルの c_char_p 要素は、 ctypes 変換規則にしたがって引数として渡された文字列をバイト列オブジェクトへ変換するでしょう。

新: ctypes 型でない要素を argtypes に入れることができますが、個々の要素は引数として使える値 (整数、文字列、 ctypes インスタンス) を返す from_param() メソッドを持っていなければなりません。これにより関数パラメータとしてカスタムオブジェクトを適合するように変更できるアダプタが定義可能となります。

errcheck

Python 関数または他の呼び出し可能オブジェクトをこの属性に代入します。呼び出し可能オブジェクトは三つ以上の引数とともに呼び出されます。

callable(result, func, arguments)

result は外部関数が返すもので、 restype 属性で指定されます。

func は外部関数オブジェクト自身で、これにより複数の関数の処理結果をチェックまたは後処理するために、同じ呼び出し可能オブジェクトを再利用できるようになります。

arguments は関数呼び出しに最初に渡されたパラメータが入ったタプルです。これにより使われた引数に基づいた特別な振る舞いをさせることができるようになります。

この関数が返すオブジェクトは外部関数呼び出しから返された値でしょう。しかし、戻り値をチェックして、外部関数呼び出しが失敗しているなら例外を送出させることもできます。

exception ctypes.ArgumentError

この例外は外部関数呼び出しが渡された引数を変換できなかったときに送出されます。

16.16.2.4. 関数プロトタイプ

Foreign functions can also be created by instantiating function prototypes. Function prototypes are similar to function prototypes in C; they describe a function (return type, argument types, calling convention) without defining an implementation. The factory functions must be called with the desired result type and the argument types of the function.

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

返された関数プロトタイプは標準 C 呼び出し規約をつかう関数を作成します。関数は呼び出されている間 GIL を解放します。 use_errno が真に設定されれば、呼び出しの前後で System 変数 errno の ctypesプライベートコピーは本当の errno の値と交換されます。 use_last_error も Windows エラーコードに対するのと同様です。

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

Windows のみ: 返された関数プロトタイプは stdcall 呼び出し規約を使う関数を作成します。ただし、 WINFUNCTYPE()CFUNCTYPE() と同じである Windows CE を除きます。関数は呼び出されている間 GIL を解放します。 use_errnouse_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) でなければなりません。第一要素はエクスポートされた関数の名前である文字列、またはエクスポートされた関数の序数である小さい整数です。第二要素は共有ライブラリインスタンスです。

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

COM メソッドを呼び出す外部関数を返します。 vtbl_index は仮想関数テーブルのインデックスで、非負の小さい整数です。 name は COM メソッドの名前です。 iid はオプションのインターフェイス識別子へのポインタで、拡張されたエラー情報の提供のために使われます。

COM メソッドは特殊な呼び出し規約を用います。このメソッドは argtypes タプルに指定されたパラメータに加えて、第一引数として COM インターフェイスへのポインタを必要とします。

オプションの paramflags パラメータは上述した機能より多機能な外部関数ラッパーを作成します。

paramflagsargtypes と同じ長さのタプルでなければなりません。

このタプルの個々の要素はパラメータについてのより詳細な情報を持ち、 1 、 2 もしくは 3 要素を含むタプルでなければなりません。

第一要素はパラメータについてのフラグの組み合わせを含んだ整数です。

1
入力パラメータを関数に指定します。
2
出力パラメータ。外部関数が値を書き込みます。
4
デフォルトで整数ゼロになる入力パラメータ。

オプションの第二要素はパラメータ名の文字列です。これが指定された場合は、外部関数を名前付きパラメータで呼び出すことができます。

オプションの第三要素はこのパラメータのデフォルト値です。

この例では、デフォルトパラメータと名前付き引数をサポートするために 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")

二番目の例は出力パラメータについて説明します。 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)
>>>

出力パラメータを持つ関数は、単一のパラメータがある場合にはその出力パラメータ値を、複数のパラメータがある場合には出力パラメータ値が入ったタプルを、それぞれ自動的に返します。そのため、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 インスタンスの代わりに window 座標のタプルを返すには、関数のフィールドを取り出し、代わりにそれらを返すことができます。この場合、通常処理は行われなくなります:

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

16.16.2.5. ユーティリティー関数

ctypes.addressof(obj)

メモリバッファのアドレスを示す整数を返します。 obj は ctypes 型のインスタンスでなければなりません。

ctypes.alignment(obj_or_type)

ctypes 型のアライメントの必要条件を返します。 obj_or_type は ctypes 型またはインスタンスでなければなりません。

ctypes.byref(obj[, offset])

obj (ctypes 型のインスタンスでなければならない) への軽量ポインタを返します。 offset はデフォルトでは 0 で、内部ポインターへ加算される整数です。

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 つ長いバッファを作成します。バイト列の長さを使うべきではない場合は、第二引数として整数を渡して、配列の長さを指定することができます。

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)

ライブラリを検索し、パス名を返します。 namelib のような接頭辞、 .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 のプライベートなエラーコードのコピーを返したりはしません。

ctypes.get_errno()

システムの errno 変数の、スレッドローカルなプライベートコピーを返します。

ctypes.get_last_error()

Windows 用: システムの LastError 変数の、スレッドローカルなプライベートコピーを返します。

ctypes.memmove(dst, src, count)

標準 C の memmove ライブラリ関数と同じものです。: count バイトを src から dst へコピーします。 dstsrc はポインタへ変換可能な整数または ctypes インスタンスでなければなりません。

ctypes.memset(dst, c, count)

標準 C の memset ライブラリ関数と同じものです。: アドレス dst のメモリブロックを値 ccount バイト分書き込みます。 dst はアドレスを指定する整数または ctypes インスタンスである必要があります。

ctypes.POINTER(type)

このファクトリ関数は新しい ctypes ポインタ型を作成して返します。ポインタ型はキャッシュされ、内部で再利用されます。したがって、この関数を繰り返し呼び出してもコストは小さいです。type は ctypes 型でなければなりません。

ctypes.pointer(obj)

この関数は obj を指す新しいポインタインスタンスを作成します。戻り値は POINTER(type(obj)) 型のオブジェクトです。

注意: 外部関数呼び出しへオブジェクトへのポインタを渡したいだけなら、はるかに高速な byref(obj) を使うべきです。

ctypes.resize(obj, size)

この関数は obj の内部メモリバッファのサイズを変更します。 obj は ctypes 型のインスタンスでなければなりません。バッファを sizeof(type(obj)) で与えられるオブジェクト型の本来のサイズより小さくすることはできませんが、バッファを拡大することはできます。

ctypes.set_errno(value)

システム変数 errno の、呼び出し元スレッドでの ctypes のプライベートコピーの現在値を value に設定し、前の値を返します。

ctypes.set_last_error(value)

Windows 用: システム変数 LastError の、呼び出し元スレッドでの ctypes のプライベートコピーの現在値を value に設定し、前の値を返します。

ctypes.sizeof(obj_or_type)

ctypes の型やインスタンスのメモリバッファのサイズをバイト数で返します。C の sizeof 演算子と同様の動きをします。

ctypes.string_at(address, size=-1)

この関数はメモリアドレス address から始まる C 文字列を返します。size が指定された場合はサイズとして使われます。指定されなければ、文字列がゼロ終端されていると仮定します。

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

Windows 用: この関数はおそらく ctypes の中で最悪の名前でしょう。これは OSError のインスタンスを作成します。 code が指定されていなかった場合、エラーコードを判別するために GetLastError が呼び出されます。 descr が指定されていなかった場合、エラーの説明文を得るために FormatError() が呼び出されます。

バージョン 3.3 で変更: 以前は WindowsError インスタンスが作成されていました。

ctypes.wstring_at(address, size=-1)

この関数は文字列としてメモリアドレス address から始まるワイドキャラクタ文字列を返します。size が指定されたならば、文字列の文字数として使われます。指定されなければ、文字列がゼロ終端されていると仮定します。

16.16.2.6. データ型

class ctypes._CData

この非公開クラスはすべての ctypes データ型の共通のベースクラスです。他のことはさておき、すべての ctypes 型インスタンスは C 互換データを保持するメモリブロックを内部に持ちます。このメモリブロックのアドレスは addressof() ヘルパー関数が返します。別のインスタンス変数が _objects として公開されます。これはメモリブロックがポインタを含む場合に存続し続ける必要のある他の Python オブジェクトを含んでいます。

ctypes データ型の共通メソッド、すべてのクラスメソッドが存在します (正確には、 メタクラス のメソッドです):

from_buffer(source[, offset])

このメソッドは source オブジェクトのバッファを共有する ctypes のインスタンスを返します。 source オブジェクトは書き込み可能バッファインターフェースをサポートしている必要があります。オプションの offset 引数では source バッファのオフセットをバイト単位で指定します。デフォルトではゼロです。もし source バッファが十分に大きくなければ、 ValueError が送出されます。

from_buffer_copy(source[, offset])

このメソッドは source オブジェクトの読み出し可能バッファをコピーすることで、ctypes のインスタンスを生成します。オプションの offset 引数では source バッファのオフセットをバイト単位で指定します。デフォルトではゼロです。もし source バッファが十分に大きくなければ、 ValueError が送出されます。

from_address(address)

このメソッドは address で指定されたメモリを使って ctypes 型のインスタンスを返します。 address は整数でなければなりません。

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 オブジェクトを持つディクショナリです。このオブジェクトはデバッグでのみ使われます。決してディクショナリの内容を変更しないで下さい。

16.16.2.7. 基本データ型

class ctypes._SimpleCData

この非公開クラスは、全ての基本的な ctypes データ型の基底クラスです。これは基本的な ctypes データ型に共通の属性を持っているので、ここで触れておきます。 _SimpleCData_CData の子クラスなので、そのメソッドと属性を継承しています。ポインタでないかポインタを含まない ctypes データ型は、現在は pickle 化できます。

インスタンスは一つだけ属性を持ちます:

value

この属性は、インスタンスの実際の値を持ちます。整数型とポインタ型に対しては整数型、文字型に対しては一文字のバイト列オブジェクト、文字へのポインタに対しては Python のバイト列オブジェクトもしくは文字列となります。

value 属性が ctypes インスタンスより参照されたとき、大抵の場合はそれぞれに対し新しいオブジェクトを返します。 ctypes はオリジナルのオブジェクトを返す実装にはなって おらず 新しいオブジェクトを構築します。同じことが他の ctypes オブジェクトインスタンスに対しても言えます。

基本データ型は、外部関数呼び出しの結果として返されたときや、例えば構造体のフィールドメンバーや配列要素を取り出すときに、ネイティブの Python 型へ透過的に変換されます。言い換えると、外部関数が c_char_prestype を持つ場合は、 c_char_p インスタンスでは なく 常に Python バイト列オブジェクトを受け取ることでしょう。

基本データ型のサブクラスはこの振る舞いを継承 しません 。したがって、外部関数の restypec_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-bit signed int データ型を表します。たいていは、 c_byte の別名です。

class ctypes.c_int16

C 16-bit signed int データ型を表します。たいていは、 c_short の別名です。

class ctypes.c_int32

C 32-bit signed int データ型を表します。たいていは、 c_int の別名です。

class ctypes.c_int64

C 64-bit 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-bit unsigned int データ型を表します。たいていは、 c_ubyte の別名です。

class ctypes.c_uint16

C 16-bit unsigned int データ型を表します。たいていは、 c_ushort の別名です。

class ctypes.c_uint32

C 32-bit unsigned int データ型を表します。たいていは、 c_uint の別名です。

class ctypes.c_uint64

C 64-bit 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 です。 MSGRECT のような有用な構造体も定義されています。

16.16.2.8. 構造化データ型

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

ネイティブのバイトオーダーでの共用体のための抽象ベースクラス。

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

ビックエンディアン バイトオーダーでの構造体のための抽象ベースクラス。

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

リトルエンディアン バイトオーダーでの構造体のための抽象ベースクラス。

ネイティブではないバイトオーダーを持つ構造体にポインタ型フィールドあるいはポインタ型フィールドを含む他のどんなデータ型をも入れることはできません。

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

ネイティブ のバイトオーダーでの構造体のための抽象ベースクラス。

具象構造体型と具象共用体型はこれらの型の一つをサブクラス化することで作らなければなりません。少なくとも、 _fields_ クラス変数を定義する必要があります。 ctypes は、属性に直接アクセスしてフィールドを読み書きできるようにする記述子 (descriptor) を作成するでしょう。これらは、

_fields_

構造体のフィールドを定義するシーケンス。要素は2要素タプルか3要素タプルでなければなりません。第一要素はフィールドの名前です。第二要素はフィールドの型を指定します。それはどんな ctypes データ型でも構いません。

c_int のような整数型のために、オプションの第三要素を与えることができます。フィールドのビット幅を定義する正の小整数である必要があります。

一つの構造体と共用体の中で、フィールド名はただ一つである必要があります。これはチェックされません。名前が繰り返しでてきたときにアクセスできるのは一つのフィールドだけです。

Structure サブクラスを定義するクラス文の 後で_fields_ クラス変数を定義することができます。これにより、次のように自身を直接または間接的に参照するデータ型を作成できるようになります:

class List(Structure):
    pass
List._fields_ = [("pnext", POINTER(List)),
                 ...
                ]

しかし、 _fields_ クラス変数はその型が最初に使われる (インスタンスが作成される、それに対して sizeof() が呼び出されるなど) より前に定義されていなければなりません。その後 _fields_ クラス変数へ代入すると AttributeError が送出されます。

構造体型のサブクラスを定義することができ、もしあるならサブクラス内で定義された _fields_ に加えて、ベースクラスのフィールドも継承します。

_pack_

インスタンスの構造体フィールドのアライメントを上書きできるようにするオブションの小整数。 _pack__fields_ が代入されたときすでに定義されていなければなりません。そうでなければ、何の効果もありません。

_anonymous_

無名 (匿名) フィールドの名前が並べあげられたオプションのシーケンス。 _fields_ が代入されたとき、 _anonymous_ がすでに定義されていなければなりません。そうでなければ、何ら影響はありません。

この変数に並べあげられたフィールドは構造体型もしくは共用体型フィールドである必要があります。構造体フィールドまたは共用体フィールドを作る必要なく、入れ子になったフィールドに直接アクセスできるようにするために、 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.lptdesctd.u.lptdesc は同等ですが、前者がより高速です。なぜなら一時的な共用体インスタンスを作る必要がないためです。:

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

構造体のサブ-サブクラスを定義することができ、ベースクラスのフィールドを継承します。サブクラス定義に別の _fields_ 変数がある場合は、この中で指定されたフィールドはベースクラスのフィールドへ追加されます。

構造体と共用体のコンストラクタは位置引数とキーワード引数の両方を受け取ります。位置引数は _fields_ の中に現れたのと同じ順番でメンバーフィールドを初期化するために使われます。コンストラクタのキーワード引数は属性代入として解釈され、そのため、同じ名前をもつ _fields_ を初期化するか、 _fields_ に存在しない名前に対しては新しい属性を作ります。

16.16.2.9. 配列とポインタ

class ctypes.Array(*args)

配列のための抽象基底クラスです。

具象配列型を作成するための推奨される方法は、任意の ctypes データ型に正の整数を乗算することです。代わりに、この型のサブクラスを作成し、 _length__type_ のクラス変数を定義することもできます。配列の要素は、標準の添え字とスライスによるアクセスを使用して読み書きを行うことができます。スライスの読み込みでは、結果のオブジェクト自体は Array ではありません。

_length_

配列の要素数を指定する正の整数。範囲外の添え字を指定すると、 IndexError が送出されます。len() がこの整数を返します。

_type_

配列内の各要素の型を指定します。

配列のサブクラスのコンストラクタは、位置引数を受け付けて、配列を順番に初期化するために使用します。

class ctypes._Pointer

ポインタのためのプライベートな抽象基底クラスです。

具象ポインタ型は、ポイント先の型を持つ POINTER() を呼び出すことで、作成できます。これは、 pointer() により自動的に行われます。

ポインタが配列を指す場合、その配列の要素は、標準の添え字とスライスによるアクセスを使用して読み書きが行えます。ポインタオブジェクトには、サイズがないため、 len() 関数は TypeError を送出します。負の添え字は、(C と同様に) ポインタの のメモリから読み込み、範囲外の添え字はおそらく (幸運な場合でも) アクセス違反によりクラッシュを起こします。

_type_

ポイント先の型を指定します。

contents

ポインタが指すオブジェクトを返します。この属性に割り当てると、ポインタが割り当てられたオブジェクトを指すようになります。

関連キーワード:  ctypes, 関数, int, オブジェクト, ポインタ, ライブラリ, インスタンス, 呼び出し, 整数, クラス