文章目录
- 前言
- 一、GUI
- 1.1 获取、关闭窗口
- 1.2 窗口截图
- 1.3 创建窗口
- 二、文件、目录
- 2.1 查找
- 2.2 创建
- 2.3 复制/移动
- 2.4 删除
- 2.5 读取/写入
- 三、服务
- 3.1 查找
- 3.2 安装
- 四、案例
- 4.1 自动发送微信消息
- 4.2 Excel 操作
- 4.3 监控文件夹
- 参考
前言
PyWin32 是一个Python库,用于在Python脚本中访问Windows API。它提供了很多模块,允许开发者使用Python代码来操作Windows操作系统。
-
github地址:https://github.com/mhammond/pywin32
-
安装方法
pip install pywin32
-
在线文档:
- https://mhammond.github.io/pywin32/
- http://timgolden.me.uk/pywin32-docs/contents.html
- https://learn.microsoft.com/zh-cn/windows/win32/api/
-
离线文档:
离线文档在Python安装路径下有Lib\site-packages\PyWin32.chm 可以查找具体使用
常用模块如下:
模块名 | 作用 | 备注 |
---|---|---|
win32api | 提供许多与Windows系统进行交互的函数和常量 | 一些难以分类的api被放在这个模块 |
win32com | 提供COM对象的创建和使用方法,可以与其他应用程序(Word, Excel等)进行交互 | Win32中的COM对象是指遵循COM(Component Object Model)规范编写的可执行二进制代码,它以dll或exe的形式发布 |
win32con | 定义了所有的常量,是其他模块的基础 | |
win32event | 提供管理事件的函数和常量,可以创建、等待和释放事件。 | |
win32file | 提供文件操作的函数和常量,可以进行文件的读写、删除等操作 | |
win32gui | 提供创建图形用户界面的函数和常量,可以创建窗口、按钮等控件 | |
win32inet | 提供与Internet协议相关的函数和常量 | 支持HTTP,FTP,Socket等网络协议操作 |
win32process | 提供与进程和线程相关的函数和常量,可以创建、打开和结束进程 | |
win32registry | 提供注册表的操作方法,可以读取和修改Windows注册表中的数据 | |
win32security | 提供Windows安全相关的函数和常量,比如权限管理、进程权限等 | |
win32system | 提供系统相关的函数和常量,比如获取系统信息、关机等操作 |
一、GUI
1.1 获取、关闭窗口
使用 EnumWindows 可以获取所有窗口的所有控件句柄,PostMessage可以关闭窗口
import win32api
import win32con
import win32gui
import win32ui
import win32process
from PIL import Image
from collections import Counter
def _myCallback(hwnd, extra):
hwnds, classes = extra
if win32gui.IsWindowVisible(hwnd):
print(hex(hwnd), win32gui.GetWindowText(hwnd), win32process.GetWindowThreadProcessId(hwnd))
hwnds.append(hwnd)
classes[win32gui.GetClassName(hwnd)] += 1
# 关闭画图
if '画图' in win32gui.GetWindowText(hwnd):
win32gui.PostMessage(hwnd, win32con.WM_CLOSE, 0, 0)
def testEnumWindows():
windows = []
classes = Counter()
win32gui.EnumWindows(_myCallback, (windows, classes))
print("窗口个数: %d" % len(windows))
print(classes)
1.2 窗口截图
# 对显示窗口进行截图,注意窗口不能最小化
def windowScreenshot(windowName, windowsClass = None):
# 根据窗口类或名字(默认)获取句柄
# 窗口的类名可以用Visual Studio的SPY++工具获取
hWnd = win32gui.FindWindow(windowsClass, windowName)
# 获取句柄窗口的大小信息
left, top, right, bot = win32gui.GetWindowRect(hWnd)
width = right - left
height = bot - top
# print(width, height, left, top)
# 返回句柄窗口的设备环境,覆盖整个窗口,包括非客户区,标题栏,菜单,边框
hWndDC = win32gui.GetWindowDC(hWnd)
# 创建设备描述表
mfcDC = win32ui.CreateDCFromHandle(hWndDC)
# 创建内存设备描述表
saveDC = mfcDC.CreateCompatibleDC()
# 创建位图对象准备保存图片
saveBitMap = win32ui.CreateBitmap()
# 为bitmap开辟存储空间
saveBitMap.CreateCompatibleBitmap(mfcDC, width, height)
# 将截图保存到saveBitMap中
saveDC.SelectObject(saveBitMap)
# 保存bitmap到内存设备描述表
saveDC.BitBlt((0, 0), (width, height), mfcDC, (0, 0), win32con.SRCCOPY)
bmpinfo = saveBitMap.GetInfo()
bmpstr = saveBitMap.GetBitmapBits(True)
###生成图像
im = Image.frombuffer('RGB',(bmpinfo['bmWidth'],bmpinfo['bmHeight']),bmpstr,'raw','BGRX')
# im.save('./%s.jpg' % windowName)
win32gui.DeleteObject(saveBitMap.GetHandle())
saveDC.DeleteDC()
mfcDC.DeleteDC()
win32gui.ReleaseDC(hWnd, hWndDC)
return im
1.3 创建窗口
创建最简单窗口
import win32api
import win32con
import win32gui
# 定义窗口类名和窗口标题
className = "MyWindowClass"
windowTitle = "标题Demo"
# 窗口回调函数
def wndProc(hWnd, message, wParam, lParam):
if message == win32con.WM_DESTROY:
win32api.PostQuitMessage(0)
return 0
else:
return win32gui.DefWindowProc(hWnd, message, wParam, lParam)
# 注册窗口类
wc = win32gui.WNDCLASS()
wc.lpfnWndProc = wndProc
wc.lpszClassName = className
wc.hInstance = win32api.GetModuleHandle(None)
classAtom = win32gui.RegisterClass(wc)
# 创建窗口
style = win32con.WS_OVERLAPPEDWINDOW
hwnd = win32gui.CreateWindow(className, windowTitle, style,
100, 100, 400, 300,
None, None, wc.hInstance, None)
# 显示窗口
win32gui.ShowWindow(hwnd, win32con.SW_SHOW)
win32gui.UpdateWindow(hwnd)
win32gui.PumpMessages()
二、文件、目录
2.1 查找
查找目录下所有文件目录名
import win32api
import win32con
import win32gui
import win32ui
import win32file
import os
dir = os.path.join(os.getcwd(), "*")
files = win32file.FindFilesW(dir)
fileNames = [i[8] for i in files]
print(fileNames)
2.2 创建
filename = '中文.txt'
file = win32file.CreateFile(
filename, win32file.GENERIC_WRITE | win32file.GENERIC_READ, 0, None, win32con.OPEN_ALWAYS, 0, None
)
directoryName = '中文目录'
win32file.CreateDirectory(directoryName, None)
2.3 复制/移动
没有发现有复制目录的api
win32api.CopyFile('1.py', 'test/1.py')
win32api.MoveFile('1.py', 'D:/1.py')
win32api.MoveFile('resource', 'test/resource')
2.4 删除
win32file.DeleteFile('D:/1.py')
win32file.RemoveDirectory('中文', None)
2.5 读取/写入
filename = '中文.txt'
f = win32file.CreateFile(
filename,
win32file.GENERIC_READ | win32file.GENERIC_WRITE,
0,
None,
win32file.CREATE_ALWAYS,
win32file.FILE_ATTRIBUTE_NORMAL,
0,
)
try:
data = "中文测试数据".encode('utf-8')
(res, written) = win32file.WriteFile(f, data)
print(res, written)
win32file.SetFilePointer(f, 0, win32file.FILE_BEGIN)
(res, s) = win32file.ReadFile(f, len(data))
print(res, s.decode('utf-8'))
finally:
f.Close()
三、服务
3.1 查找
import win32con
import win32service
def EnumServices():
resume = 0
accessSCM = win32con.GENERIC_READ
accessSrv = win32service.SC_MANAGER_ALL_ACCESS
# Open Service Control Manager
hscm = win32service.OpenSCManager(None, None, accessSCM)
# Enumerate Service Control Manager DB
typeFilter = win32service.SERVICE_WIN32
stateFilter = win32service.SERVICE_STATE_ALL
statuses = win32service.EnumServicesStatus(hscm, typeFilter, stateFilter)
for short_name, desc, status in statuses:
print(short_name, desc, status)
EnumServices()
3.2 安装
import win32serviceutil
import win32service
import win32event
import win32api
class MyService(win32serviceutil.ServiceFramework):
_svc_name_ = "MyService"
_svc_display_name_ = "My Service"
def __init__(self, args):
win32serviceutil.ServiceFramework.__init__(self, args)
self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
self.interval = 60 # 60 seconds
def SvcDoRun(self):
self.ReportServiceStatus(win32service.SERVICE_RUNNING)
while True:
self.do_something()
if win32event.WaitForSingleObject(self.hWaitStop, self.interval * 1000) == win32event.WAIT_OBJECT_0:
break
def do_something(self):
print("do something...")
# do your jobs here
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
win32event.SetEvent(self.hWaitStop)
if __name__ == '__main__':
win32serviceutil.HandleCommandLine(MyService)
四、案例
4.1 自动发送微信消息
import win32clipboard as w
import win32con
import win32api
import win32gui
import os
# 把文字放入剪贴板
def setText(aString):
w.OpenClipboard()
w.EmptyClipboard()
w.SetClipboardData(win32con.CF_UNICODETEXT,aString)
w.CloseClipboard()
# 模拟ctrl+V
def ctrlV():
win32api.keybd_event(win32con.VK_CONTROL,0,0,0) #按下ctrl
win32api.keybd_event(ord('V'),0,0,0) #按下V
win32api.keybd_event(ord('V'),0,win32con.KEYEVENTF_KEYUP,0)#释放V
win32api.keybd_event(win32con.VK_CONTROL,0,win32con.KEYEVENTF_KEYUP,0)#释放ctrl
# 模拟alt+s
def altS():
win32api.keybd_event(win32con.VK_MENU,0,0,0)
win32api.keybd_event(ord('S'),0,0,0)
win32api.keybd_event(ord('S'),0,win32con.KEYEVENTF_KEYUP,0)
win32api.keybd_event(win32con.VK_MENU,0,win32con.KEYEVENTF_KEYUP,0)
# 模拟enter
def enter():
win32api.keybd_event(win32con.VK_RETURN,0,0,0)
win32api.keybd_event(win32con.VK_RETURN,0,win32con.KEYEVENTF_KEYUP,0)
# 模拟鼠标单击
def click():
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN,0,0,0,0)
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP,0,0,0,0)
# 移动鼠标的位置
def movePos(pos):
win32api.SetCursorPos(pos)
if __name__=="__main__":
name_list=['文件传输助手'] # 这里是要发送信息的联系人
send_content="测试中文" # 这里是需要发送的信息内容
addressIconPos = (28,147) # 通讯录按钮坐标
inputPos = (148,35) # 输入框坐标
hwnd=win32gui.FindWindow("WeChatMainWndForPC", '微信') # 返回微信窗口的句柄信息
print(hwnd)
if hwnd is None:
print('没找到微信窗口')
sys.exit()
win32gui.SetForegroundWindow(hwnd) # 设置为当前活动窗口
win32gui.ShowWindow(hwnd, win32con.SW_RESTORE) # 恢复最小化的窗口
win32gui.MoveWindow(hwnd,0,0,1000,700,True) # 将微信窗口移动到指定位置和大小
win32gui.SetWindowPos(hwnd, win32con.HWND_TOPMOST, 0, 0,1000,700, win32con.SWP_NOMOVE | win32con.SWP_NOSIZE)
time.sleep(1)
for name in name_list:
movePos(addressIconPos)
click()
movePos(inputPos)
click()
time.sleep(1)
setText(name)
ctrlV()
time.sleep(1) # 等待联系人搜索成功
enter()
time.sleep(1)
setText(send_content)
ctrlV()
time.sleep(1)
altS()
time.sleep(1)
# win32gui.PostMessage(hwnd, win32con.WM_CLOSE, 0, 0)
- 虚拟按键文档:https://learn.microsoft.com/zh-cn/windows/win32/inputdev/virtual-key-codes
4.2 Excel 操作
import win32com.client
# 创建Excel COM对象
excel = win32com.client.Dispatch("Excel.Application")
# 显示Excel应用程序
excel.Visible = True
# 打开Excel文件
workbook = excel.Workbooks.Open("file.xlsx")
# 选择工作表
worksheet = workbook.Worksheets("Sheet1")
# 读取单元格内容
cell_value = worksheet.Range("A1").Value
# 修改单元格内容
worksheet.Range("A1").Value = "Hello, World!"
# 保存并关闭Excel文件
workbook.Save()
workbook.Close()
4.3 监控文件夹
import win32file
import win32con
import win32api
import threading
import datetime
import time
# https://learn.microsoft.com/zh-cn/windows/win32/api/winnt/ns-winnt-file_notify_information
ACTIONS = {
1: "该文件已添加到目录中",
2: "该文件已从目录中删除",
3: "该文件已修改", # 这可以是时间戳或属性中的更改。
4: "该文件已重命名,这是旧名称",
5: "该文件已重命名,这是新名称"
}
logfile = 'fileLog'
def win32watcherThread(abspath, file_lock):
dirHandle = win32file.CreateFile (
abspath,
ntsecuritycon.FILE_LIST_DIRECTORY,
win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE,
None,
win32con.OPEN_EXISTING,
win32con.FILE_FLAG_BACKUP_SEMANTICS | win32con.FILE_FLAG_OVERLAPPED,
None
)
while True:
results = win32file.ReadDirectoryChangesW (
dirHandle,
1024,
True,
win32con.FILE_NOTIFY_CHANGE_FILE_NAME |
win32con.FILE_NOTIFY_CHANGE_DIR_NAME |
win32con.FILE_NOTIFY_CHANGE_ATTRIBUTES |
win32con.FILE_NOTIFY_CHANGE_SIZE |
win32con.FILE_NOTIFY_CHANGE_LAST_WRITE |
win32con.FILE_NOTIFY_CHANGE_SECURITY,
None,
None
)
for action, file in results:
fullFilename = win32api.GetFullPathName(file)
# fullPath = win32api.GetFullPathName(abspath)
file_lock.acquire()
log(abspath, fullFilename, ACTIONS.get(action, "Unknown"))
file_lock.release()
def log(abspath, fullFileName, action):
try:
f = open(logfile, 'a')
f.write(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S ') + abspath + ' ' + fullFileName+'->' + action+'\n')
f.close()
except IOError:
print('failed to open log file %s for writing', logfile)
def win32watcher(directorys):
file_lock = threading.Lock()
threads = [ threading.Thread(target=win32watcherThread,args=(abspath,file_lock,)) for abspath in directorys ]
for thread in threads:
thread.setDaemon(True)
thread.start()
try:
while 1:
time.sleep(3600)
except KeyboardInterrupt:
print("Cleaning up.")
directorys = ['test001', 'test002']
win32watcher(directorys)
参考
- https://mhammond.github.io/pywin32/html/com/win32com/HTML/QuickStartClientCom.html
- Watch a Directory for Changes
- 如何利用 python 向微信群里定时发送消息?