8

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)
ppwater
  • 2,315
  • 4
  • 15
  • 29
user2046488
  • 233
  • 3
  • 4
  • 6

1 Answers1

14

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 << 16 | 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 << 16 | x
>>> lParam & 0x7FFF # x
0: 300
>>> lParam >> 16 # y
1: 300

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

Igonato
  • 10,175
  • 3
  • 35
  • 64
  • 2
    thanks, your explanation and wiki clarify it for me, it works now. Yes, notepad has another subwindow. But I will put a remark about lParam. It is a just 4 bytes digit, so to get right lParam value I should convert coords, which are integer (x=300,y=300), to 2 bytes digits(WORD), than concatenate them in right order(low word is x, high is y). Btw in C++ there is macros MAKELPARAM(), so you can also call it in python through using ctypes module. – user2046488 Feb 10 '13 at 14:08
  • 1
    Shoudn't it be `lParam = y << 16 | x`? – mFoxRU Apr 26 '18 at 12:54
  • @mFoxRU Just checked win api docs, I think you're right, docs say it's low-order word and high-order word, word meaning 16 bits. Just works in this case since 300 is even (has 0 at the end). 15 was in the original question, and I didn't think too much of it – Igonato Apr 27 '18 at 04:41
  • @mFoxRU yes, I tried with 15 and the coordinates were off. Using 16 fixed it – Ruehri Nov 01 '18 at 13:00