今天认真尝试了一下在python上调用.NET库的实现,多谢gpt时代,学习成本很低,问了几轮gpt,终于打通了。
可喜的是,在影刀上,也是可以无障碍使用的。
为什么要有这个需求?
因为每种语言都有它的优点擅长的东西,python语言很优秀,爬虫玩得飞起。但是论到桌面自动化,还是.NET语言老道,毕竟桌面自动化几乎等同于windows自动化,.NET是微软亲儿子,能不好吗?.NET社区,过往可是桌面开发工具的标配,包括影刀windows版本也是.NET技术开发的。
当下我想到有价值的应用点是:
桌面UI自动化
.NET里的flaui库,据我的认知,应该比pywinauto要强,可以有xpath使用,还有很多特性,文档也全,也容易学习。
Excel的操作
.NET里有一个不依赖于COM组件的实现轮子叫EPPLUS,对标的是python的openpyxl。这个轮子虽说是新版要商业授权,但自己内部使用,应该很安全的,旧版的功能也足够丰富。新版功能很屌炸,实现了全套OFFICE的公式引擎,连最新的动态数组公式溢出都有。影刀在使用openpyxl里,是拿不到公式计算的结果的,但在EPPLUS是可以的,并且在内存里新写的公式一样能计算。还有很多很多,甩openpyxl几条街。
在.NET里,调用OFFICE的COM接口自动化操作,也是最完整的,和VBA接口一致,比起python的wincom32、xlwings要强大完整得多。
熟悉.NET语言的程序员群体
曾经的我,是不想用影刀的,因为python不是我的主要语言,我更倾向于uibot、PAD、uipath这些类VB和VB语言,对我更熟悉。但gpt时代,代码已经不是障碍,也因影刀实在是优秀,所以全面拥抱影刀了,相信能够打通.NET的轮子库,很多熟悉.NET语言的群体,也会更喜欢用影刀,将.NET库的实现改造成python,gpt很能胜任,哈哈。
最后,附上源代码,供爱好者尝试下:
能够实现这种能力的跨越,只在python上需要安装一个pythonnet的包就可以。
# 使用提醒:
# 1. xbot包提供软件自动化、数据表格、Excel、日志、AI等功能
# 2. package包提供访问当前应用数据的功能,如获取元素、访问全局变量、获取资源文件等功能
# 3. 当此模块作为流程独立运行时执行main函数
# 4. 可视化流程中可以通过"调用模块"的指令使用此模块
import xbot
from xbot import print, sleep
from .import package
from .package import variables as glv
import clr
from pathlib import Path
import sys
def main(args):
app_path=Path(__file__).parent
dll_dir=app_path / 'resources'
for file in dll_dir.iterdir():
clr.AddReference(str(file))
# 导入 .NET 命名空间
from FlaUI.UIA3 import UIA3Automation, UIA3PropertyLibrary
from FlaUI.Core.Conditions import ConditionFactory
from FlaUI.Core.Definitions import WindowVisualState
# 使用 UIA3Automation 获取桌面元素
automation = UIA3Automation()
# 获取桌面作为根元素
desktop = automation.GetDesktop()
# 创建 ConditionFactory 实例
property_library = UIA3PropertyLibrary()
condition_factory = ConditionFactory(property_library)
# 查找微信窗口
wd_wechat = desktop.FindFirstChild(condition_factory.ByClassName("WeChatMainWndForPC"))
if wd_wechat is None:
print("未能找到微信窗口")
sys.exit()
if wd_wechat.Patterns.Window.Pattern.WindowVisualState == WindowVisualState.Minimized:
wd_wechat.Patterns.Window.Pattern.SetWindowVisualState(WindowVisualState.Normal)
# 查找朋友圈按钮
btn_friend = wd_wechat.FindFirstByXPath("//Button[@Name='朋友圈']")
if btn_friend is None:
print("未能找到朋友圈按钮")
sys.exit()
# 点击朋友圈按钮
btn_friend.Click()
print("成功点击朋友圈按钮")
# 保持控制台窗口打开
input("按任意键退出...")
使用COM组件调用Excel,有些麻烦,但能用是最关键,背后写代码辛苦点是小事,没有某个接口,实现不出来,那才要命。
import clr
import sys
from datetime import datetime
# 添加对 COM 库的引用
clr.AddReference('System.Runtime.InteropServices')
# 导入必要的命名空间
from System import Type, Activator
from System.Runtime.InteropServices import Marshal, COMException
from System.Reflection import BindingFlags
# 添加对 Excel COM 对象的引用
clr.AddReference(r'C:\Program Files (x86)\Microsoft Visual Studio\Shared\Visual Studio Tools for Office\PIA\Office15\Microsoft.Office.Interop.Excel.dll')
# 导入 Excel 命名空间
import Microsoft.Office.Interop.Excel as Excel
# 尝试获取当前正在运行的 Excel 实例
try:
excel_app = Marshal.GetActiveObject('Excel.Application')
workbook = excel_app.GetType().InvokeMember("ActiveWorkbook", BindingFlags.GetProperty, None, excel_app, None)
except COMException:
excel_app_type = Type.GetTypeFromProgID("Excel.Application")
excel_app = Activator.CreateInstance(excel_app_type)
workbooks = excel_app.GetType().InvokeMember("Workbooks", BindingFlags.GetProperty, None, excel_app, None)
workbook = workbooks.GetType().InvokeMember("Add", BindingFlags.InvokeMethod, None, workbooks, None)
# 确认获取到 Excel 应用实例
if excel_app:
# 设置 Excel 可见
excel_app.GetType().InvokeMember("Visible", BindingFlags.SetProperty, None, excel_app, [True])
# 获取 Excel 版本
excel_version = excel_app.GetType().InvokeMember("Version", BindingFlags.GetProperty, None, excel_app, None)
print(f"Excel版本: {excel_version}")
# 确认获取到工作簿
if workbook:
# 获取活动工作表
worksheet = workbook.GetType().InvokeMember("ActiveSheet", BindingFlags.GetProperty, None, workbook, None)
# 确认获取到工作表
if worksheet:
# 写入数据到单元格
worksheet.GetType().InvokeMember("Cells", BindingFlags.SetProperty, None, worksheet, [1, 1, "Hello, Excel!"])
worksheet.GetType().InvokeMember("Cells", BindingFlags.SetProperty, None, worksheet, [2, 1, datetime.now().strftime("%Y-%m-%d %H:%M:%S")])
# 保存工作簿
# workbook.GetType().InvokeMember("Save", BindingFlags.InvokeMethod, None, workbook, None)
print("数据已写入并保存到活动工作簿。")
else:
print("没有找到活动工作表。")
else:
print("没有找到活动工作簿。")
input("按任意键退出...")