pywin32:入门_win32con.wm_settext-程序员宅基地

技术标签: 模块方法  python  

-想整个QQ自动发消息的,于是找到了这个,这个模块呢就是用来调用window系统的API,来打开个程序,自动发个消息之类的,更高级的线程进程啥的咱也不会。。。

链接: https://pan.baidu.com/s/1mqrFYh3TGUVQ8gIcXoJ1BQ 提取码: fr9x pywin32 的帮助文档,,,
链接: https://pan.baidu.com/s/1U26Q23JqJblrUIfvGBZSQw 提取码: h5vq spy++的

window 消息机制

参考脑补链接
消息是系统定义的一个32位值,定义了一个事件。消息的结构(MSG):

typedef struct tagMsg
{
    
       HWND    hwnd;            //接受该消息的窗口句柄,就是消息所属的窗口,一般是16进制的一堆数,表示这资源内存的标识号
       UINT    message;         //消息常量标识符,也就是我们通常所说的消息号,其实就是一些常量,也就五千多个吧。。
       WPARAM  wParam;     //32位消息的特定附加信息,确切含义依赖于消息值,
       LPARAM  lParam;        //32位消息的特定附加信息,确切含义依赖于消息值
       DWORD   time;            //消息创建时的时间
       POINT   pt;                  //消息创建时的鼠标/光标在屏幕坐标系中的位置
}MSG;

消息标识符:WM_NULL—0x0000 空消息。具体的查询就要看MSDN了(微软的开发文档)
这里面还可以查询所有消息的,wParam and lParam。感觉不错。。。。就是没有中文。。。

当鼠标、键盘事件被触发后,相应的鼠标或键盘驱动程序就会把这些事件转换成相应的消息,然后输送到系统消息队列,由
Windows系统去进行处理。Windows系统则在适当的时机,从系统消息队列中取出一个消息,根据前面我们所说的MSG消息结构确定消息是要被送往那个窗口,然后把取出的消息送往创建窗口的线程的相应队列,下面的事情就该由线程消息队列操心了,Windows开始忙自己的事情去了。线程看到自己的消息队列中有消息,就从队列中取出来,通过操作系统发送到合适的窗口过程去处理。

pywin32

网上实在找不到全面的教程,,,至于帮助文档,,各种模块的方法倒是很全,但是完全没讲怎么和消息的结合,自带的例子,,呵呵看不懂。所以就尽量把所有能找到的实例研究一遍,估计常用的方法也差不多了,毕竟超超超极多的消息和方法也没打算全记住。。。

win32api 提供了常用的用户API

win32clipboard 提供了有关粘贴板的API

win32gui 提供了有关windows用户界面图形操作的API

win32file 提供了有关文件操作的API

win32con 有关的消息常量,,,感觉这些模块都好用欸,,一步一步来吧

spy++ 的使用

这个软件是来看窗口的句柄,类名的,很好用啊,不过人家说是 VC 自带的,我咋没有呢。。。
参考脑补连接

栗子一:启动记事本写入文字再另存为

参考脑补连接

import win32gui         # 先来小小的体验一下
import win32con
win = win32gui.FindWindow('Notepad','新建文本文档.txt - 记事本')          # 找到这个文件,里面的参数都是再spy++ 里面找的
tid = win32gui.FindWindowEx(win,None,'Edit',None)                       # 主窗口下的子窗口
win32gui.SendMessage(tid, win32con.WM_SETTEXT, None, '呐呐呐')  # 写入字段
win32gui.PostMessage(tid,win32con.WM_KEYDOWN,win32con.VK_RETURN,0)       # 插入一个回车

在这里插入图片描述
下面就是我再这个例子中学到的一些函数和消息。参考这位大佬写的四篇文章,,可惜不更新了

启动记事本

win32api.ShellExecute int = ShellExecute(hwnd, op , file , params ,
dir , bShow ) 打开或打印一个文件。 Parameters

hwnd : PyHANDLE

父窗口的句柄,如果没有父窗口,则为0。该窗口接收应用程序生成的任何消息框(例如,用于错误报告)。

op : string

要执行的操作。可以是“打开”,“打印”或“无”,默认为“打开”。

file : string 文件名

params : string

如果文件名包含可执行文件,则传递的参数。对于文档文件,应为None。

dir : string

应用程序的初始目录。

bShow : int

指定在打开应用程序时是否显示该应用程序。如果lpszFile参数指定一个文档文件,则此参数为零。

win32api.Sleep
int = Sleep(time, bAlterable )

在指定的时间内暂停当前线程的执行。

Parameters

time : int

The number of milli-seconds to sleep for,

