Python自动化之win32利器pywin32

前言

PyWin32 是一个Python库,用于在Python脚本中访问Windows API。它提供了很多模块,允许开发者使用Python代码来操作Windows操作系统。

常用模块如下:

模块名作用备注
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)

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)

参考

  1. https://mhammond.github.io/pywin32/html/com/win32com/HTML/QuickStartClientCom.html
  2. Watch a Directory for Changes
  3. 如何利用 python 向微信群里定时发送消息?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

aabond

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值