Pythonでウィンドウメッセージを受け取る(Windows API)
最近、Windows APIのウィンドウメッセージを取得しないといけないことがありました。
Windows APIなんか触ったことないし、Pythonでのウィンドウメッセージの取得の方法の記事なんか全然なくて、もうC++とか.NETなどを使うしかないかなと思ったのですが、どうにかPythonでできたので今回記事を書きました。(需要はほぼないと思いますが)
windowを作成し、ウィンドメッセージを処理する
コードは以下の通りです。コメントでコードの説明を入れましたのでご覧ください。
特別なことは何もやっておらず、ただウィンドウを作成してウィンドウメッセージを処理する関数を定義してあげただけです。
自分はここで作成したウィンドウハンドルを使って、APIのようなものに渡してあげて、そのAPIからメッセージを投げてもらって、メッセージを処理しました。
from ctypes import * from ctypes.wintypes import * # 関数プロトタイプ(CやC++の関数の宣言で使われるやつでインターフェイスを示すためのもの) # 第一引数が戻り値の型、それ以降はPyWndProcedureの引数の型 WNDPROCTYPE = WINFUNCTYPE(c_int, HWND, c_uint, WPARAM, LPARAM) # ウィンドウクラスの各項目を設定する際に使われる構造体 class WNDCLASSEX(Structure): _fields_ = [("cbSize", c_uint), ("style", c_uint), ("lpfnWndProc", WNDPROCTYPE), ("cbClsExtra", c_int), ("cbWndExtra", c_int), ("hInstance", HANDLE), ("hIcon", HANDLE), ("hCursor", HANDLE), ("hBrush", HANDLE), ("lpszMenuName", LPCWSTR), ("lpszClassName", LPCWSTR), ("hIconSm", HANDLE)] class Window: WS_OVERLAPPEDWINDOW = 0xcf0000 WS_CAPTION = 0xc00000 SW_SHOW = 5 CS_HREDRAW = 2 CS_VREDRAW = 1 CW_USEDEFAULT = 0x80000000 WM_DESTROY = 2 WHITE_BRUSH = 0 def __init__(self, handle_data_callback): def PyWndProcedure(hWnd, Msg, wParam, lParam): # ここに受け取ったメッセージごとの処理を書く if Msg == self.WM_DESTROY: windll.user32.PostQuitMessage(0) # 特定のメッセージに対してやりたい処理をかませる elif Msg == 10000: handle_data_callback() # メッセージのデフォルトの処理は元々の処理系に任せる。 return windll.user32.DefWindowProcW(hWnd, Msg, wParam, lParam) WndProc = WNDPROCTYPE(PyWndProcedure) # GetModuleHandleWは現在呼び出し元プロセスにロードされているexeやdllのメモリ上の位置を示すアドレスを返す hInst = windll.kernel32.GetModuleHandleW(0) wclassName = 'My Python Win32 Class' wname = 'My window' wndClass = WNDCLASSEX() wndClass.cbSize = sizeof(WNDCLASSEX) wndClass.style = self.CS_HREDRAW | self.CS_VREDRAW wndClass.lpfnWndProc = WndProc wndClass.cbClsExtra = 0 wndClass.cbWndExtra = 0 wndClass.hInstance = hInst wndClass.hIcon = 0 wndClass.hCursor = 0 wndClass.hBrush = windll.gdi32.GetStockObject(self.WHITE_BRUSH) wndClass.lpszMenuName = 0 wndClass.lpszClassName = wclassName wndClass.hIconSm = 0 # byrefは参照によってパラメータを渡すために使うためのもの(pointerと同じような働きだがこちらの方が高速) regRes = windll.user32.RegisterClassExW(byref(wndClass)) # ウィンドウハンドルが返り値。ウィンドウハンドルとはコンピュータが各ウィンドウに割り振る管理番号のこと # ANSI文字列、Unicodeのどちらを使用するかという区別で関数名の最後にAかWがついている。(C言語はオーバーロードがないため別の関数になってるらしい) 今回はWで統一 hWnd = windll.user32.CreateWindowExW( 0,wclassName,wname, self.WS_OVERLAPPEDWINDOW | self.WS_CAPTION, self.CW_USEDEFAULT, self.CW_USEDEFAULT, 300,300,0,0,hInst,0) if not hWnd: print('Failed to create window') exit(0) # ウィンドウの表示(表示せずただメッセージを受け取りたいだけの場合はコメントアウト) print('ShowWindow', windll.user32.ShowWindow(hWnd, SW_SHOW)) print('UpdateWindow', windll.user32.UpdateWindow(hWnd)) msg = MSG() lpmsg = pointer(msg) # GetMessageは呼び出し側スレッドのメッセージキューからメッセージを取得し、指定された構造体にそのメッセージを格納する。 while windll.user32.GetMessageW(lpmsg, 0, 0, 0) != 0: windll.user32.TranslateMessage(lpmsg) windll.user32.DispatchMessageW(lpmsg) def handle_message(): print("メッセージ受け取ったよ!") if __name__ == '__main__': win = Window(handle_message)
参照
https://stackoverflow.com/questions/5353883/python3-ctype-createwindowex-simple-example https://gist.github.com/mouseroot/6128651