bAlterable=0 : int

指定该函数是否可能由于I / O完成回调函数而提前终止。

Return Value 如果指定的时间间隔到期,则返回值为零。


win32gui.FindWindow PyHANDLE = FindWindow(ClassName, WindowName )

检索顶级窗口的句柄,该窗口的类名和窗口名与指定的字符串匹配。Parameters

ClassName : 要查找的窗口类的名,可以为None

WindowName : 查找窗口的标题,可以为None


win32gui.FindWindowEx PyHANDLE = FindWindowEx(Parent, ChildAfter , ClassName , WindowName )

Parameters

Parent : PyHANDLE 将搜索其子窗口的窗口。如果为0,则假定为桌面窗口。

ChildAfter : PyHANDLE 以Z顺序搜索之后的子窗口,可以为0以搜索所有子窗口

ClassName :

WindowName :

save_path = r'C:\Users\NERO\Desktop\栗子一.txt' # 这个也是以后保存的文件名
notepad_path=r'C:\Windows\notepad.exe'       # 这个是记事本应用的路径
win32api.ShellExecute(0,'open',notepad_path,'','',1)
sasveas_posilst=[0,3]
while True:
    win = win32gui.FindWindow('Notepad','')     # 来个循环,等待窗口的开启。
    if win != 0:                         # 如果开启win 就是这个窗口的句柄了。
        win32api.Sleep(200)
        print('启动成功',win)
        break

写入文字

win32gui.SendMessage int = SendMessage(hwnd, message , wparam , lparam )

Sends a message to the window.

Parameters

hwnd : int

The handle to the Window

message : int

The ID of the message to post

wparam=None : int/str

Type depends on the message

lparam=None : int/str

Type depends on the message

edit_handle = win32gui.FindWindowEx(win,0,'Edit',None)                 # 找到子窗口,输入文字,这个Edit 再spy++ 里获得。
print(edit_handle)
win32gui.SendMessage(edit_handle,win32con.WM_SETTEXT,None,'现在我很困啊')

WM_SETTEXT message
wParam
不使用此参数。
lParam
指向以空值结尾的字符串的指针,该字符串是窗口文本。

将指定的消息发送到一个或多个窗口。该SendMessage函数的函数调用指定的窗口的窗口过程,并不会返回,直到窗口过程已经处理了该消息。 要发送消息并立即返回,请使用SendMessageCallback或SendNotifyMessage函数。

要将消息发布到线程的消息队列中并立即返回,只是把消息放入队列,不管其他程序是否处理都返 回,然后继续执行,请使用PostMessage或PostThreadMessage函数。

在控制别的应用程序的时候,经常需要等待直到某个功能结束,例如:
打开一个窗口–>等待直到窗口结束
这 个时候就可以用到SendMessage --------------------------------------------> SendMessage消息是同步的
如果在打开这个窗口后仍然需要对该窗口的界面进行设置,比如Edit的value等等,比如:
打 开一个窗口–>控制窗口的control的属性
这个时候就需要PostMessage --------------------------------------------> PostMessage消息是异步的

win32gui.PostMessage PostMessage(hwnd, message, wparam, lparam) 在消息队列中加入为指定的窗体加入一条消息,并马上返回,不等待线程对消息的处理。 Parameters

hwnd : int

The handle to the Window

message : int

The ID of the message to post

wparam=0 : int

An integer whose value depends on the message

lparam=0 : int

An integer whose value depends on the message

菜单操作

在这里插入图片描述

GetMenu(hwnd)            
描述:获取窗口的菜单句柄。
参数:
hwnd:整型,需要获取菜单的窗口的句柄。
说明:获取的是插图中黄色的部分。

GetSubMenu(hMenu, nPos)
描述:获取菜单的下拉菜单或者子菜单。
参数:
hMenu:整型,菜单的句柄,从GetMenu获得。
nPos:整型,下拉菜单或子菜单的的索引,从0算起。
说明:这个可以获取插图中蓝色的部分z;如描述所述,这个不仅可以获取本例中的下拉菜单,还可以获取子菜单。

GetMenuItemID(hMenu, nPos)
描述:获取菜单中特定项目的标识符。
参数:
hMenu:整型,包含所需菜单项的菜单句柄,从GetSubMenu获得。
nPos:整型,菜单项的索引,从0算起。
说明:这个获取的就是红色区域中的项目啦,注意,分隔符是被编入索引的,所以退出就是8,那几条线也算的。
menu_handle = win32gui.GetMenu(win)
menu_handle = win32gui.GetSubMenu(menu_handle,0)
com_ID = win32gui.GetMenuItemID(menu_handle,3)
win32gui.PostMessage(win,win32con.WM_COMMAND,com_ID,0)      # 不明白 WM_COMMAND的参数这么些,去MSDN搜吧,,
# 通过postmessage 来打开了另存为这个窗口。
while True:
    if win32gui.FindWindow(None, '另存为')!=0:
        win32api.Sleep(2000)
        print('另存为打开成功')
        break
    else:
        win32api.Sleep(200)

