python3 ctype CreateWindowEx simple example
Asked Answered
T

3

7

I have googled for some time but could not find simple example of python3 ctypes and Win32 API for creating and showing window. Please point me to good link or show code here.

Thanks in advance!

Tonkin answered 18/3, 2011 at 15:7 Comment(0)
P
12

This is most easy to do with the win32gui module and its friends, win32api and win32con. There's no need to write your own ctypes wrappers to the Windows API. The simplest Petzold style app comes out something like this:

import win32api, win32con, win32gui

class MyWindow:

    def __init__(self):
        win32gui.InitCommonControls()
        self.hinst = win32api.GetModuleHandle(None)
        className = 'MyWndClass'
        message_map = {
            win32con.WM_DESTROY: self.OnDestroy,
        }
        wc = win32gui.WNDCLASS()
        wc.style = win32con.CS_HREDRAW | win32con.CS_VREDRAW
        wc.lpfnWndProc = message_map
        wc.lpszClassName = className
        win32gui.RegisterClass(wc)
        style = win32con.WS_OVERLAPPEDWINDOW
        self.hwnd = win32gui.CreateWindow(
            className,
            'My win32api app',
            style,
            win32con.CW_USEDEFAULT,
            win32con.CW_USEDEFAULT,
            300,
            300,
            0,
            0,
            self.hinst,
            None
        )
        win32gui.UpdateWindow(self.hwnd)
        win32gui.ShowWindow(self.hwnd, win32con.SW_SHOW)

    def OnDestroy(self, hwnd, message, wparam, lparam):
        win32gui.PostQuitMessage(0)
        return True

w = MyWindow()
win32gui.PumpMessages()
Prelatism answered 18/3, 2011 at 17:25 Comment(8)
Python version is 3.2 and I get ImportError: No module named win32con error message. It seems that there are no win32api, win32con, win32gui modulesTonkin
You just haven't got it installed. Get it from here: sourceforge.net/projects/pywin32/files/pywin32/Build216Prelatism
depends on which distro. My ActiveState 2.x come with it, but the ActiveState 3.x don't. You are living life on the bleeding edge with Python 3.x though, always more likely to run into wrinkles like this. But it does look promising that it will fly on 3.x.Prelatism
I haven't been able to make PyWin32 work on anything but Anaconda (which comes with it), other attempts on Py27, 32, and 34 have only loaded the binary modules the first run, if at all, which is why I posted a native solution over PyWin32.Marcelo
@Marcelo pip install pypiwin32Prelatism
Thanks, but tbh, it's a bit late, I already made my decision to drop PyWin32 back on python32's release due to native modules already being available _winapi, ctypes.windll.user32, etc... just seems a bit pointless to use an extension when there's already native functionality ykno :)Marcelo
@Marcelo That's not what you said before. You said you couldn't install it. Not having to translate the Win32 header files is certainly convenient, but if masocishm is your thing.... To be fair, raw Win32 programming is an exercise in masocishm in any language, but python is especially painful for this.Prelatism
I'd gotten it installed on Py27, 32, and 34, as stated above (I'd also attempted py35, but was never successful), and I do believe pip was the most successful method... I never got it working with Py27 as it always said it couldn't find the binaries, but for py3, it would work just fine the first run, and then fail to load the binaries on any following runs.Marcelo
M
6

Found this nice little trinket and took the time to get it working on nothing but the standard library of vanilla python 3.4.0:
(for those who wish to use natives over PyWin32)
http://code.activestate.com/recipes/208699-calling-windows-api-using-ctypes-and-win32con/

import sys
from ctypes import *

kernel32 = windll.kernel32
user32 = windll.user32
gdi32 = windll.gdi32

NULL = 0
CW_USEDEFAULT = -2147483648
IDI_APPLICATION = 32512
WS_OVERLAPPEDWINDOW = 13565952

CS_HREDRAW = 2
CS_VREDRAW = 1

IDC_ARROW = 32512
WHITE_BRUSH = 0

SW_SHOWNORMAL = 1

WNDPROC = WINFUNCTYPE(c_long, c_int, c_uint, c_int, c_int)

class WNDCLASS(Structure):
    _fields_ = [('style', c_uint),
                ('lpfnWndProc', WNDPROC),
                ('cbClsExtra', c_int),
                ('cbWndExtra', c_int),
                ('hInstance', c_int),
                ('hIcon', c_int),
                ('hCursor', c_int),
                ('hbrBackground', c_int),
                ('lpszMenuName', c_char_p),
                ('lpszClassName', c_char_p)]

