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を呼び出すことを推奨したい.