可以看到,当WM_COMMAND操作菜单时,wparm就是菜单标识符(高字节低字节的问题一会再说),Iparam0
在这里插入图片描述

选择utf-8 编码(comboBox下拉组合框操作),写入文件名,点击确定

本来不想用函数的,奈何人家写的函数太香了,,,,总的来一遍吧。。

save_path = r'C:\Users\NERO\Desktop\test.txt'
notepad_path=r'C:\Windows\notepad.exe'
win32api.ShellExecute(0,'open',notepad_path,'','',1)
sasveas_posilst=[0,3]
while True:
    win = win32gui.FindWindow('Notepad','')
    if win != 0:
        win32api.Sleep(200)
        print('启动成功',win)
        break
edit_handle = win32gui.FindWindowEx(win,0,'Edit',None)
print(edit_handle)
win32gui.SendMessage(edit_handle,win32con.WM_SETTEXT,None,'现在我很困啊')

menu_handle = win32gui.GetMenu(win)
menu_handle = win32gui.GetSubMenu(menu_handle,0)
com_ID = win32gui.GetMenuItemID(menu_handle,3)
win32gui.PostMessage(win,win32con.WM_COMMAND,com_ID,0)           # 这里用SendMessage 为啥不行呢。。。就打不开这个窗口
				# 我感觉应该是SendMessage 要等消息返回才继续,否则处于堵塞状态吧。。。											
while True:
    if win32gui.FindWindow(None, '另存为')!=0:        # 这里的循环就是等待postmessage发送的消息执行
        win32api.Sleep(2000)
        print('另存为打开成功')
        break
    else:
        win32api.Sleep(200)

在这里插入图片描述

save_win = win32gui.FindWindow(None, '另存为')
# save_name_handle = win32gui.FindWindowEx(save_win,0,'Edit','*.txt')
# win32gui.SendMessage(save_name_handle,win32con.WM_SETTEXT,None,save_path)
# 姑且试了下,那个文件名的框框再好几层下面,,直接是找不到的要一层一层下去,这就麻烦了,不过人家用递归写的函数,,精彩!!!

save_name_handle = find_handle_by_wndlist(save_win, [('DUIViewWndClassName',0),('DirectUIHWND',0),\
                                                         ('FloatNotifySink',0),('ComboBox',0),('Edit',0)])
win32gui.SendMessage(save_name_handle,win32con.WM_SETTEXT,None,save_path)      # 这里就可以把我们的文件名填进去了

comboBox_handle = find_handle_by_wndlist(save_win, [('ComboBox', 0)])  # 找到comboBox的句柄

select_comboBox_item(save_win,comboBox_handle,3)               # 这就是对编码的选择

save_btn_handle = find_handle_by_wndlist(save_win, [('Button', 0)])       # 找到确定按钮的句柄
win32api.SendMessage(save_win,win32con.WM_COMMAND,win32con.VK_LBUTTON,save_btn_handle)     # 执行点击操作,看不懂可以看靠下面的解析。

def find_idxSubHandle(pHandle, winClass, index=0):            # 还带索引的功能,真香,,,,,
    """
    已知子窗口的窗体类名
    寻找第index号个同类型的兄弟窗口
    """
    assert type(index) == int and index >= 0
    handle = win32gui.FindWindowEx(pHandle, 0, winClass, None)
    while index > 0:
        handle = win32gui.FindWindowEx(pHandle, handle, winClass, None)
        index -= 1
    return handle

def find_subHandle(pHandle, winClassList):
    """
    递归寻找子窗口的句柄
    pHandle是祖父窗口的句柄
    winClassList是各个子窗口的class列表,父辈的list-index小于子辈
    """
    assert type(winClassList) == list
    if len(winClassList) == 1:           # 如果就一层就直接找了
        return find_idxSubHandle(pHandle, winClassList[0][0], winClassList[0][1])
    else:                     # 对于多层,递归找下去
        pHandle = find_idxSubHandle(pHandle, winClassList[0][0], winClassList[0][1])
        return find_subHandle(pHandle, winClassList[1:])

def find_handle_by_wndlist(pHandle, winClassList):
    return find_subHandle(pHandle, winClassList)

