Pythonから固定長文字列を含む構造体を要求するWin APIを呼び出す他
Pythonから固定長文字列を含む構造体を要求するWin APIを呼び出す方法のサンプルと途中で学んだことの覚書です。最初は実際のコードから。
サンプルコード
このコードではGetVersionEx関数とOSVERSIONINFO構造体(固定長文字列''TCHAR szCSDVersion[128]''を含む)を用いて、固定長文字列を含む構造体を要求するWin APIの呼び出しを確認します。
from ctypes import * class OSVERSIONINFO(Structure): _fields_ = [ ("os_version_info_size", c_uint), ("major_version", c_uint), ("minor_version", c_uint), ("build_number", c_uint), ("platform_id", c_uint), ("csd_version", c_wchar * 128)] def __init__(self): self.os_version_info_size = 20 + 128 * 2 osverinfo = OSVERSIONINFO() ret = windll.Kernel32.GetVersionExW(pointer(osverinfo)) print(osverinfo.csd_version)
補足
ctypesパッケージ
Pythonで「動的リンク/共有ライブラリを純粋な Python でラップするために使う」機能を提供するパッケージです。詳細な説明は公式ドキュメントを参考にして下さい。ドキュメントを精読した訳ではないので以下の説明も一部間違っている可能性があります。
windll.Kernel32.GetVersionExW(pointer(...))
左から順番に意味を説明すると、 windll:C言語でいうstdcall呼び出し規約(WINAPI/APIENTRY)で Kernel32:Kernel32(.dll)に存在する GetVersionEx:GetVersionExW関数を呼び出しなさい。 pointer(...):引数はpointer(...)とする ということだそうです。
実際にはctypes内部でwindll
という変数が定義されていて、「.Kernel32
」はそれが実装する__getattr__
を呼び出す仕組みだと思います。__getattr__
はKernel32を読み込んだクラスのインスタンスを返しますが、そのクラスがまた__getattr__
を実装しているので.GetVersionExW
でその名前のクラスのインスタンスが返されます。
GetVersionEx(...)
部分ではpointer(osverinfo)
とすることで事前に作成したOSVERSIONINFO
クラス(Structure
クラスから派生)のポインタを渡すことを明示しています。数値(0, 1, 2,...)や文字列では直に指定することもできるかと思いますが、同じように明示したければc_int(0)
といったc_*
クラスを指定して型を指定します。
呼び出し規約をcdeclに変えたい場合はcdll.Kernel32
のように呼び出します。
getattr
クラスのインスタンスに存在しない属性が参照された場合に呼び出されます。以下のコードが最も簡単な例です。
class class1: def __getattr__(self, name): return name if __name__ == "__main__": c1 = class1() print(c1.abc) print(c1.defg) #defはキーワードなのでシンタックスエラーが発生します。
少し複雑にするとwindll.~
のようにクラスを返すことも出来ます。
class class2: def __init__(self, name): self.name = name def __getattr__(self, name): return "{0}, {1}".format(self.name, name) class class1: def __getattr__(self, name): return class2(name) if __name__ == "__main__": print(class1().abc.defg)
なお、インスタンスに存在する属性が参照された場合も呼び出されるメソッドとして__getattribute__
があります。
ctypesの構造体に固定長配列を持たせる
上のサンプルコードのcsd_version
のようにc_* * n
あるいは<Structureの派生型> * n
とすれば良いそうです。
class OSVERSIONINFO(Structure): _fields_ = [ ("os_version_info_size", c_uint), ("major_version", c_uint), ("minor_version", c_uint), ("build_number", c_uint), ("platform_id", c_uint), ("csd_version", c_wchar * 128)] ...