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.1. 動的リンクライブラリをロードする¶
動的リンクライブラリをロードするために、 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 ...>
>>>
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 ?
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
>>>
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
は引数の数を間違えたり、あるいは呼び出し規約を間違えた関数呼び出しからあなたを守ろうとします。残念ながら、これは 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(b"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 ?
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 type | 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 |
|
int |
c_ulonglong |
|
int |
c_size_t |
size_t |
int |
c_ssize_t |
|
int |
c_float |
float |
float |
c_double |
double |
float |
c_longdouble |
long double |
float |
c_char_p |
|
バイト列オブジェクトまたは |
c_wchar_p |
|
文字列または |
c_void_p |
void * |
整数または |
コンストラクタは任意のオブジェクトをその真偽値として受け取ります。
これら全ての型はその型を呼び出すことによって作成でき、オプションとして型と値が合っている初期化子を指定することができます:
>>> c_int()
c_long(0)
>>> c_wchar_p("Hello, World")
c_wchar_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_wchar_p(s)
>>> print(c_s)
c_wchar_p('Hello, World')
>>> c_s.value = "Hi, there"
>>> print(c_s)
c_wchar_p('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
のユニコード文字を含む変更可能なメモリブロックを作成するには、 create_unicode_buffer()
関数を使ってください。
16.16.1.5. 続・関数を呼び出す¶
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 ?
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 ?
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 ?
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 ?
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 関数がパラメータのデータ型として ポインタ を想定していることがあります。おそらくパラメータと同一の場所に書き込むためか、もしくはそのデータが大きすぎて値渡しできない場合です。これは パラメータの参照渡し としても知られています。
ctypes
は byref()
関数をエクスポートしており、パラメータを参照渡しするために使用します。 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
型である必要があります。
以下は、 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
>>>
しかし、もっと複雑な構造体を構築することもできます。ある構造体は、他の構造体をフィールド型として使うことで、他の構造体を含むことができます。
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>
>>>
警告
ctypes
では、 bit 型フィールドのある共用体や構造体の関数への値渡しはサポートしていません。これは 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 ?
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 ?
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 ?
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 かゼロになっているレコードで終端されている配列を指すよう初期化されます。フリーズされたモジュールを import するとき、このテーブルを検索します。サードパーティ製のコードは、この動作に工夫を加えて、フリーズ化モジュールの集合を動的に作成して提供することもできるかもしれません。
これで、このポインタを操作することが役に立つことを証明できるでしょう。例の大きさを制限するために、このテーブルを 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 はフローズンモジュールとフローズンパッケージ (負のサイズのメンバーで表されています) を持っているという事実はあまり知られておらず、テストにだけ使われています。例えば、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
>>>
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 オブジェクトがその度に作られます。
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.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. 関数プロトタイプ¶
外部関数は関数プロトタイプをインスタンス化することによって作成されます。関数プロトタイプは C の関数プロトタイプと似ています。実装を定義せずに、関数 (戻り値、引数の型、呼び出し規約) を記述します。ファクトリ関数は関数に要求する戻り値の型と引数の型とともに呼び出されます。
-
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_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)
でなければなりません。第一要素はエクスポートされた関数の名前である文字列、またはエクスポートされた関数の序数である小さい整数です。第二要素は共有ライブラリインスタンスです。
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 の 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)¶ この関数は変更可能なユニコード文字バッファを作成します。返されるオブジェクトは
c_wchar
の ctypes 配列です。init_or_size は配列のサイズを指定する整数もしくは配列要素を初期化するために使われる文字列である必要があります。
第一引数として文字列が指定された場合は、バッファが文字列の長さより一要素分大きく作られます。配列の最後の要素が NUL 終端文字であるためです。文字列の長さを使うべきでない場合は、配列のサイズを指定するために整数を第二引数として渡すことができます。
-
ctypes.
DllCanUnloadNow
()¶ Windows 用: この関数は ctypes をつかってインプロセス COM サーバーを実装できるようにするためのフックです。_ctypes 拡張 dll がエクスポートしている DllCanUnloadNow 関数から呼び出されます。
-
ctypes.
DllGetClassObject
()¶ Windows 用: この関数は ctypes をつかってインプロセス COM サーバーを実装できるようにするためのフックです。
_ctypes
拡張 dll がエクスポートしている DllGetClassObject 関数から呼び出されます。
-
ctypes.util.
find_library
(name)¶ ライブラリを検索し、パス名を返します。 name は
lib
のような接頭辞、.so
や.dylib
のような接尾辞、そして、バージョンナンバーを除くライブラリ名です (これは posix のリンカーオプション-l
で使われる書式です) 。もしライブラリが見つからなければ、None
を返します。厳密な機能はシステムに依存します。
-
ctypes.util.
find_msvcrt
()¶ Windows 用: Python と拡張モジュールで使われる VC ランタイムライブラリのファイル名を返します。もしライブラリ名が同定できなければ、
None
を返します。もし、例えば拡張モジュールにより割り付けられたメモリを
free(void *)
で解放する必要があるなら、メモリ割り付けを行ったのと同じライブラリの関数を使うことが重要です。
-
ctypes.
FormatError
([code])¶ Windows 用: エラーコード code の説明文を返します。エラーコードが指定されない場合は、Windows api 関数 GetLastError を呼び出して、もっとも新しいエラーコードが使われます。
-
ctypes.
GetLastError
()¶ Windows 用: 呼び出し側のスレッド内で Windows によって設定された最新のエラーコードを返します。この関数はWindowsの GetLastError() 関数を直接実行します。ctypesのプライベートなエラーコードのコピーを返したりはしません。
-
ctypes.
get_last_error
()¶ Windows 用: システムの
LastError
変数の、スレッドローカルなプライベートコピーを返します。
-
ctypes.
memmove
(dst, src, count)¶ 標準 C の memmove ライブラリ関数と同じものです: count バイトを src から dst へコピーします。dst と src はポインタへ変換可能な整数または ctypes インスタンスでなければなりません。
-
ctypes.
memset
(dst, c, count)¶ 標準 C の memset ライブラリ関数と同じものです: アドレス 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 の内部メモリバッファのサイズを変更します。obj は ctypes 型のインスタンスでなければなりません。バッファを
sizeof(type(obj))
で与えられるオブジェクト型の本来のサイズより小さくすることはできませんが、バッファを拡大することはできます。
-
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 化できます。インスタンスは一つだけ属性を持ちます:
基本データ型は、外部関数呼び出しの結果として返されたときや、例えば構造体のフィールドメンバーや配列要素を取り出すときに、ネイティブの Python 型へ透過的に変換されます。言い換えると、外部関数が c_char_p
の restype
を持つ場合は、 c_char_p
インスタンスでは なく 常に Python バイト列オブジェクトを受け取ることでしょう。
基本データ型のサブクラスはこの振る舞いを継承 しません 。したがって、外部関数の restype
が c_void_p
のサブクラスならば、関数呼び出しからこのサブクラスのインスタンスを受け取ります。もちろん、 value
属性にアクセスしてポインタの値を得ることができます。
これらが基本データ型です:
-
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_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_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
データ型を表し、値はユニコード文字列の単一の文字として解釈されます。コンストラクタはオプションの文字列初期化子を受け取り、その文字列の長さはちょうど一文字である必要があります。
-
class
ctypes.
c_wchar_p
¶ C
wchar_t *
データ型を表し、ゼロ終端ワイド文字列へのポインタでなければなりません。コンストラクタは整数のアドレスもしくは文字列を受け取ります。
-
class
ctypes.
c_bool
¶ C の
bool
データ型 (より正確には、 C99 以降の_Bool
) を表します。True
またはFalse
の値を持ち、コンストラクタは真偽値と解釈できるオブジェクトを受け取ります。
-
class
ctypes.
HRESULT
¶ Windows用:
HRESULT
値を表し、関数またはメソッド呼び出しに対する成功またはエラーの情報を含んでいます。
-
class
ctypes.
py_object
¶ C
PyObject *
データ型を表します。引数なしでこれを呼び出すとNULL
PyObject *
ポインタを作成します。
ctypes.wintypes
モジュールは他の Windows 固有のデータ型を提供します。例えば、 HWND
, WPARAM
, DWORD
です。 MSG
や RECT
のような有用な構造体も定義されています。
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.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_
に存在しない名前に対しては新しい属性を作ります。-
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
¶ ポインタが指すオブジェクトを返します。この属性に割り当てると、ポインタが割り当てられたオブジェクトを指すようになります。
-