def select_comboBox_item(PCB_handle, CB_handle, select_index):
    if win32api.SendMessage(CB_handle, win32con.CB_SETCURSEL, select_index, 0) == select_index:
        win32api.SendMessage(PCB_handle, win32con.WM_COMMAND, win32con.CBN_SELENDOK<<16+0,CB_handle)  # 组合框的控制标识符,控件的ID是0,所以低位直接加0
        win32api.SendMessage(PCB_handle, win32con.WM_COMMAND, win32con.CBN_SELCHANGE<<16+0, CB_handle) # ID在 spy里找就行
        print('改变完成')
    else:
        raise Exception("Change saving type failed")

CB_SETCURSEL 消息
描述:下拉框操作
参数:
wParam:以0起始的待选选项的索引;如果该值为-1,将从组合框列表中删除当前选项,并使当前选项为空
lParam:未使用。0
返回值:
更改选择成功将返回所设置选项的索引号。


CBN_SELENDOK 通知(notification code)
描述:当用户选择了有效的列表项时发送,提示父窗体处理用户的选择。父窗体通过WM_COMMAND消息接收这个通知。
参数:(作为WM_COMMAND的参数)
wParam:LOWORD为组合框的ID. HIWORD为CBN_SELENDOK的值。
lParam:组合框的句柄。


CBN_SELCHANGE 通知(notification code)
描述:当用户更改了列表项的选择时发送,不论用户是通过鼠标选择或是通过方向键选择都会发送此通知。父窗体通过WM_COMMAND消息接收这个通知。
参数:(作为WM_COMMAND的参数)
wParam:LOWORD为组合框的ID. HIWORD为CBN_SELCHANGE的值。
lParam:组合框的句柄。

也就是说,如果要改变下拉框的选项,要使用这三个消息。

众所周知,,,消息的32位的,而一个消息都有两个参数 ,wparam lparam,当参数不够用时,wparam就分成两半,高字节和低字节,

CBN_SELENDOK notification code
Parameters
wParam
.高字节指定通知代码,低字节就是控件ID(再spy++里找)
lParam
combo box.句柄

所以来个位运算 <<16 左移16位,低位加ID,就行啦

然后就是那个点击的操作,,,win32api.SendMessage(save_win,win32con.WM_COMMAND,win32con.VK_LBUTTON,save_btn_handle)
下面就是WM_COMMAND 参数所有的情况(网页自动翻译的,,可能不准吧)
在这里插入图片描述
这应该就是控制操作了,虚拟按键
VK_LBUTTON 0x01
鼠标左键,,,,至于为啥使用 win32api的SendMessage()我也不知道,测试了一下跟win32gui的这个一样的效果。。。。。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_46192930/article/details/106887785

智能推荐

海康威视网络摄像头开发流程(五)------- 直播页面测试_ezuikit 测试的url-程序员宅基地

文章浏览阅读3.8k次。1、将下载好的萤石js插件,添加到SoringBoot项目中。位置可参考下图所示。(容易出错的地方,在将js插件在html页面引入时,发生路径错误的问题)所以如果对页面中引入js的路径不清楚,可参考下图所示存放路径。2、将ezuikit.js引入到demo-live.html中。(可直接将如下代码复制到你创建的html页面中)<!DOCTYPE html><html lan..._ezuikit 测试的url

如何确定组态王与多动能RTU的通信方式_组态王ua-程序员宅基地

文章浏览阅读322次。第二步,在弹出的对话框选择,设备驱动—>PLC—>莫迪康—>ModbusRTU—>COM,根据配置软件选择的协议选期期,这里以此为例,然后点击“下一步”。第四步,把使用虚拟串口打勾(GPRS设备),根据需要选择要生成虚拟口,这里以选择KVCOM1为例,然后点击“下一步”设备ID即Modbus地址(1-255) 使用DTU时,为下485接口上的设备地址。第六步,Modbus的从机地址,与配置软件相同,这里以1为例,点击“下一步“第五步,Modbus的从机地址,与配置软件相同,这里以1为例,点击“下一步“_组态王ua

npm超详细安装(包括配置环境变量)!!!npm安装教程(node.js安装教程)_npm安装配置-程序员宅基地

文章浏览阅读9.4k次,点赞22次,收藏19次。安装npm相当于安装node.js,Node.js已自带npm,安装Node.js时会一起安装,npm的作用就是对Node.js依赖的包进行管理,也可以理解为用来安装/卸载Node.js需要装的东西_npm安装配置

火车头采集器AI伪原创【php源码】-程序员宅基地

