Python自动化之win32利器pywin32

文章目录

  • 前言
  • 一、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()

Python自动化之win32利器pywin32_第1张图片

二、文件、目录

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)

参考

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

你可能感兴趣的:(python,github,python,自动化,pywin32,win32,win32gui,win32api,win32file)