class RECT(Structure):
    _fields_ = [('left', c_long),
                ('top', c_long),
                ('right', c_long),
                ('bottom', c_long)]

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

class POINT(Structure):
    _fields_ = [('x', c_long),
                ('y', c_long)]

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

def ErrorIfZero(handle):
    if handle == 0:
        raise WinError
    else:
        return handle

def MainWin():
    global NULL
    CreateWindowEx          = user32.CreateWindowExA
    CreateWindowEx.argtypes = [c_int, c_char_p, c_char_p, c_int, c_int, c_int, c_int, c_int, c_int, c_int, c_int, c_int]
    CreateWindowEx.restype  = ErrorIfZero

    # Define Window Class
    wndclass = WNDCLASS()
    wndclass.style          = CS_HREDRAW | CS_VREDRAW
    wndclass.lpfnWndProc    = WNDPROC(WndProc)
    wndclass.cbClsExtra = wndclass.cbWndExtra = 0
    wndclass.hInstance      = kernel32.GetModuleHandleA(c_int(NULL))
    wndclass.hIcon          = user32.LoadIconA(c_int(NULL), c_int(IDI_APPLICATION))
    wndclass.hCursor        = user32.LoadCursorA(c_int(NULL), c_int(IDC_ARROW))
    wndclass.hbrBackground  = gdi32.GetStockObject(c_int(WHITE_BRUSH))
    wndclass.lpszMenuName   = None
    wndclass.lpszClassName  = b"MainWin"
    # Register Window Class
    if not user32.RegisterClassA(byref(wndclass)):
        raise WinError()
    # Create Window
    hwnd = CreateWindowEx(0,
                          wndclass.lpszClassName,
                          b"Python Window",
                          WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT,
                          CW_USEDEFAULT,
                          CW_USEDEFAULT,
                          CW_USEDEFAULT,
                          NULL,
                          NULL,
                          wndclass.hInstance,
                          NULL)
    # Show Window
    user32.ShowWindow(c_int(hwnd), c_int(SW_SHOWNORMAL))
    user32.UpdateWindow(c_int(hwnd))
    # Pump Messages
    msg = MSG()
    pMsg = pointer(msg)
    NULL = c_int(NULL)

    while user32.GetMessageA( pMsg, NULL, 0, 0) != 0:
        user32.TranslateMessage(pMsg)
        user32.DispatchMessageA(pMsg)

    return msg.wParam

WM_PAINT = 15
WM_DESTROY = 2

DT_SINGLELINE = 32
DT_CENTER = 1
DT_VCENTER = 4
def WndProc(hwnd, message, wParam, lParam):
    ps = PAINTSTRUCT()
    rect = RECT()

    if message == WM_PAINT:
        hdc = user32.BeginPaint(c_int(hwnd), byref(ps))
        user32.GetClientRect(c_int(hwnd), byref(rect))
        user32.DrawTextA(c_int(hdc),
                         b"Python Powered Windows" ,
                         c_int(-1), byref(rect), 
                         DT_SINGLELINE|DT_CENTER|DT_VCENTER)
        user32.EndPaint(c_int(hwnd), byref(ps))
        return 0
    elif message == WM_DESTROY:
        user32.PostQuitMessage(0)
        return 0

    return user32.DefWindowProcA(c_int(hwnd), c_int(message), c_int(wParam), c_int(lParam))

if __name__=='__main__':
    sys.exit(MainWin())


Marcelo answered 4/12, 2018 at 18:24 Comment(3)
I would love to use this code but I get an error with Python 3.8 in PyCharm 2020.1: OSError: [WinError 87] Wrong Parameter. (translated from German). This seems to be related to the line raise WinError() but the actual problem then is that user32.RegisterClassA(wndclass) returns false.Generalist
@Ben have you found a fix for this? I run into the following error on Python 3.8.6: wndclass.lpszClassName = "MainWin" TypeError: bytes or integer address expected instead of str instanceThanet
I don't remember, probably not.Generalist
O
6

Here's a pure ctypes version based on the @Tcll answer ported to "wide" APIs as well. The original version didn't handle 64-bit Python correctly (casting handles to c_int) and was using the ANSI APIs, which isn't recommended anymore. It also declares full argtypes/restype for everything to help catch coding errors.

As you can see, it's much easier to use pywin32 instead.

