LoginSignup
3
1

More than 1 year has passed since last update.

Python Win32 APIでウインドウをつくる

Posted at

実際のところ、Pythonでこれをやる意味は皆無ですが...
WinMainで始まるWindowsプログラムそのものをPythonでつくってみます。
以下のサンプルプログラムをPythonに直します。

Win32 APIで避けて通れないのは、独特な型と複雑な構造体です。
ウインドウメッセージの構造体MSGは、

winuser.h
typedef struct tagMSG {
    HWND        hwnd;
    UINT        message;
    WPARAM      wParam;
    LPARAM      lParam;
    DWORD       time;
    POINT       pt;
} MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;

Pythonでは以下のようにして(自分で)定義します。

class MSG(Structure):
    _fields_ = [
        ("hwnd", c_void_p),
        ("message", c_int),
        ("wParam", POINTER(c_int)),
        ("lParam", POINTER(c_int)),
        ("time", c_int),
        ("pt", POINT)]

ハンドル(HWND)はポインタなのでc_void_p、他はとりあえずc_intと考えればよいです。
WPARAM,LPARAMはc_intにするとaccess violationになったので、POINTER(c_int)としました。
POINTはWindowsで定義されている構造体です。
このようにして、必要なすべての構造体と定数を定義しておいて(ctypes_windows.py)

from ctypes_windows import *

def WindowProc(hwnd, uMsg, wParam, lParam):
    if uMsg == WM_DESTROY:
        PostQuitMessage(0)
        return 0
    elif uMsg == WM_PAINT:
        ps = PAINTSTRUCT()
        hdc = BeginPaint(hwnd, pointer(ps))
        FillRect(hdc, pointer(ps.rcPaint), c_void_p(COLOR_WINDOW))
        EndPaint(hwnd, pointer(ps))
        return 0
    return DefWindowProc(hwnd, uMsg, wParam, lParam)

def WinMain():
    hInstance = GetModuleHandle(None)

    CLASS_NAME = create_string_buffer(b"Sample Window Class")

    wc = WNDCLASS()
    wc.lpfnWndProc = WNDPROC(WindowProc)
    wc.hInstance = hInstance
    wc.lpszClassName = CLASS_NAME

    RegisterClass(pointer(wc))

    lpWindowName = create_string_buffer(b"Learn to Program Windows")
    hwnd = CreateWindowEx(0, CLASS_NAME, lpWindowName, WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
                          None, None, hInstance, None)
    if hwnd == None:
        raise Exception

    ShowWindow(hwnd, SW_SHOWNORMAL)

    msg = MSG()
    while (GetMessage(pointer(msg), None, 0, 0)):
        TranslateMessage(pointer(msg))
        DispatchMessage(pointer(msg))

if __name__ == "__main__":
    WinMain()

実行結果
window.png

構造体と定数

ctypes_windows.py
from ctypes import *

GetModuleHandle = windll.kernel32.GetModuleHandleA
RegisterClass = windll.user32.RegisterClassA
CreateWindowEx = windll.user32.CreateWindowExA
ShowWindow = windll.user32.ShowWindow
GetMessage = windll.user32.GetMessageA
TranslateMessage = windll.user32.TranslateMessage
DispatchMessage = windll.user32.DispatchMessageA
DefWindowProc = windll.user32.DefWindowProcA
PostQuitMessage = windll.user32.PostQuitMessage
BeginPaint = windll.user32.BeginPaint
FillRect = windll.user32.FillRect
EndPaint = windll.user32.EndPaint

WNDPROC = WINFUNCTYPE(c_int, c_void_p, c_int, POINTER(c_int), POINTER(c_int))

class WNDCLASS(Structure):
    _fields_ = [
        ("style", c_int),
        ("lpfnWndProc", WNDPROC),
        ("cbClsExtra", c_int),
        ("cbWndExtra", c_int),
        ("hInstance", c_void_p),
        ("hIcon", c_void_p),
        ("hCursor", c_void_p),
        ("hbrBackground", c_void_p),
        ("lpszMenuName", POINTER(c_char)),
        ("lpszClassName", POINTER(c_char))]

class POINT(Structure):
    _fields_ = [
        ("x", c_int),
        ("y", c_int)]

class MSG(Structure):
    _fields_ = [
        ("hwnd", c_void_p),
        ("message", c_int),
        ("wParam", c_int),
        ("lParam", c_int),
        ("time", c_int),
        ("pt", POINT)]

class RECT(Structure):
    _fields_ = [
        ("left", c_int),
        ("top", c_int),
        ("right", c_int),
        ("bottom", c_int)]

class PAINTSTRUCT(Structure):
    _fields_ = [
        ("hdc", c_void_p),
        ("fErase", c_int),
        ("rcPaint", RECT),
        ("fRestore", c_int),
        ("fIncUpdate", c_int),
        ("rgbReserved", c_char*32)]

WS_OVERLAPPED       = 0x00000000
WS_POPUP            = 0x80000000
WS_CHILD            = 0x40000000
WS_MINIMIZE         = 0x20000000
WS_VISIBLE          = 0x10000000
WS_DISABLED         = 0x08000000
WS_CLIPSIBLINGS     = 0x04000000
WS_CLIPCHILDREN     = 0x02000000
WS_MAXIMIZE         = 0x01000000
WS_CAPTION          = 0x00C00000
WS_BORDER           = 0x00800000
WS_DLGFRAME         = 0x00400000
WS_VSCROLL          = 0x00200000
WS_HSCROLL          = 0x00100000
WS_SYSMENU          = 0x00080000
WS_THICKFRAME       = 0x00040000
WS_GROUP            = 0x00020000
WS_TABSTOP          = 0x00010000
WS_MINIMIZEBOX      = 0x00020000
WS_MAXIMIZEBOX      = 0x00010000
WS_OVERLAPPEDWINDOW = (WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX)

CW_USEDEFAULT = 0x80000000
SW_SHOWNORMAL = 10

WM_CREATE = 0x0001
WM_DESTROY = 0x0002
WM_COMMAND = 0x0111
WM_PAINT = 0x000f

COLOR_WINDOW = 5

window.png

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1