Python 3 から MessageBox (Windows API) を呼び出す
| 公開日: | |
|---|---|
| 最終更新日: |
タイトルの通りのことを行いたい場合, Python ドキュメント によれば,下記のコードで実現できるとされている.
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(text="Spam, spam, spam")
MessageBox(flags=2, text="foo bar")
しかし,予期に反して Python 3 ではこのコードは動作せず,以下のエラーが出力される.
Traceback (most recent call last):
File "<stdin>", line 1, in <?>
ctypes.ArgumentError: argument 2: <class 'TypeError'>: wrong type
原因はエラーメッセージの通り,つまり,Unicode文字列を,バイト列を引数にとるMessageBoxAへ渡そうとしていることである.
この場合,MessageBoxAの代わりにMessageBoxWを呼び出すよう以下のように修正すれば,期待する動作となるはずだ.
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", None), (1, "flags", 0)
MessageBox = prototype(("MessageBoxW", windll.user32), paramflags)
MessageBox()
MessageBox(text="Spam, spam, spam")
MessageBox(flags=2, text="foo bar")
あるいは,特別な事情(?)によりMessageBoxAを呼び出さなくてはならない場合は,以下のコードにより問題を部分的に解決できる.
from ctypes import c_int, WINFUNCTYPE, windll
from ctypes.wintypes import HWND, LPCSTR, UINT
from locale import getpreferredencoding
preferredEncoding = getpreferredencoding()
prototype = WINFUNCTYPE(c_int, HWND, LPCSTR, LPCSTR, UINT)
paramflags = (1, "hwnd", 0), (1, "text", "Hi".encode(preferredEncoding)), (1, "caption", None), (1, "flags", 0)
MessageBox = prototype(("MessageBoxA", windll.user32), paramflags)
MessageBox()
MessageBox(text="Spam, spam, spam".encode(preferredEncoding))
MessageBox(flags=2, text="foo bar".encode(preferredEncoding))
getpreferredencoding()によりシステムのデフォルトエンコーディングを取得し,Unicode文字列をそれにエンコードしたものをMessageBoxAへ渡せばよい.
ただし,エンコードできない文字を含んだUnicode文字列の場合この方法では当然失敗するし,オーバヘッドも生じるので,MessageBoxWを呼び出すことを推奨したい.