句柄点击Windows窗体按钮

Python笔记 无标签 2020-03-9 阅读:275

特别声明:本文只是作为一个笔记记录,代码借鉴网络上的多个文章,因时间久远没记住每个出处,如有侵权,请联系我立即进行删除处理。
如得知来源,我会注明每段内容的出处,诚邀daolao指点。

一、点击“普通(Button)按钮”--通过按钮句柄发送点击消息

0x00 先导入win32相关的库

import win32api,win32con,win32gui

0x01 获取程序的主体窗口的句柄 Handle,这里需要用到程序的标题Title或类名Class,可以使用Spy++这类工具获取标题和类名。

handle = win32gui.FindWindow(Title,Class) 

0x02 遍历程序的所有子句柄

childHandleList = []
win32gui.EnumChildWindows(handle, lambda hwnd, param: param.append(hwnd), childHandleList)

0x03 已知子句柄,得到每个句柄的标题

childTitleList = [] # 子句柄标题
for i in childHandleList:
        childTitleList.append(win32gui.GetClassName(i))

0x04 将子句柄list与子句柄标题list一一组合,方便后面通过标题得到句柄

childFormList = dict(zip(childHandleList, childTitleList))

0x05 已知按钮的标题,从子句柄标题中查找,得到对应的子句柄,也就是按钮的句柄

keys = [x[0] for x in childFormList.items() if '按钮标题' in x[1]]
    for i in keys:
            buttonHandle= i

0x06 得到按钮的句柄后,就能向按钮发送点击消息

SendMessage(buttonHandle,WM_LBUTTONDOWN,0,0);//按下鼠标左键
SendMessage(buttonHandle, WM_LBUTTONUP, 0, 0);//松开鼠标左键

小结:直接用Spy++工具得到按钮的句柄,然后用SendMessage发送点击消息,这种方法最简单。但是,如果有其他需求,可能会用到更复杂的操作。

二、点击“菜单(Menu)按钮”--通过菜单ID发送点击消息

0x00 导入win32相关库

import win32api,win32con,win32gui

0x01 获取程序的主体窗口的句柄 Handle

handle = win32gui.FindWindow(Title,Class)

0x02 获取菜单句柄

menu = win32gui.GetMenu(handle)

0x03 通过菜单句柄,根据菜单、子菜单ID定位想要点击的菜单

menu1 = win32gui.GetSubMenu(menu, 0)#第几个菜单
cmd_ID = win32gui.GetMenuItemID(menu1, 1)#第几个子菜单

备注:菜单ID是根据菜单所在顺序来定的,菜单的起始ID为0,子菜单的起始ID为0

0x04 发送点击消息

win32gui.PostMessage(hwnd, win32con.WM_COMMAND, cmd_ID, 0)

小结:需要获取父菜单ID、子菜单ID,才能定位想要点击的菜单,ID需要自己按照菜单分布顺序来数,如果向确认菜单ID是否正确,可以将其输出显示出来,代码如下。

# 获取菜单的文本内容
def get_menu_item_txt(menu, idx):
    import win32gui_struct
    mii, extra = win32gui_struct.EmptyMENUITEMINFO() # 新建一个win32gui的空的结构体mii
    win32gui.GetMenuItemInfo(menu, idx, True, mii)
    ftype, fstate, wid, hsubmenu, hbmpchecked, hbmpunchecked,\
    dwitemdata, text, hbmpitem = win32gui_struct.UnpackMENUITEMINFO(mii) # 解包mii
    return text

# 遍历显示出菜单文本
for i in range(5):
    print(get_menu_item_txt(menu, i))
for i in range(5):
    print(get_menu_item_txt(menu1, i))

三、点击“工具栏(ToolerBarWindow32)按钮”--定位按钮坐标发送点击消息

0x00 导入win32相关库

import win32api, win32gui, win32con
import time
import ctypes

0x01 获取程序的主体窗口的句柄 Handle

handle = win32gui.FindWindow(Title,Class)

