Python win32api SendMesage

user2046488 picture user2046488 · Feb 9, 2013 · Viewed 16.8k times · Source

I am trying to clarify win32api. And I just made a simple example. Get the Notepad window, move the mouse to a position, click and write a string. But it does not work. What's the problem?
And could anybody clarify for me what the lParam parameter is?
What does it do, What type is it and How should it look?

import win32api, win32con, win32gui, win32ui, win32service, os, time



def f_click(pycwnd):
        x=300
        y=300
        lParam = y <<15 | x
        pycwnd.SendMessage(win32con.WM_LBUTTONDOWN, win32con.MK_LBUTTON, lParam);
        pycwnd.SendMessage(win32con.WM_LBUTTONUP, 0, lParam);

def get_whndl():
        whndl = win32gui.FindWindowEx(0, 0, None, 'NB.txt - Notepad')
        return whndl

def make_pycwnd(hwnd):       
        PyCWnd = win32ui.CreateWindowFromHandle(hwnd)
        return PyCWnd
        
def send_input_hax(pycwnd, msg):
    f_click(pycwnd)
    for c in msg:
        if c == "\n":
            pycwnd.SendMessage(win32con.WM_KEYDOWN, win32con.VK_RETURN, 0)
            pycwnd.SendMessage(win32con.WM_KEYUP, win32con.VK_RETURN, 0)
        else:
            pycwnd.SendMessage(win32con.WM_CHAR, ord(c), 0)
    pycwnd.UpdateWindow()
        
whndl = get_whndl()
pycwnd = make_pycwnd(whndl)
msg = "It works !\n"
send_input_hax(pycwnd,msg)

Answer

Igonato picture Igonato · Feb 9, 2013

There is another window inside a Notepad's main one, you need to send your messages to it. You can see this 'hidden' window with Microsoft Spy++ tool or you can get all child windows like so:

def callback(hwnd, hwnds):
    if win32gui.IsWindowVisible(hwnd) and win32gui.IsWindowEnabled(hwnd):
        hwnds[win32gui.GetClassName(hwnd)] = hwnd
    return True

hwnds = {}
win32gui.EnumChildWindows(whndl, callback, hwnds)

Window we are looking for has 'Edit' class name and it is the only enabled and visible child window for Notepad. So your code will work this way:

import win32api, win32con, win32gui, win32ui, win32service, os, time


def f_click(pycwnd):
    x=300
    y=300
    lParam = y <<15 | x
    pycwnd.SendMessage(win32con.WM_LBUTTONDOWN, win32con.MK_LBUTTON, lParam);
    pycwnd.SendMessage(win32con.WM_LBUTTONUP, 0, lParam);

def get_whndl():
    whndl = win32gui.FindWindowEx(0, 0, None, 'NB.txt - Notepad')
    return whndl

def make_pycwnd(hwnd):       
    PyCWnd = win32ui.CreateWindowFromHandle(hwnd)
    return PyCWnd

def send_input_hax(pycwnd, msg):
    f_click(pycwnd)
    for c in msg:
        if c == "\n":
            pycwnd.SendMessage(win32con.WM_KEYDOWN, win32con.VK_RETURN, 0)
            pycwnd.SendMessage(win32con.WM_KEYUP, win32con.VK_RETURN, 0)
        else:
            pycwnd.SendMessage(win32con.WM_CHAR, ord(c), 0)
    pycwnd.UpdateWindow()

whndl = get_whndl()

def callback(hwnd, hwnds):
    if win32gui.IsWindowVisible(hwnd) and win32gui.IsWindowEnabled(hwnd):
        hwnds[win32gui.GetClassName(hwnd)] = hwnd
    return True
hwnds = {}
win32gui.EnumChildWindows(whndl, callback, hwnds)
whndl = hwnds['Edit']

pycwnd = make_pycwnd(whndl)
msg = "It works !\n"
send_input_hax(pycwnd,msg)

lParam is int and what you see here is trick that allows you to pass more than one value through a single argument. Let's say that we need to pass two digits to a function which takes only one argument. We can send them as double digit number and split it inside function. Same way bitwise shift (<<) and bitwise or (|) operations are also reversable in your case:

>>> x = 300
>>> y = 300
>>> lParam = y << 15 | x
>>> lParam & 0x7FFF # x
0: 300
>>> lParam >> 15 # y
1: 300

You can read more about bitwise operations in Wikipedia and Python Wiki.