#!/usr/bin/python3.10 # -*- mode: python; Encoding: utf-8; coding: utf-8 -*- # Last updated: <2026/01/05 17:57:56 +0900> """ 与えられたウインドウハンドル(HWND)を親として、 子ウインドウを作成して1色で塗り潰す。 スクリーンセーバのプレビューモードの反応を確認するために作成。 以下のオプションを受け付ける。 * /p : プレビュー画面モード * /c : 設定画面モード。今回はメッセージボックスのみを表示 Windows11 x64 25H2 + Python 3.10.10 64bit """ import sys import ctypes import win32gui import win32api import win32con dbg = False # dbg = True # 描画処理をするかしないか draw_enable = True # draw_enable = False WINDOW_CLASSNAME = "PythonFillOnlySimpleWindow" # Windows11等の高解像度ディスプレイへの対応 try: ctypes.windll.shcore.SetProcessDpiAwareness(1) except Exception: ctypes.windll.user32.SetProcessDPIAware() def print_log(s: str): """コンソールにメッセージを出力""" if dbg: print(s) class MyChildWindow: """子ウインドウ担当クラス""" def __init__(self, parent_hwnd): """初期化処理""" self.parent_hwnd = parent_hwnd # 親のウインドウハンドル self.class_name = WINDOW_CLASSNAME self.paint_count = 0 self.hwnd = self._create_window() def _create_window(self): """子ウインドウを作成""" wc = win32gui.WNDCLASS() wc.lpfnWndProc = self.wnd_proc wc.lpszClassName = self.class_name wc.hInstance = win32api.GetModuleHandle(None) wc.hCursor = win32gui.LoadCursor(0, win32con.IDC_ARROW) wc.hbrBackground = win32gui.CreateSolidBrush(win32api.RGB(0, 0, 0)) # リサイズ時に再描画を要求 # wc.style = win32con.CS_HREDRAW | win32con.CS_VREDRAW try: # 定義したクラスをOSに登録 win32gui.RegisterClass(wc) except win32gui.error as e: # 登録済みなら Error code = 1410 が出るので、それ以外なら例外を出す if e.winerror != 1410: raise # 親のクライアント領域を取得 rect = win32gui.GetClientRect(self.parent_hwnd) w = rect[2] - rect[0] h = rect[3] - rect[1] # ウインドウを作成。ウインドウハンドルを返す hwnd = win32gui.CreateWindowEx( 0, # 拡張スタイル(今回は無し) self.class_name, # 登録クラス名 "Child Window", # ウインドウタイトル win32con.WS_CHILD | win32con.WS_VISIBLE, # 子ウィンドウとして作成、即表示 0, 0, w, h, # 親の左上(0,0)を起点に配置 self.parent_hwnd, # 親ウィンドウハンドル 0, # メニューなし win32api.GetModuleHandle(None), # インスタンス None, # 追加パラメータなし ) if not hwnd: print_log("[Child] Error: ウインドウ生成に失敗") return None print_log(f"[Child] ウインドウ生成。HWND: {hwnd}") return hwnd def wnd_proc(self, hwnd, msg, wparam, lparam): """ウインドウプロシージャ""" if msg == win32con.WM_PAINT: # 描画を要求された if win32gui.IsWindow(self.hwnd): self.paint_count += 1 print_log(f"[Child] WM_PAINT 受信。count={self.paint_count}") hdc = win32gui.GetDC(hwnd) # 描画を準備。hdc(デバイスコンテキスト)取得 rect = win32gui.GetClientRect(hwnd) # クライアント領域のサイズを取得 col = win32api.RGB(0, 0, 255) # 色を設定 brush = win32gui.CreateSolidBrush(col) # ブラシを作成 win32gui.FillRect(hdc, rect, brush) # 塗り潰し # クリーンアップ。ブラシやhdcを削除や開放しないとメモリリークを起こす win32gui.DeleteObject(brush) win32gui.ReleaseDC(hwnd, hdc) return 0 # メッセージを処理した場合は0を返してやる if msg == win32con.WM_DESTROY: # ウインドウの破棄を要求された print_log("[Child] WM_DESTROY 受信。Quit を送信。") win32gui.PostQuitMessage(0) return 0 # 自分で処理しないメッセージはOS既定の処理に渡す return win32gui.DefWindowProc(hwnd, msg, wparam, lparam) def run(self): if win32gui.IsWindow(self.hwnd): # 初回描画を強制的に促す(メッセージキューにWM_PAINTを入れる) win32gui.InvalidateRect(self.hwnd, None, True) # キューを待たずに、今すぐ描画を反映 win32gui.UpdateWindow(self.hwnd) # メッセージループ開始。 # OSからの信号(WM_PAINT等)を待ち受けて wnd_procへ流し続ける # これがないとスクリプトは終了してしまう。 win32gui.PumpMessages() return def config_mode(): """設定画面モード""" # メッセージボックスのみを表示 message = "Config mode" title = "Information" ctypes.windll.user32.MessageBoxW(None, message, title, 0) def main(): # コマンドラインオプションを解析して処理を分ける args = sys.argv[1:] if len(args) >= 1: opt = args[0].lower() if opt == "/p" and len(args) == 2: hwnd = int(sys.argv[2]) if draw_enable: # 子ウインドウを生成して描画する状態 if win32gui.IsWindow(hwnd): preview = MyChildWindow(hwnd) preview.run() else: # 何もしないで抜ける状態 return elif opt == "/c": config_mode() elif opt == "/s": print_log("Fullscreen mode") else: config_mode() else: config_mode() print_log("[Child] プロセス終了") if __name__ == "__main__": main()