0x02 从电脑运行中的程序中查找目标程序,目的:不使用Spyt++这类工具帮助获取程序句柄/标题/类

'''
 获取主窗体句柄
'''
mainFormList = dict() # 存储运行中的所有程序的'句柄'和'标题',键值对的形式存储

def get_all_hwnd(hwnd,mouse):
    if win32gui.IsWindow(hwnd) and win32gui.IsWindowEnabled(hwnd) and win32gui.IsWindowVisible(hwnd):
        mainFormList.update({hwnd:win32gui.GetWindowText(hwnd)}) # 更新mainFormList
win32gui.EnumWindows(get_all_hwnd, 0)

# 已知目标程序Title,然后从mainFormList中查找包含目标程序Title数据,成功得到Title对应的句柄。
keys = [x[0] for x in mainFormList.items() if '目标程序Title' in x[1]]  # keys即为目标程序句柄

# 得到的句柄keys,输出的数据有方括号“[]”,所以需要去除“[]”
for i in keys:
       mainHandle = i

0x03 已得到目标程序的主句柄mainHanlde,接下来获取它的子窗体ToolbarWindow32的句柄

childHandleList = [] # 子窗体句柄list
childTitleList = [] # 子窗体标题list
win32gui.EnumChildWindows(mainHandle, lambda hwnd, param: param.append(hwnd), childHandleList) # 通过主窗体句柄,得到所有子窗体句柄,并存储到childHandleList中

# 通过childHandleList存储的句柄,得到所有句柄的标题,并存储到 childTitleList
for i in childHandleList:
    childTitleList.append(win32gui.GetClassName(i))

# 将子窗体的'句柄'和'标题'两个list组成字典
childFormList = dict(zip(childHandleList, childTitleList))
# Ps:将句柄和标题列表合并成为字典,得到标题就能得到相应的句柄

# 从childFormList中查找标题为“ToolbarWindow32”的数据,便成功获取Toolbarwindow32的句柄
keys = [x[0] for x in childFormList.items() if 'ToolbarWindow32' in x[1]]

# 得到的句柄keys,输出的数据有方括号“[]”, 所以需要去除“[]”
 for i in keys:
        toolbarwindow32Handle = i # 得到 ToolbarWindow32 窗体的句柄

0x04 得到ToolbarWindow32的句柄后,接下来获取它的屏幕坐标

在获取ToolbarWindow32窗体的坐标之前,先要保证该程序没有处于后台运行

所以先将程序置顶显示,然后获取ToolbarWindow32窗体的坐标

# 置顶窗口
win32gui.ShowWindow(mainHandle, win32con.SW_SHOWNORMAL)
win32gui.SetForegroundWindow(mainHandle)
time.sleep(0.5) # 给窗口置顶一个缓冲时间,也可以不要,时间根据情况而定

#得到ToolbarWindow32窗体的左上角、右下角的坐标--绝对坐标
left, top, right, bottom = win32gui.GetWindowRect(ToolbarHandle)

#算出要得到的工具栏按钮的坐标位置,用屏幕测量工具得到像素距离
buttonX = left + 像素值  # 按钮的X轴坐标
buttonY = top + 像素值  # 按钮的Y轴坐标

0x05 鼠标定位到按钮坐标,并执行点击事件

# 鼠标定位到按钮的坐标位置
win32api.SetCursorPos((buttonX, buttonY))
    
# 执行鼠标左键单击
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN | win32con.MOUSEEVENTF_LEFTUP, 0,0,0,0)

小结:因为ToolbarWindow32这类窗体中的按钮没有独立的句柄,所以只好使用坐标定位点击的方式,实现工具栏按钮的点击。

关于工具栏按钮的点击,如果有不用坐标的方式就能实现,请留言告诉我,万分感谢!

版权声明

本文基于《署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)》许可协议授权
文章链接:https://tooler.club/archives/python-click-windows-button.html (转载时请注明本文出处及文章链接)

如果觉得我的文章对你有用,请随意赞赏

0条评论