Tested on Python 2.7 32-bit, Python 3.6 64-bit and Python 3.8 32-bit.

#coding:utf8
from __future__ import unicode_literals

import sys
import ctypes as ct
import ctypes.wintypes as w  # ctypes has many pre-defined Windows types

def errcheck(result, func, args):
    if result is None or result == 0:
        raise ct.WinError(ct.get_last_error())
    return result

def minusonecheck(result, func, args):
    if result == -1:
        raise ct.WinError(ct.get_last_error())
    return result

# Missing from ctypes.wintypes...
LRESULT = ct.c_int64
HCURSOR = ct.c_void_p

WNDPROC = ct.WINFUNCTYPE(LRESULT, w.HWND, w.UINT, w.WPARAM, w.LPARAM)

def MAKEINTRESOURCE(x):
    return w.LPCWSTR(x)

class WNDCLASS(ct.Structure):
    _fields_ = (('style', w.UINT),
                ('lpfnWndProc', WNDPROC),
                ('cbClsExtra', ct.c_int),
                ('cbWndExtra', ct.c_int),
                ('hInstance', w.HINSTANCE),
                ('hIcon', w.HICON),
                ('hCursor', HCURSOR),
                ('hbrBackground', w.HBRUSH),
                ('lpszMenuName', w.LPCWSTR),
                ('lpszClassName', w.LPCWSTR))

class PAINTSTRUCT(ct.Structure):
    _fields_ = (('hdc', w.HDC),
                ('fErase', w.BOOL),
                ('rcPaint', w.RECT),
                ('fRestore', w.BOOL),
                ('fIncUpdate', w.BOOL),
                ('rgbReserved', w.BYTE * 32))

kernel32 = ct.WinDLL('kernel32', use_last_error=True)
GetModuleHandle = kernel32.GetModuleHandleW
GetModuleHandle.argtypes = w.LPCWSTR,
GetModuleHandle.restype = w.HMODULE
GetModuleHandle.errcheck = errcheck

user32 = ct.WinDLL('user32', use_last_error=True)
CreateWindowEx = user32.CreateWindowExW
CreateWindowEx.argtypes = w.DWORD, w.LPCWSTR, w.LPCWSTR, w.DWORD, ct.c_int, ct.c_int, ct.c_int, ct.c_int, w.HWND, w.HMENU, w.HINSTANCE, w.LPVOID
CreateWindowEx.restype = w.HWND
CreateWindowEx.errcheck = errcheck
LoadIcon = user32.LoadIconW
LoadIcon.argtypes = w.HINSTANCE, w.LPCWSTR
LoadIcon.restype = w.HICON
LoadIcon.errcheck = errcheck
LoadCursor = user32.LoadCursorW
LoadCursor.argtypes = w.HINSTANCE, w.LPCWSTR
LoadCursor.restype = HCURSOR
LoadCursor.errcheck = errcheck
RegisterClass = user32.RegisterClassW
RegisterClass.argtypes = ct.POINTER(WNDCLASS),
RegisterClass.restype = w.ATOM
RegisterClass.errcheck = errcheck
ShowWindow = user32.ShowWindow
ShowWindow.argtypes = w.HWND, ct.c_int
ShowWindow.restype = w.BOOL
UpdateWindow = user32.UpdateWindow
UpdateWindow.argtypes = w.HWND,
UpdateWindow.restype = w.BOOL
UpdateWindow.errcheck = errcheck
GetMessage = user32.GetMessageW
GetMessage.argtypes = ct.POINTER(w.MSG), w.HWND, w.UINT, w.UINT
GetMessage.restype = w.BOOL
GetMessage.errcheck = minusonecheck
TranslateMessage = user32.TranslateMessage
TranslateMessage.argtypes = ct.POINTER(w.MSG),
TranslateMessage.restype = w.BOOL
DispatchMessage = user32.DispatchMessageW
DispatchMessage.argtypes = ct.POINTER(w.MSG),
DispatchMessage.restype = LRESULT
BeginPaint = user32.BeginPaint
BeginPaint.argtypes = w.HWND, ct.POINTER(PAINTSTRUCT)
BeginPaint.restype = w.HDC
GetClientRect = user32.GetClientRect
GetClientRect.argtypes = w.HWND, ct.POINTER(w.RECT)
GetClientRect.restype = w.BOOL
GetClientRect.errcheck = errcheck
DrawText = user32.DrawTextW
DrawText.argtypes = w.HDC, w.LPCWSTR, ct.c_int, ct.POINTER(w.RECT), w.UINT
DrawText.restype = ct.c_int
EndPaint = user32.EndPaint
EndPaint.argtypes = w.HWND, ct.POINTER(PAINTSTRUCT)
EndPaint.restype = w.BOOL
PostQuitMessage = user32.PostQuitMessage
PostQuitMessage.argtypes = ct.c_int,
PostQuitMessage.restype = None
DefWindowProc = user32.DefWindowProcW
DefWindowProc.argtypes = w.HWND, w.UINT, w.WPARAM, w.LPARAM
DefWindowProc.restype = LRESULT

