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.
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)