Search code examples
c#python-3.xtkinterpywin32windows-messages

Why does my pywin32 SendMessage doesn't work?


I am trying to understand python's capability to use Windows Messages with pywin32 library. For this I have created 2 scripts that are supposed to run parralel to each other. One is a client that is supposed to send messages the other is a server that is supposed to print all incoming messages. To use window messeages I have given them gui windows with tkinter library.

But when I run them paralel server doesn't recieve any messages even though it seems that there are no errors and the windows handles check out. I made sure that I was reaching the correct handle using Microsoft Spy++ in Visual Studio.

I find their handles using this function:


def get_window_handle_by_tittle(tittle):
    window_handle = [0]

    def window_callback(hwnd, lParam):
        window_tittle, window_handle = lParam
        current_tittle = win32gui.GetWindowText(hwnd)
        if current_tittle == window_tittle:
        # check if this is the target window
            window_handle[0] = hwnd
            return False
        else:
            return True

    try:
        # Start the enumeration of top-level windows

        lParam = (tittle, window_handle)
        win32gui.EnumWindows(window_callback, lParam)

        return window_handle[0]
    except pywintypes.error as e:
        if window_handle[0] and window_handle[0] != 0:
            # Check window title if provided

            return window_handle[0]
        else:
            return None
    except Exception as e:
        print(f"Error getting window handle: {e}")

And here are the code for client and the codes for client and the server that I run as different python scripts' main functions.

import time
import win32api
import win32gui
import tkinter as tk

import pywintypes


WM_USER = 0x0400
WM_STATEREQUEST = WM_USER + 1005
WM_STATEANSWER = WM_USER + 1006

def client:
    root = tk.Tk()
    root.title("WM_client")
    root.geometry("400x300")
    root.update()
    root.deiconify()
    python_hwnd = get_window_handle_by_tittle("WM_client")
    print(f"Server handle is {hex(python_hwnd)}, starting listening")
    target = get_window_handle_by_tittle("WM_server")
    while True:
        print(f"message to be sent {hex(int(target))}, {WM_STATEREQUEST} , {hex(python_hwnd)}, 0")

        response = win32api.SendMessage(target, WM_STATEREQUEST, python_hwnd, 0)
        print("server sent message response code", response)
        print("last error given", win32api.GetLastError())
        time.sleep(1)
def server:

    root = tk.Tk()
    root.title("WM_server")
    root.geometry("400x300")
    root.update()
    root.deiconify()
    python_hwnd = get_window_handle_by_tittle("WM_server")
    print(f"Server handle is {hex(python_hwnd)}, starting listening")

    while True:
        msg = win32gui.PeekMessage(python_hwnd, 0, 100, win32con.PM_NOREMOVE)
        if msg[0] != 0:
            # Extract values from the tuple

            window_handle, message_type, w_param, l_param, time_posted, (cursor_x, cursor_y) = msg[1]
            if message_type != 96:
            # exclude windows paint messages 
                # Print each value
                print("\nWindow Handle:", window_handle)
                print("Message Type:", hex(message_type), message_type)
                print("wParam:", w_param)
                print("lParam:", l_param, "\n")

Again this is not a problem with Server's PeekMessage function but instead with client. Because if that were e the case I would have been able too see them using Spy++. So right now I don't believe I need to make a hook function instead of pywin32's peek message function. Problem here is messages are not being sent or atleast not being sent to the correct window somehow.

I have expecerd the server to print out the messages that are send by SendMessage() function. Especially after confirming the window handles of the script windows.


Solution

  • Apperently peekMessages don't work for me had to create a listener class with proper hook functioning to listen for messages. I still don't really know how the win32py library works for intents of windows messages but this soliton I took from another place works for me.

    class Listener:
        def __init__(self):
    
            message_map = {
                WM_MESSAGEONE : self.OnMessageOne , 
                WM_MESSAGETWO : self.OnMessageTwo
            }
            wc = win32gui.WNDCLASS()
    
            wc.lpfnWndProc = self.WndProc  # Set window procedure
            wc.lpszClassName = 'message-listener'
            hinst = wc.hInstance = win32api.GetModuleHandle(None)
            classAtom = win32gui.RegisterClass(wc)
            self.hwnd = win32gui.CreateWindow (
                classAtom,
                "WM_server",
                0,
                0,
                0,
                win32con.CW_USEDEFAULT,
                win32con.CW_USEDEFAULT,
                0,
                0,
                hinst,
                None
            )
    
        def WndProc(self, hwnd, msg, wparam, lparam):
            if msg == win32con.WM_COPYDATA:
                return 0
            elif msg == WM_MESSAGEONE :  # Handle custom message
                return self.OnMessageOne (hwnd, msg, wparam, lparam)
            elif msg == WM_MESSAGETWO : # Handle custom message
                return self.OnMessageTwo(hwnd, msg, wparam, lparam)
            else:
                return win32gui.DefWindowProc(hwnd, msg, wparam, lparam)