文章浏览阅读748次,点赞21次,收藏26次。大家好,小编来为大家解答以下问题,python基础训练100题,python入门100例题,现在让我们一起来看看吧!宝子们还在新手村练级的时候,不单要吸入基础知识,夯实自己的理论基础,还要去实际操作练练手啊!由于文章篇幅限制,不可能将100道题全部呈现在此除了这些,下面还有我整理好的基础入门学习资料,视频和讲解文案都很齐全,用来入门绝对靠谱,需要的自提。保证100%免费这不,贴心的我爆肝给大家整理了这份今天给大家分享100道Python练习题。大家一定要给我三连啊~

Linux Ubuntu 安装 Sublime Text (无法使用 wget 命令,使用安装包下载)_ubuntu 安装sumlime text打不开-程序员宅基地

文章浏览阅读1k次。 为了在 Linux ( Ubuntu) 上安装sublime,一般大家都会选择常见的教程或是 sublime 官网教程,然而在国内这种方法可能失效。为此,需要用安装包安装。以下就是使用官网安装包安装的教程。打开 sublime 官网后,点击右上角 download, 或是直接访问点击打开链接,即可看到各个平台上的安装包。选择 Linux 64 位版并下载。下载后,打开终端,进入安装..._ubuntu 安装sumlime text打不开

CrossOver for Mac 2024无需安装 Windows 即可以在 Mac 上运行游戏 Mac运行exe程序和游戏 CrossOver虚拟机 crossover运行免安装游戏包-程序员宅基地

文章浏览阅读563次,点赞13次,收藏6次。CrossOver24是一款类虚拟机软件,专为macOS和Linux用户设计。它的核心技术是Wine,这是一种在Linux和macOS等非Windows操作系统上运行Windows应用程序的开源软件。通过CrossOver24,用户可以在不购买Windows授权或使用传统虚拟机的情况下,直接在Mac或Linux系统上运行Windows软件和游戏。该软件还提供了丰富的功能,如自动配置、无缝集成和实时传输等,以实现高效的跨平台操作体验。

随便推点

一个用聊天的方式让ChatGPT写的线程安全的环形List_为什么gpt一写list就卡-程序员宅基地

文章浏览阅读1.7k次。一个用聊天的方式让ChatGPT帮我写的线程安全的环形List_为什么gpt一写list就卡

Tomcat自带的设置编码Filter-程序员宅基地

文章浏览阅读336次。我们在前面的文章里曾写过Web应用中乱码产生的原因和处理方式,旧文回顾:深度揭秘乱码问题背后的原因及解决方式其中我们提到可以通过Filter的方式来设置请求和响应的encoding,来解..._filterconfig selectencoding

javascript中encodeURI和decodeURI方法使用介绍_js encodeur decodeurl-程序员宅基地

文章浏览阅读651次。转自:http://www.jb51.net/article/36480.htmencodeURI和decodeURI是成对来使用的,因为浏览器的地址栏有中文字符的话,可以会出现不可预期的错误,所以可以encodeURI把非英文字符转化为英文编码,decodeURI可以用来把字符还原回来_js encodeur decodeurl

Android开发——打包apk遇到The destination folder does not exist or is not writeable-程序员宅基地

文章浏览阅读1.9w次,点赞6次,收藏3次。前言在日常的Android开发当中,我们肯定要打包apk。但是今天我打包的时候遇到一个很奇怪的问题Android The destination folder does not exist or is not writeable,大意是目标文件夹不存在或不可写。出现问题的原因以及解决办法上面有说报错的中文大意是:目标文件夹不存在或不可写。其实问题就在我们的打包界面当中图中标红的Desti..._the destination folder does not exist or is not writeable

Eclipse配置高大上环境-程序员宅基地

文章浏览阅读94次。一、配置代码编辑区的样式 <1>打开Eclipse,Help —> Install NewSoftware,界面如下: <2>点击add...,按下图所示操作: name:随意填写,Location:http://eclipse-color-th..._ecplise高大上设置

Linux安装MySQL-5.6.24-1.linux_glibc2.5.x86_64.rpm-bundle.tar_linux mysql 安装 mysql-5.6.24-1.linux_glibc2.5.x86_6-程序员宅基地

文章浏览阅读2.8k次。一,下载mysql:http://dev.mysql.com/downloads/mysql/; 打开页面之后,在Select Platform:下选择linux Generic,如果没有出现Linux的选项,请换一个浏览器试试。我用的谷歌版本不可以,换一个别的浏览器就行了,如果还是不行,需要换一个翻墙的浏览器。 二,下载完后解压缩并放到安装文件夹下: 1、MySQL-client-5.6.2_linux mysql 安装 mysql-5.6.24-1.linux_glibc2.5.x86_64.rpm-bundle