檜山正幸のキマイラ飼育記 このページをアンテナに追加 RSSフィード Twitter

キマイラ・サイトは http://www.chimaira.org/です。
トラックバック/コメントは日付を気にせずにどうぞ。
連絡は hiyama{at}chimaira{dot}org へ。
蒸し返し歓迎!
このブログの更新は、Twitterアカウント @m_hiyama で通知されます。
Follow @m_hiyama
ところで、アーカイブってけっこう便利ですよ。

2012-03-05 (月)

Pythonから呼ぶWin32 APIがうまく動かない件

| 09:32 | Pythonから呼ぶWin32 APIがうまく動かない件を含むブックマーク

pyreadline-1.7.1に入れ替えたら、それまで動いていたWin32を呼ぶクリアスクリーンが動かなくなってしまった、という現象がありまして、その理由がわかりませんでした。にわか仕込みでデバッガ(pdb)を使ったりしましたがヤッパリわからない。

結局、次のエラーメッセージのとおりだったんですね。

  File "./lib/win32cls.py", line 67, in clear_screen
    coord, byref(dwDummy))
ArgumentError: argument 4: <type 'exceptions.TypeError'>: wrong type

File "./lib/win32cls.py", line 67 は次のようなコード。

    FillConsoleOutputAttribute (hConOut, csbi.wAttributes, 
                                csbi.dwSize.X * csbi.dwSize.Y, 
                                coord, byref(dwDummy))

FillConsoleOutputAttribute関数の第4引数(argument 4)は、coordですが、このcoordが実際に wrong type だったようです。X,Y座標を含むデータであるcoordの初期化は次のとおり。

  coord = COORD() # 初期値 coord.X = 0, coord.Y = 0

COORDは、もともとはC言語の構造体ですが、それをPythonクラスとして定義しています。

class COORD(Structure):
  """struct in wincon.h."""
  _fields_ = [
    ("X", SHORT),
    ("Y", SHORT)]

このCOORD型データがWin32 API関数にうまく渡らないようです。それに気付いたのは、pyreadlineのconsole.pyのなかにfixcoordという関数を見つけたからです。必要な部分だけを抜き書きすると:

# pyreadline-1.7.1/pyreadline/console/cosole.py
#
#       Copyright (C) 2003-2006 Gary Bishop.
#       Copyright (C) 2006  Jorgen Stenarson. <jorgen.stenarson@bostream.nu>
#
#  Distributed under the terms of the BSD License.


def fixcoord(x, y):
    u'''Return a long with x and y packed inside.'''
    
    # this is a hack! ctypes won't pass structures but COORD is 
    # just like a long, so this works.
    return c_int(y << 16 | x)

座標を作るときは、例えば COORD(2, 3) の代わりに fixcoord(2, 3) とします。そうするとエラーはなくなります。

しかし不思議なのは、以前pyreadline-1.5と併用していたときはfixcoordなしで動いていたことです。pyreadline-1.5がWindows APIをフックするようなことは考えにくいし、pyreadline-1.5自体も逐一fixcoordしています。ウーン謎だ。

トラックバック - http://d.hatena.ne.jp/m-hiyama/20120305