gdi32 = ct.WinDLL('gdi32', use_last_error=True)
GetStockObject = gdi32.GetStockObject
GetStockObject.argtypes = ct.c_int,
GetStockObject.restype = w.HGDIOBJ

CW_USEDEFAULT = ct.c_int(0x80000000).value
IDI_APPLICATION = MAKEINTRESOURCE(32512)

WS_OVERLAPPED  = 0x00000000
WS_CAPTION     = 0x00C00000
WS_SYSMENU     = 0x00080000
WS_THICKFRAME  = 0x00040000
WS_MINIMIZEBOX = 0x00020000
WS_MAXIMIZEBOX = 0x00010000

WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX
assert WS_OVERLAPPEDWINDOW == 0x00CF0000

CS_HREDRAW = 2
CS_VREDRAW = 1

IDC_ARROW = MAKEINTRESOURCE(32512)
WHITE_BRUSH = 0

SW_SHOWNORMAL = 1

WM_PAINT = 15
WM_DESTROY = 2
DT_SINGLELINE = 32
DT_CENTER = 1
DT_VCENTER = 4

def MainWin():
    # Define Window Class
    wndclass = WNDCLASS()
    wndclass.style         = CS_HREDRAW | CS_VREDRAW
    wndclass.lpfnWndProc   = WNDPROC(WndProc)
    wndclass.cbClsExtra    = 0
    wndclass.cbWndExtra    = 0
    wndclass.hInstance     = GetModuleHandle(None)
    wndclass.hIcon         = LoadIcon(None, IDI_APPLICATION)
    wndclass.hCursor       = LoadCursor(None, IDC_ARROW)
    wndclass.hbrBackground = GetStockObject(WHITE_BRUSH)
    wndclass.lpszMenuName  = None
    wndclass.lpszClassName = 'MainWin'

    # Register Window Class
    RegisterClass(ct.byref(wndclass))

    # Create Window
    hwnd = CreateWindowEx(0,
                          wndclass.lpszClassName,
                          'Python Window',
                          WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT,
                          CW_USEDEFAULT,
                          CW_USEDEFAULT,
                          CW_USEDEFAULT,
                          None,
                          None,
                          wndclass.hInstance,
                          None)
    # Show Window
    user32.ShowWindow(hwnd, SW_SHOWNORMAL)
    user32.UpdateWindow(hwnd)

    # Pump Messages
    msg = w.MSG()
    while GetMessage(ct.byref(msg), None, 0, 0) != 0:
        TranslateMessage(ct.byref(msg))
        DispatchMessage(ct.byref(msg))

    return msg.wParam

def WndProc(hwnd, message, wParam, lParam):
    ps = PAINTSTRUCT()
    rect = w.RECT()

    if message == WM_PAINT:
        hdc = BeginPaint(hwnd, ct.byref(ps))
        GetClientRect(hwnd, ct.byref(rect))
        DrawText(hdc,
                 'Python Powered Windows 你好吗?',
                 -1, ct.byref(rect),
                 DT_SINGLELINE|DT_CENTER|DT_VCENTER)
        EndPaint(hwnd, ct.byref(ps))
        return 0
    elif message == WM_DESTROY:
        PostQuitMessage(0)
        return 0

    return DefWindowProc(hwnd, message, wParam, lParam)

if __name__=='__main__':
    sys.exit(MainWin())

enter image description here

Ohm answered 5/1, 2021 at 0:22 Comment(2)
Minor note that pywin32 usually doesn't run more than once after installation (it can't find itself), the best way to install pywin32 is to install Anaconda which comes with a proper package that works repeatedly.Marcelo
@Marcelo there is a post install step if installed via pip in the Scripts subdirectoryOhm

© 2022 - 2024 — McMap. All rights reserved.