バージョン 2.5 で追加.
ctypes は Python のための外部関数ライブラリです。このライブラリは Cと互換性のあるデータ型を提供し、動的リンク/共有ライブラリ内の関数呼び 出しを可能にします。動的リンク/共有ライブラリを純粋な Python でラップ するために使うことができます。
注意: このチュートリアルのコードサンプルは動作確認のために doctest を使います。コードサンプルの中には Linux 、 Windows 、あるいは Mac OS X上で異なる動作をするものがあるため、サンプルのコメントに doctest 命令 を入れてあります。
注意: いくつかのコードサンプルで ctypes の c_int 型を参照して います。 32 ビットシステムにおいてこの型は c_long 型のエイリ アスです。そのため、 c_int 型を想定しているときに c_long が表示されたとしても、混乱しないようにしてください — 実際には同じ型なのです。
動的リンクライブラリをロードするために、 ctypes は cdll をエクス ポートします。 Windows では windll と oledll オブジェクトをエクスポートします。
これらのオブジェクトの属性としてライブラリにアクセスすることでライブラ リをロードします。 cdll は標準 cdecl 呼び出し規約を用いて関数を エクスポートしているライブラリをロードします。それに対して、 windll ライブラリは stdcall 呼び出し規約を用いる関数を呼び出します。 oledll も stdcall 呼び出し規約を使いますが、関数がWindows HRESULT エラーコードを返すことを想定しています。このエラーコー ドは関数呼び出しが失敗したとき、 WindowsError 例外を自動的に 送出させるために使われます。
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 ファイル拡張子を自動的に追加します。
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 オブジェクトの属性として関数にアクセスします:
>>> 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 ?
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 プロトタイプを持ちます。 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 ?
File "ctypes.py", line 310, in __getitem__
func = _StdcallFuncPtr(name, self)
AttributeError: function ordinal 0 not found
>>>
これらの関数は他の Python 呼び出し可能オブジェクトと同じように呼び出す ことができます。 この例では time() 関数 (Unixエポックからのシステム時間を秒単位で返 す) と、 GetModuleHandleA() 関数 (win32モジュールハンドルを返す) を使います。
この例は両方の関数を NULL ポインタとともに呼び出します (None を NULL ポインタとして使う必要があります):
>>> print libc.time(None)
1150640792
>>> print hex(windll.kernel32.GetModuleHandleA(None))
0x1d000000
>>>
ctypes は引数の数を間違えたり、あるいは呼び出し規約を間違えた関数 呼び出しからあなたを守ろうとします。残念ながら、これは Windows でしか 機能しません。関数が返った後にスタックを調べることでこれを行います。し たがって、エラーは発生しますが、その関数は呼び出された 後です:
>>> windll.kernel32.GetModuleHandleA()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
ValueError: Procedure probably called with not enough arguments (4 bytes missing)
>>> windll.kernel32.GetModuleHandleA(0, 0)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
ValueError: Procedure probably called with too many arguments (4 bytes in excess)
>>>
同じ例外が cdecl 呼び出し規約を使って stdcall 関数を呼び出した ときに送出されますし、逆の場合も同様です。:
>>> cdll.kernel32.GetModuleHandleA(None)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
ValueError: Procedure probably called with not enough arguments (4 bytes missing)
>>>
>>> windll.msvcrt.printf("spam")
Traceback (most recent call last):
File "<stdin>", line 1, in ?
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 ?
WindowsError: exception: access violation reading 0x00000020
>>>
しかし、 ctypes を使って Python をクラッシュさせる方法は十分なほど あるので、よく注意すべきです。
None 、整数、長整数、バイト文字列およびユニコード文字列だけが、こ うした関数呼び出しにおいてパラメータとして直接使えるネイティブの Python オブジェクトです。 None は C の NULL ポインタとして渡さ れ、バイト文字列とユニコード文字列はそのデータを含むメモリブロックへの ポインタ (char * または wchar_t *) として渡されます。 Python 整数と Python 長整数はプラットホームのデフォルトの C int 型として 渡され、その値は C int 型に合うようにマスクされます。
他のパラメータ型をもつ関数呼び出しに移る前に、 ctypes データ型につ いてさらに学ぶ必要があります。
ctypes はたくさんの C と互換性のあるデータ型を定義しています :
ctypesの型 Cの型 Pythonの型 c_char char 1文字の文字列 c_wchar wchar_t 1文字のユニコード文字列 c_byte char 整数/長整数 c_ubyte unsigned char 整数/長整数 c_short short 整数/長整数 c_ushort unsigned short 整数/長整数 c_int int 整数/長整数 c_uint unsigned int 整数/長整数 c_long long 整数/長整数 c_ulong unsigned long 整数/長整数 c_longlong __int64 or long long 整数/長整数 c_ulonglong unsigned __int64 or unsigned long long 整数/長整数 c_float float 浮動小数点数 c_double double 浮動小数点数 c_longdouble longdouble 浮動小数点数 c_char_p char * (NUL 終端) 文字列または None c_wchar_p wchar_t * (NUL 終端) ユニコードまたは None c_void_p void * 整数/長整数または None
これら全ての型はその型を呼び出すことによって作成でき、オプションとして 型と値が合っている初期化子を指定することができます:
>>> c_int()
c_long(0)
>>> c_char_p("Hello, World")
c_char_p('Hello, World')
>>> 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_char_p(s)
>>> print c_s
c_char_p('Hello, World')
>>> c_s.value = "Hi, there"
>>> print c_s
c_char_p('Hi, there')
>>> print s # 最初の文字列は変更されていない
Hello, World
>>>
しかし、変更可能なメモリを指すポインタであることを想定している関数へそ れらを渡さないように注意すべきです。もし変更可能なメモリブロックが必要 なら、 ctypes には create_string_buffer 関数があり、いろいろな方法 で作成することできます。 現在のメモリブロックの内容は raw プロパティを使ってアクセス (ある いは変更) することができます。もし現在のメモリブロックに NUL 終端文字 列としてアクセスしたいなら、 value プロパティを使ってください:
>>> from ctypes import *
>>> p = create_string_buffer(3) # 3バイトのバッファを作成、NULで初期化される
>>> print sizeof(p), repr(p.raw)
3 '\x00\x00\x00'
>>> p = create_string_buffer("Hello") # NUL終端文字列を含むバッファを作成
>>> print sizeof(p), repr(p.raw)
6 'Hello\x00'
>>> print repr(p.value)
'Hello'
>>> p = create_string_buffer("Hello", 10) # 10バイトのバッファを作成
>>> print sizeof(p), repr(p.raw)
10 'Hello\x00\x00\x00\x00\x00'
>>> p.value = "Hi"
>>> print sizeof(p), repr(p.raw)
10 'Hi\x00lo\x00\x00\x00\x00\x00'
>>>
create_string_buffer 関数は初期の ctypes リリースにあった c_string 関数だけでなく、 (エイリアスとしてはまだ利用できる) c_buffer 関数をも置き換えるものです。 C の型 wchar_t のユニコード文字を含む変更可能なメモリブロックを作 成するには、 create_unicode_buffer 関数を使ってください。
printf は sys.stdout では なく 、本物の標準出力チャンネルへプリ ントすることに注意してください。したがって、これらの例はコンソールプロ ンプトでのみ動作し、 IDLE や PythonWin では動作しません。:
>>> printf = libc.printf
>>> printf("Hello, %s\n", "World!")
Hello, World!
14
>>> printf("Hello, %S", u"World!")
Hello, World!
13
>>> printf("%d bottles of beer\n", 42)
42 bottles of beer
19
>>> printf("%f bottles of beer\n", 42.5)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
ArgumentError: argument 2: exceptions.TypeError: Don't know how to convert parameter 2
>>>
前に述べたように、必要な C のデータ型へ変換できるようにするためには、 整数、文字列およびユニコード文字列を除くすべての Python 型を対応する ctypes 型でラップしなければなりません。:
>>> printf("An int %d, a double %f\n", 1234, c_double(3.14))
Integer 1234, double 3.1400001049
31
>>>
自作のクラスのインスタンスを関数引数として使えるように、 ctypes 引 数変換をカスタマイズすることもできます。 ctypes は _as_parameter_ 属性を探し出し、関数引数として使 います。 もちろん、整数、文字列もしくはユニコードの中の一つでなければなりませ ん。:
>>> class Bottles(object):
... def __init__(self, number):
... self._as_parameter_ = number
...
>>> bottles = Bottles(42)
>>> printf("%d bottles of beer\n", bottles)
42 bottles of beer
19
>>>
インスタンスのデータを _as_parameter_ インスタンス変数の中に入 れたくない場合には、そのデータを利用できるようにする property を定 義することができます。
argtypes 属性を設定することによって、 DLL からエクスポートされ ている関数に要求される引数の型を指定することができます。
argtypes は C データ型のシーケンスでなければなりません (この場 合 printf 関数はおそらく良い例ではありません。なぜなら、引数の数が 可変であり、フォーマット文字列に依存した異なる型のパラメータを取るから です。一方では、この機能の実験にはとても便利です)。:
>>> printf.argtypes = [c_char_p, c_char_p, c_int, c_double]
>>> printf("String '%s', Int %d, Double %f\n", "Hi", 10, 2.2)
String 'Hi', Int 10, Double 2.200000
37
>>>
( C の関数のプロトタイプのように) 書式を指定すると互換性のない引数型に なるのを防ぎ、引数を有効な型へ変換しようとします。:
>>> printf("%d %d %d", 1, 2, 3)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
ArgumentError: argument 2: exceptions.TypeError: wrong type
>>> printf("%s %d %f", "X", 2, 3)
X 2 3.00000012
12
>>>
関数呼び出しへ渡す自作のクラスを定義した場合には、 argtypes シー ケンスの中で使えるようにするために、そのクラスに from_param() ク ラスメソッドを実装しなければなりません。 from_param() クラスメソッドは関数呼び出しへ渡された Python オブ ジェクトを受け取り、型チェックもしくはこのオブジェクトが受け入れ可能で あると確かめるために必要なことはすべて行ってから、オブジェクト自身、 _as_parameter_ 属性、あるいは、この場合に C 関数引数として渡し たい何かの値を返さなければなりません。 繰り返しになりますが、その返される結果は整数、文字列、ユニコード、 ctypes インスタンス、あるいは _as_parameter_ 属性をもつオ ブジェクトであるべきです。
デフォルトでは、関数は C int を返すと仮定されます。他の戻り値の型 を指定するには、関数オブジェクトの restype 属性に設定します。
さらに高度な例として、 strchr 関数を使います。この関数は文字列ポイ ンタと char を受け取り、文字列へのポインタを返します。:
>>> strchr = libc.strchr
>>> strchr("abcdef", ord("d"))
8059983
>>> strchr.restype = c_char_p # c_char_pは文字列へのポインタ
>>> strchr("abcdef", ord("d"))
'def'
>>> print strchr("abcdef", ord("x"))
None
>>>
上の ord("x") 呼び出しを避けたいなら、 argtypes 属性を設定 することができます。 二番目の引数が一文字の Python 文字列から C の char へ変換されます。:
>>> strchr.restype = c_char_p
>>> strchr.argtypes = [c_char_p, c_char]
>>> strchr("abcdef", "d")
'def'
>>> strchr("abcdef", "def")
Traceback (most recent call last):
File "<stdin>", line 1, in ?
ArgumentError: argument 2: exceptions.TypeError: one character string expected
>>> print strchr("abcdef", "x")
None
>>> strchr("abcdef", "d")
'def'
>>>
外部関数が整数を返す場合は、 restype 属性として呼び出し可能な Python オブジェクト (例えば、関数またはクラス) を使うこともできます。 呼び出し可能オブジェクトは 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 ?
File "<stdin>", line 3, in ValidHandle
WindowsError: [Errno 126] The specified module could not be found.
>>>
WinError はエラーコードの文字列表現を得るために Windows の FormatMessage() api を呼び出し、例外を 返す 関数です。 WinError はオプションでエラーコードパラメータを取ります。このパラ メータが使われない場合は、エラーコードを取り出すために GetLastError() を呼び出します。
errcheck 属性によってもっと強力なエラーチェック機構を利用でき ることに注意してください。詳細はリファレンスマニュアルを参照してくださ い。
時には、 C api 関数がパラメータのデータ型として ポインタ を想定して いることがあります。おそらくパラメータと同一の場所に書き込むためか、も しくはそのデータが大きすぎて値渡しできない場合です。これは パラメータ の参照渡し としても知られています。
ctypes は byref() 関数をエクスポートしており、パラメータを参 照渡しするために使用します。 pointer 関数を使っても同じ効果が得ら れます。 しかし、 pointer は本当のポインタオブジェクトを構築するためより多 くの処理を行うことから、 Python 側でポインタオブジェクト自体を必要とし ないならば byref() を使う方がより高速です。:
>>> i = c_int()
>>> f = c_float()
>>> s = create_string_buffer('\000' * 32)
>>> print i.value, f.value, repr(s.value)
0 0.0 ''
>>> libc.sscanf("1 3.14 Hello", "%d %f %s",
... byref(i), byref(f), s)
3
>>> print i.value, f.value, repr(s.value)
1 3.1400001049 'Hello'
>>>
構造体と共用体は ctypes モジュールに定義されている Structure および Union ベースクラスから導出されなけ ればなりません。それぞれのサブクラスは _fields_ 属性を定義する 必要があります。 _fields_ は フィールド名 と フィールド型 を持つ 2要素タプル のリストでなければなりません。
フィールド型は c_int か他の ctypes 型 (構造体、共用体、配 列、ポインタ) から導出された ctypes 型である必要があります。
x と y という名前の二つの整数からなる簡単な 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 ?
ValueError: too many initializers
>>>
また、さらに複雑な構造体を構成することができます。 Structure はそれ自 体がフィールド型に構造体を使うことで他の構造体を内部に持つことができま す。
upperleft と lowerright という名前の二つの 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>
>>>
デフォルトでは、 Structure と Union のフィールドは C コンパイラが行う のと同じ方法でアライメントされています。サブクラスを定義するときに _pack_ クラス属性を指定することでこの動作を変えることは可能です。 このクラス属性には正の整数を設定する必要があり、フィールドの最大アライ メントを指定します。これは MSVC で #pragmapack(n) が行っていること 同じです。
ctypes は Structure と Union に対してネイティブのバイトオーダーを 使います。 ネイティブではないバイトオーダーの構造体を作成するには、 BigEndianStructure 、 LittleEndianStructure 、 BigEndianUnion および LittleEndianUnion ベースクラスの中の一つを使います。これらのクラスにポ インタフィールドを持たせることはできません。
ビットフィールドを含む構造体と共用体を作ることができます。ビットフィー ルドは整数フィールドに対してのみ作ることができ、ビット幅は _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>
>>>
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,
...
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 (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 ?
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
>>>
POINTER型フィールドを NULL に設定するために、 None を代入して もかまいません。:
>>> bar.values = None
>>>
XXX list other conversions...
時には、非互換な型のインスタンスであることもあります。 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 ?
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
>>>
です。 C では、前方宣言により指定され、後で定義されます。:
struct cell; /* 前方宣言 */
struct {
char *name;
struct cell *next;
} cell;
ctypes コードへの直接的な変換ではこうなるでしょう。しかし、動作しませ ん:
>>> class cell(Structure):
... _fields_ = [("name", c_char_p),
... ("next", POINTER(cell))]
...
Traceback (most recent call last):
File "<stdin>", line 1, in ?
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,
... p = p.next[0]
...
foo bar foo bar foo bar foo bar
>>>
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))
>>>
コールバック関数のはじめての実装なので、受け取った引数を単純に表示して、 0 を返します (漸進型開発 (incremental development)です ;-):
>>> def py_cmp_func(a, b):
... print "py_cmp_func", a, b
... return 0
...
>>>
C の呼び出し可能なコールバック関数を作成します。:
>>> cmp_func = CMPFUNC(py_cmp_func)
>>>
そうすると、準備完了です。:
>>> qsort(ia, len(ia), sizeof(c_int), cmp_func)
py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...>
py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...>
py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...>
py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...>
py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...>
py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...>
py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...>
py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...>
py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...>
py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...>
>>>
ポインタの中身にアクセスする方法がわかっているので、コールバック関数を 再定義しましょう。:
>>> def py_cmp_func(a, b):
... print "py_cmp_func", a[0], b[0]
... return 0
...
>>> cmp_func = CMPFUNC(py_cmp_func)
>>>
Windowsでの実行結果です。:
>>> qsort(ia, len(ia), sizeof(c_int), cmp_func)
py_cmp_func 7 1
py_cmp_func 33 1
py_cmp_func 99 1
py_cmp_func 5 1
py_cmp_func 7 5
py_cmp_func 33 5
py_cmp_func 99 5
py_cmp_func 7 99
py_cmp_func 33 99
py_cmp_func 7 33
>>>
linuxではソート関数がはるかに効率的に動作しており、実施する比較の数が 少ないように見えるのが不思議です。:
>>> 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
>>>
ええ、ほぼ完成です! 最終段階は、実際に二つの要素を比較して実用的な結果 を返すことです。:
>>> def py_cmp_func(a, b):
... print "py_cmp_func", a[0], b[0]
... return a[0] - b[0]
...
>>>
Windowsでの最終的な実行結果です。:
>>> qsort(ia, len(ia), sizeof(c_int), CMPFUNC(py_cmp_func))
py_cmp_func 33 7
py_cmp_func 99 33
py_cmp_func 5 99
py_cmp_func 1 99
py_cmp_func 33 7
py_cmp_func 1 33
py_cmp_func 5 33
py_cmp_func 5 7
py_cmp_func 1 7
py_cmp_func 5 1
>>>
Linuxでは:
>>> 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
>>>
Windows の qsort() 関数は linux バージョンより多く比較する必要が あることがわかり、非常におもしろいですね!
簡単に確認できるように、今では配列はソートされています。:
>>> for i in ia: print i,
...
1 5 7 33 99
>>>
コールバック関数についての重要な注意事項:
C コードから使われる限り、 CFUNCTYPE オブジェクトへの参照を確実に保持 してください。 ctypes は保持しません。もしあなたがやらなければ、オブジェクトはゴ ミ集めされてしまい、コールバックしたときにあなたのプログラムをクラッシュ させるかもしれません。
共有ライブラリの一部は関数だけでなく変数もエクスポートしています。 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 ポインタにアクセスします。
Pythonドキュメントからの引用すると: このポインタはメンバーがすべて NULLまたはゼロであるレコードを最後に持つ “struct_frozen” レコードの配 列を指すように初期化されます。フローズン (frozen) モジュールがインポー トされたとき、このテーブルから探索されます。サードパーティ製コードは動 的に作成されたフローズンモジュールの集合を提供するためと、これにいた ずらすることができます。
これで、このポインタを操作することが役に立つことを証明できるでしょう。 例の大きさを制限するために、このテーブルを 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:
... print item.name, item.size
... if item.name is None:
... break
...
__hello__ 104
__phello__ -104
__phello__.spam 104
None 0
>>>
標準 Python はフローズンモジュールとフローズンパッケージ (負のサイズの メンバーで表されています) を持っているという事実はあまり知られておらず、 テストにだけ使われています。例えば、 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 、 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 オブジェクトがその度に作 られます。
ctypes は可変サイズの配列と構造体をサポートしています (バージョン 0.9.9.7で追加されました)。
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 の動的性質を使って一つ一つデータ型を(再)定義する ことです。
前節で説明した通り、外部関数はロードされた共有ライブラリの属性としてア クセスできます。デフォルトではこの方法で作成された関数オブジェクトはど んな数の引数でも受け取り、引数としてどんな ctypes データのインスタンス をも受け取り、そして、ライブラリローダーが指定したデフォルトの結果の値 の型を返します。関数オブジェクトはプライベートクラスのインスタンスで す。:
C の呼び出し可能外部関数のためのベースクラス。
外部関数のインスタンスも C 互換データ型です。それらは C の関数ポイ ンタを表しています。
この振る舞いは外部関数オブジェクトの特別な属性に代入することによっ て、カスタマイズすることができます。
外部関数の結果の型を指定するために ctypes 型を代入する。何も返さ ない関数を表す void に対しては None を使います。
ctypes 型ではない呼び出し可能な Python オブジェクトを代入するこ とは可能です。このような場合、関数が C int を返すと仮定され、 呼び出し可能オブジェクトはこの整数を引数に呼び出されます。さらに 処理を行ったり、エラーチェックをしたりできるようにするためです。 これの使用は推奨されません。より柔軟な後処理やエラーチェックのた めには restype として ctypes 型を使い、 errcheck 属性へ 呼び出し可能オブジェクトを代入してください。
関数が受け取る引数の型を指定するために ctypes 型のタプルを代入し ます。 stdcall 呼び出し規約をつかう関数はこのタプルの長さと同じ 数の引数で呼び出されます。その上、 C 呼び出し規約をつかう関数は追加 の不特定の引数も取ります。
外部関数が呼ばれたとき、それぞれの実引数は argtypes タプ ルの要素の from_param() クラスメソッドへ渡されます。このメ ソッドは実引数を外部関数が受け取るオブジェクトに合わせて変えられ るようにします。 例えば、 argtypes タプルの c_char_p 要素は、 ctypes 変換規則にしたがって引数として渡されたユニコード文字列を バイト文字列へ変換するでしょう。
新: ctypes 型でない要素を argtypes に入れることができますが、個々 の要素は引数として使える値 ( 整数、文字列、 ctypes インスタンス ) を返す from_param() メソッドを持っていなければなりません。 これにより関数パラメータとしてカスタムオブジェクトを適合するよう に変更できるアダプタが定義可能となります。
Python 関数または他の呼び出し可能オブジェクトをこの属性に代入し ます。呼び出し可能オブジェクトは三つ以上の引数とともに呼び出され ます。
result は外部関数が返すもので、 restype 属性で指定さ れます。
func は外部関数オブジェクト自身で、これにより複数の関数の処 理結果をチェックまたは後処理するために、同じ呼び出し可能オブジェ クトを再利用できるようになります。
arguments は関数呼び出しに最初に渡されたパラメータが入っ たタプルです。これにより使われた引数に基づた特別な振る舞いを させることができるようになります。
この関数が返すオブジェクトは外部関数呼び出しから返された値でしょ う。しかし、戻り値をチェックして、外部関数呼び出しが失敗している なら例外を送出させることもできます。
この例外は外部関数呼び出しが渡された引数を変換できなかったときに送 出されます。
外部関数は関数プロトタイプをインスタンス化することによって作成されます。 関数プロトタイプは C の関数プロトタイプと似ています。実装を定義せずに、 関数 ( 戻り値、引数の型、呼び出し規約 ) を記述します。ファクトリ関数は 関数に要求する戻り値の型と引数の型とともに呼び出されます。
返された関数プロトタイプは標準 C 呼び出し規約をつかう関数を作成しま す。関数は呼び出されている間 GIL を解放します。 use_errno が True に設定されれば、呼び出しの前後で System 変数 errno の ctypesプライベートコピーは本当の errno の値と交換され ます。; use_last_error も Windows エラーコードに対するのと同様です。
バージョン 2.6 で変更: オプションの use_errno と use_last_error 変数が追加されました。
Windows 用: 返された関数プロトタイプは stdcall 呼び出し規約をつかう関数を作成します。 ただし、 WINFUNCTYPE() が CFUNCTYPE() と同じである Windows CE を除きます。 関数は呼び出されている間 GIL を解放します。 use_errno と use_last_error は前述と同じ意味を持ちます。
返された関数プロトタイプは 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 パラメータは上述した機能より多機能な外部 関数ラッパーを作成します。
paramflags は argtypes と同じ長さのタプルでなければなりま せん。
このタプルの個々の要素はパラメータについてのより詳細な情報を持ち、 1 、 2 もしくは 3 要素を含むタプルでなければなりません。
第一要素はパラメータについてのフラグの組み合わせを含んだ整数です。
- 1
- 入力パラメータを関数に指定します。
- 2
- 出力パラメータ。外部関数が値を書き込みます。
- 4
- デフォルトで整数ゼロになる入力パラメータ。
オプションの第二要素はパラメータ名の文字列です。これが指定された場 合は、外部関数を名前付きパラメータで呼び出すことができます。
オプションの第三要素はこのパラメータのデフォルト値です。
この例では、デフォルトパラメータと名前付き引数をサポートするために Windows MessageBoxA 関数をラップする方法を示します。 windowsヘッダファイルの C の宣言はこれです。:
WINUSERAPI int WINAPI
MessageBoxA(
HWND hWnd ,
LPCSTR lpText,
LPCSTR lpCaption,
UINT uType);
ctypes を使ってラップします。:
>>> from ctypes import c_int, WINFUNCTYPE, windll
>>> from ctypes.wintypes import HWND, LPCSTR, UINT
>>> prototype = WINFUNCTYPE(c_int, HWND, LPCSTR, LPCSTR, UINT)
>>> paramflags = (1, "hwnd", 0), (1, "text", "Hi"), (1, "caption", None), (1, "flags", 0)
>>> MessageBox = prototype(("MessageBoxA", 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
>>>
メモリバッファのアドレスを示す整数を返します。 obj は ctypes 型 のインスタンスでなければなりません。
ctypes 型のアライメントの必要条件を返します。 obj_or_type は ctypes 型またはインスタンスでなければなりません。
obj (ctypes 型のインスタンスでなければならない) への軽量ポイン タを返します。 offset はデフォルトでは 0 で、内部ポインターへ加 算される整数です。
byref(obj, offset) は、 C コードとしては、以下のようにみなされ ます。:
(((char *)&obj) + offset)
返されるオブジェクトは外部関数呼び出しのパラメータとしてのみ使用で きます。 pointer(obj) と似たふるまいをしますが、作成が非常に速 く行えます。
バージョン 2.6 で追加: offset オプション引数が追加れました。
この関数は C のキャスト演算子に似ています。 obj と同じメモリブ ロックを指している type の新しいインスタンスを返します。 type はポインタ型でなければならず、 obj はポインタとして解 釈できるオブジェクトでなければなりません。
この関数は変更可能な文字バッファを作成します。返されるオブジェクト は c_char の ctypes 配列です。
init_or_size は配列のサイズを指定する整数もしくは配列要素を初期 化するために使われる文字列である必要があります。
第一引数として文字列が指定された場合は、バッファが文字列の長さより 一要素分大きく作られます。配列の最後の要素が NUL 終端文字であるため です。 文字列の長さを使うべきでない場合は、配列のサイズを指定するために整 数を第二引数として渡すことができます。
第一引数がユニコード文字列ならば、 ctypes 変換規則にしたがい 8 ビッ ト文字列へ変換されます。
この関数は変更可能なユニコード文字バッファを作成します。返されるオ ブジェクトは c_wchar の ctypes 配列です。
init_or_size は配列のサイズを指定する整数もしくは配列要素を初期 化するために使われるユニコード文字列です。
第一引数としてユニコード文字列が指定された場合は、バッファが文字列 の長さより一要素分大きく作られます。配列の最後の要素が NUL 終端文字 であるためです。 文字列の長さを使うべきでない場合は、配列のサイズを指定するために整 数を第二引数として渡すことができます。
第一引数が 8 ビット文字列ならば、 ctypes 変換規則にしたがいユニコー ド文字列へ変換されます。
Windows用: この関数は ctypes をつかってインプロセス COM サーバーを 実装できるようにするためのフックです。 _ctypes 拡張 dll がエクスポー トしている DllCanUnloadNow 関数から呼び出されます。
Windows用: この関数は ctypes をつかってインプロセス COM サーバーを 実装できるようにするためのフックです。 _ctypes 拡張 dll が エクスポートしている DllGetClassObject 関数から呼び出されます。
ライブラリを検索し、パス名を返します。 name は lib のような接頭辞、 .so や .dylib のような接尾辞、そして、バージョンナンバー を除くライブラリ名です (これは posix のリンカーオプション -l で使われる書式です) 。もしライブラリが見つからなければ、 None を返します。
実際の機能はシステムに依存します。
バージョン 2.6 で変更: Windows限定: find_library("m") もしくは find_library("c") は find_msvcrt() の呼び出し結果を返します。
Windows用: Python と拡張モジュールで使われる VC ランタイプライブラ リのファイル名を返します。もしライブラリ名が同定できなければ、 None を返します。
もし、例えば拡張モジュールにより割り付けられたメモリを free(void *) で解放する必要があるなら、メモリ割り付けを行ったのと同じライブ ラリの関数を使うことが重要です。
バージョン 2.6 で追加.
Windows用: エラーコードの説明文を返します。エラーコードが指定されな い場合は、 Windows api 関数 GetLastError を呼び出して、もっとも新し いエラーコードが使われます。
Windows用: 呼び出し側のスレッド内で Windows によって設定された最新 のエラーコードを返します。 この関数はWindowsの GetLastError() 関数を直接実行します。 ctypesのプライベートなエラーコードのコピーを返したりはしません。
Windowsのみ: システムの LastError 変数の、スレッドローカルなプライベートコピーを返します。
バージョン 2.6 で追加.
標準 C の memmove ライブラリ関数と同じものです。: count バイトを src から dst へコピーします。 dst と src はポインタへ変 換可能な整数または ctypes インスタンスでなければなりません。
標準 C の memset ライブラリ関数と同じものです。: アドレス dst の メモリブロックを値 c を count バイト分書き込みます。 dst はアドレスを指定する整数または ctypes インスタンスである必要 があります。
このファクトリ関数は新しい ctypes ポインタ型を作成して返します。ポ インタ型はキャッシュされ、内部で再利用されます。したがって、この関 数を繰り返し呼び出してもコストは小さいです。型は ctypes 型でなけれ ばなりません。
この関数は obj を指す新しいポインタインスタンスを作成します。戻 り値は POINTER(type(obj)) 型のオブジェクトです。
注意: 外部関数呼び出しへオブジェクトへのポインタを渡したいだけなら、 はるかに高速な byref(obj) を使うべきです。
この関数は obj の内部メモリバッファのサイズを変更します。 obj は ctypes 型のインスタンスでなければなりません。 バッファを sizeof(type(obj)) で与えられるオブジェクト型の本来のサイ ズより小さくすることはできませんが、バッファを拡大することはできま す。
この関数は 8 ビット文字列とユニコード文字列の間で変換するときに使わ れる規則を設定します。 encoding は 'utf-8' や 'mbcs' のよう なエンコーディングを指定する文字列でなければなりません。 errors は エンコーディング/デコーディングエラーについてのエラー処理を指定する 文字列でなければなりません。指定可能な値の例としては、 "strict" 、 "replace" または "ignore" があります。
set_conversion_mode は以前の変換規則を含む 2 要素タプルです。 windows では初期の変換規則は ('mbcs', 'ignore') であり、他のシ ステムでは ('ascii', 'strict') です。
システム変数 errno の、呼び出し元スレッドでの ctypes のプライベー トコピーの現在値を value に設定し、前の値を返します。
バージョン 2.6 で追加.
Windows用: システム変数 LastError の、呼び出し元スレッドでの ctypes のプライベートコピーの現在値を value に設定し、前の値を返 します。
バージョン 2.6 で追加.
ctypes 型もしくはインスタンスのメモリバッファのサイズをバイト単位で 返します。 C の sizeof() 関数と同じ動作です。
この関数はメモりアドレス address から始まる文字列を返します。 size が指定された場合はサイズとして使われます。指定されなければ、文字列 がゼロ終端されていると仮定します。
Windows用: この関数は ctypes の中でもおそらく最悪な名前がつけれたも のです。 WindowsError のインスタンスを作成します。 code が指定されないなら ば、エラーコードを決めるために GetLastError が呼び出されます。 descr が指定されないならば、 FormatError() がエラーの説明 文を得るために呼び出されます。
この関数はユニコード文字列としてメモリアドレス address から始ま るワイドキャラクタ文字列を返します。 size が指定されたならば、 文字列の文字数として使われます。指定されなければ、文字列がゼロ終端 されていると仮定します。
この非公開クラスはすべての ctypes データ型の共通のベースクラスです。 他のものに取り込まれることで、すべての ctypes 型インスタンスがは C 互換データを保持するメモリブロックを内部に持ちます。メモリブロック のアドレスを addressof() ヘルパー関数が返さします。別のインスタ ンス変数は _objects として公開されます。これはメモリブロッ クがポインタを含む場合に存続し続ける必要のある他の Python オブジェ クトを含んでいます。
ctypes データ型の共通メソッド、すべてのクラスメソッドが存在します (正確には、メタクラスのメソッドです):
このメソッドは source オブジェクトのバッファを共有する ctypes の インスタンスを返します。 source オブジェクトは書き込み可能バッ ファインターフェースをサポートしている必要があります。オプション の offset 引数では source バッファのオフセットをバイト単位で 指定します。; デフォルトではゼロです。もし source バッファが十分に大きくなけれ ば、 ValueError が送出されます。
バージョン 2.6 で追加.
このメソッドは source オブジェクトの読み出し可能バッファをコピー することで、ctypes のインスタンスを生成します。オプションの offset 引数では source バッファのオフセットをバイト単位で指 定します。; デフォルトではゼロです。もし source バッファが十分に大きくなけれ ば、 ValueError が送出されます。
バージョン 2.6 で追加.
このメソッドは address で指定されたメモリを使って ctypes 型のイ ンスタンスを返します。 address は整数でなければなりません。
このメソッドは obj を ctypes 型に適合させます。外部関数の argtypes タプルに、その型があるとき、外部関数呼び出しで 実際に使われるオブジェクトと共に呼び出されます。
すべての ctypes のデータ型は、それが型のインスタンスであれば、 obj を返すこのクラスメソッドのデフォルトの実装を持ちます。 いくつかの型は、別のオブジェクトも受け付けます。
このメソッドは、共有ライブラリによってエクスポートされた ctypes 型のインスタンスを返します。 name はエクスポートされたデータの名前で、 library はロードさ れた共有ライブラリです。
ctypes データ型共通のインスタンス変数:
ctypes 型データのインスタンスは、それ自身のメモリブロックを持た ず、基底オブジェクトのメモリブロックの一部を共有することがありま す。 _b_base_ 読み出し専用属性は、メモリブロックを保持す る ctypes の基底オブジェクトです。
この非公開クラスはすべての基本 ctypes データ型のベースクラスです。 ここでこのクラスに触れたのは、基本 ctypes データ型の共通属性を含ん でいるからです。 _SimpleCData は _CData のサブクラスですので、そのメソッドと 属性を継承しています。
バージョン 2.6 で変更: ポインタと、ポインタを含まない ctypes データ型が pickle 化できる ようになりました。
単一の属性を持つインスタンス:
この属性は、インスタンスの実際の値を持ちます。整数型とポインタ型 に対しては整数型、文字型に対しては一文字の文字列、文字へのポイン タに対しては Python の文字列もしくはユニコード文字列となります。
value 属性が ctypes インスタンスより参照されたとき、大抵の場 合はそれぞれに対し新しいオブジェクトを返します。 ctypes はオ リジナルのオブジェクトを返す実装にはなって おらず 新しいオブジェ クトを構築します。同じことが他の ctypes オブジェクトインスタンス に対しても言えます。
基本データ型は、外部関数呼び出しの結果として返されたときや、例えば構造 体のフィールドメンバーや配列要素を取り出すときに、ネイティブの Python 型へ透過的に変換されます。言い換えると、外部関数が c_char_p の restype を持つ場合は、 c_char_p インスタンスでは なく 常に Python 文字列を受け取ることでしょう。
基本データ型のサブクラスはこの振る舞いを継承 しません 。したがって、 外部関数の restype が c_void_p のサブクラスならば、関 数呼び出しからこのサブクラスのインスタンスを受け取ります。もちろん、 value 属性にアクセスしてポインタの値を得ることができます。
これらが基本データ型です:
C の signed char データ型を表し、小整数として値を解釈します。コンス トラクタはオプションの整数初期化子を受け取ります。 オーバーフローのチェックは行われません。
C char データ型を表し、単一の文字として値を解釈します。コンストラク タはオプションの文字列初期化子を受け取り、その文字列の長さちょうど 一文字である必要があります。
C char * データ型を表し、ゼロ終端文字列へのポインタでなければなりま せん。コンストラクタは整数のアドレスもしくは文字列を受け取ります。
C double データ型を表します。コンストラクタはオプションの浮動小数点 数初期化子を受け取ります。
C long double データ型を表します。コンストラクタはオプションで浮動 小数点数初期化子を受け取ります。 sizeof(long double) == sizeof(double) であるプラットホームでは c_double の別名 です。
バージョン 2.6 で追加.
C float データ型を表します。コンストラクタはオプションの浮動小数点 数初期化子を受け取ります。
C signed int データ型を表します。コンストラクタはオプションの整数初 期化子を受け取ります。オーバーフローのチェックは行われません。 sizeof(int) == sizeof(long) であるプラットホームでは、 c_long の別名です。
C 64-bit signed int データ型を表します。たいていは、 c_longlong の別名です。
C signed long データ型を表します。コンストラクタはオプションの 整数初期化子を受け取ります。オーバーフローのチェックは行われません。
C signed long long データ型を表します。コンストラクタはオプションの 整数初期化子を受け取ります。オーバーフローのチェックは行われません。
C signed short データ型を表します。コンストラクタはオプションの 整数初期化子を受け取ります。オーバーフローのチェックは行われません。
C size_t データ型を表します。
C unsigned char データ型を表します。その値は小整数として解釈さ れます。コンストラクタはオプションの整数初期化子を受け取ります。オー バーフローのチェックは行われません。
C unsigned int データ型を表します。コンストラクタはオプションの 整数初期化子を受け取ります。オーバーフローのチェックは行われません。 sizeof(int) == sizeof(long) であるプラットホームでは、 c_ulong の別名です。
C 64-bit unsigned int データ型を表します。たいていは、 c_ulonglong の別名です。
C unsigned long データ型を表します。コンストラクタはオプション の整数初期化子を受け取ります。オーバーフローのチェックは行われませ ん。
C unsigned long long データ型を表します。コンストラクタはオプショ ンの整数初期化子を受け取ります。オーバーフローのチェックは行われま せん。
C unsigned short データ型を表します。コンストラクタはオプション の整数初期化子を受け取ります。オーバーフローのチェックは行われませ ん。
C void * データ型を表します。値は整数として表されます。コンスト ラクタはオプションの整数初期化子を受け取ります。
C wchar_t データ型を表し、値はユニコード文字列の単一の文字とし て解釈されます。コンストラクタはオプションの文字列初期化子を受け取 り、その文字列の長さはちょうど一文字である必要があります。
C wchar_t * データ型を表し、ゼロ終端ワイド文字列へのポインタで なければなりません。コンストラクタは整数のアドレスもしくは文字列を 受け取ります。
C bool データ型 ( より正確には、 C99 の _Bool ) を表します。そ の値は True または False であり、コンストラクタはどんなオブジェクト ( 真値を持ちます ) でも受け取ります。
バージョン 2.6 で追加.
C PyObject * データ型を表します。引数なしでこれを呼び出すと NULL PyObject * ポインタを作成します。
ctypes.wintypes モジュールは他の Windows 固有のデータ型を提供します。 例えば、 HWND 、 WPARAM または DWORD です。 MSG や RECT のような有用な構造体も定義されています。
ネイティブのバイトオーダーでの共用体のための抽象ベースクラス。
ビックエンディアン バイトオーダーでの構造体のための抽象ベースクラス。
リトルエンディアン バイトオーダーでの構造体のための抽象ベースクラス。
ネイティブではないバイトオーダーを持つ構造体にポインタ型フィールドある いはポインタ型フィールドを含む他のどんなデータ型をも入れることはでき ません。
ネイティブ のバイトオーダーでの構造体のための抽象ベースクラス。
具象構造体型と具象共用体型はこれらの型の一つをサブクラス化することで作 らなければなりません。少なくとも、 _fields_ クラス変数を定義す る必要があります。 ctypes は、属性に直接アクセスしてフィールドを読 み書きできるようにする記述子 ( descriptor ) を作成するでしょう。 これらは、
- ctypes._fields_¶
構造体のフィールドを定義するシーケンス。要素は2要素タプルか3要素 タプルでなければなりません。第一要素はフィールドの名前です。第 二要素はフィールドの型を指定します。それはどんな ctypes データ型で も構いません。
c_int のような整数型のために、オプションの第三要素を与 えることができます。フィールドのビット幅を定義する正の小整数であ る必要があります。
一つの構造体と共用体の中で、フィールド名はただ一つである必要があ ります。これはチェックされません。名前が繰り返しでてきたときにア クセスできるのは一つのフィールドだけです。
Structure サブクラスを定義するクラス文の 後で 、 _fields_ クラス変数を定義することができます。 これにより自身を直接または間接的に参照するデータ型を作成できるよ うになります。:
class List(Structure): pass List._fields_ = [("pnext", POINTER(List)), ... ]しかし、 _fields_ クラス変数はその型が最初に使われる ( インスタンスが作成される、それに対して sizeof() が呼び出される など ) より前に定義されていなければなりません。その後 _fields_ クラス変数へ代入すると AttributeError が送出されます。
構造体および共用体サブクラスは位置引数と名前付き引数の両方を受け取 ります。位置引数は _fields_ 定義中に現れたのと同じ順番で フィールドを初期化するために使われ、名前付き引数は対応する名前を 使ってフィールドを初期化するために使われます。
構造体型のサブクラスを定義することができ、もしあるならサブクラス 内で定義された _fields_ に加えて、ベースクラスのフィールドも 継承します。
- ctypes._pack_¶
インスタンスの構造体フィールドのアライメントを上書きできるように するオブションの小整数。 _pack_ は _fields_ が 代入されたときすでに定義されていなければなりません。そうでなけれ ば、何ら影響はありません。
- ctypes._anonymous_¶
無名 ( 匿名 ) フィールドの名前が並べあげられたオプションのシーケ ンス。 _fields_ が代入されたとき、 _anonymous_ がすでに 定義されていなければなりません。そうでなければ、何ら影響はありません。
この変数に並べあげられたフィールドは構造体型もしくは共用体型フィー ルドである必要があります。構造体フィールドまたは共用体フィールド を作る必要なく、入れ子になったフィールドに直接アクセスできるよう にするために、 ctypes は構造体型の中に記述子を作成します。
型の例です(Windows):
class _U(Union): _fields_ = [("lptdesc", POINTER(TYPEDESC)), ("lpadesc", POINTER(ARRAYDESC)), ("hreftype", HREFTYPE)] class TYPEDESC(Structure): _fields_ = [("u", _U), ("vt", VARTYPE)] _anonymous_ = ("u",)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_ に存在しない名前に対しては新しい属性を作ります。