win32com报错:无效的类字符串(Invalid Class String)

异常:无效的类字符串 (Invalid Class String)

使用python操作COM组件的朋友应该都碰到过,这个报错来自于python标准库pythoncomwin32com也是建立在pythoncom基础上的,在我使用win32com一年多的期间,印象中碰到三次这个报错。有时候各种办法都尝试了,也无法解决,最后不得已重装了系统,虽然有点为了抓只老鼠把家给拆了的感觉,但最后也能解决了问题。

好死不死,昨天(2024年1月17日),我又碰到了这个问题,第四次了!,下面是业务场景:

线上测试时,有一台机器报错,同时本地开发环境也报同样的错:无效的类字符串。每台下游机器均在子进程中运行win32com,并不在主进程中。(是不是和进程有关呢?按下不表)。接下来,就逐步分析,彻底把把这只老鼠给揪出来消灭掉。

win32com是如何工作的

弄清楚这个问题,一切就好办了。我们直接以win32com的客户端启动器DispatchDispatchEx来举例,下面是一个简单的示例:

python 复制代码
from win32com.client import Dispatch

client = Dispatch('kwps.application')

无效的类字符串的错误在Dispatch('kwps.application')这一步就会出现。

为何实参kwps.application就能启动wps客户端、word.application能启动ms word客户端,而visio.application能启动Visio呢。简单粗暴地说:使用不同的实参,就能启动不同的客户端。

实参是什么,实际上是由程序在windows注册表中的程序ID(ProgId)决定的,看图:

在注册表中搜索关键词wps就能看到红框中所示的KWPS.Application的程序ID,这是一种身份标识,是唯一的。Dispatch('kwps.application')凭借实参,通过pythoncom来实现在注册表中查找与之匹配的应用程序。同样的,如果你安装了ms office,你通过word也能查到相关的程序ID。注意,Dispatch入参不区分大小写。

跑题:你是否也看到了截图中红框下方有KWPS.Document,那么,是不是也可以Dispatch('kwps.document'),它会启动一个什么呢?我已经试过了,并且瞬间就找到了一个为下游机器节省开销的方法。有好奇心的你不妨也尝试下。

分析

有了上面的理解作为基础,再来分析报错的原因。

情况一

错误的程序ID

当我使用Dispatch('wps.application')时(注意,入参少了个k),就会报错:无效的类字符串,因为COM组件无法通过wps.application匹配到应用程序,正确的应为kwps.application,毫无疑问,入参要求和程序ID完全匹配。如果你报了同样的错误,不妨检查下注册表中的程序ID。

情况二

正常运行的程序,突然不行了

由于某种原因,程序的注册表信息有损坏导致COM组件无法唤起客户端,比如wps,但这种情况下有可能连手动也打不开wps了,一般重装wps能解决问题。

情况三

在IDE中可以运行,但在cmd窗口中就无法执行,在其他电脑上也正常

如图,这是我自己测试的

图一:在PowShell中运行出错

图二:在vscode中运行正常

其实,之前大概率碰到的就是这种情形,莫名其妙就崩了。我将这两张图发到群里时,估计有不少朋友都看出问题所在了:权限问题。

情景还原

截图中涉及到的是上面提到的下游机器之一,不过是在本地的开发环境中,是一套django服务,runserver时,命令行窗口使用了管理员身份,图二是使用vscode控制台直接运行的,图一和图二最终运行的都是同一套win32com服务。它们的唯一的差异,就是权限不同。所以,我有足够的理由推测:由权限引起的。为了验证我的推测,我做了下面的测试:

测试一

写个win32com的最小实现:demo.py

from win32com.client import Dispatch

client = Dispatch('kwps.application')
client.Quit()

1、分别使用管理员身份启动cmdPowerShell,使用python demo.py来运行这段脚本,结果都报错:无效的类字符串

2、使用当前用户身份来启动cmdPowerShell,也就是正常打开命令行工具,使用python demo.py来运行这段脚本,结果正常。

上面提到,我的win32com运行在子进程中,为了保险,进一步测试在多进程中的表现如何,所以看测试二。

测试二

把demo.py变成多进程,如下:

python 复制代码
import pythoncom
from multiprocessing import Process

from win32com.client import DispatchEx

def demo():
    pythoncom.CoInitialize()
    client = DispatchEx('kwps.application')
    print('wps客户端实例:', client)
    client.Quit()
    pythoncom.CoUninitialize()

if __name__ == '__main__':
    p = Process(target=demo)
    p.start()

再次重复测试一的步骤,测试结果一致。不关乎进程,测试结果只和身份有关,管理员身份当前用户身份最大的区别就是权限,意味着,即便是程序ID无误,也无法启动目标客户端。

更为详细的原因,我就不得而知了,超纲了。但已经完全可以确定,这种情况与权限有关,即以何种权限来启动程序,和当前的用户身份是否匹配。如果哪位大佬知晓更为本质的原因,欢迎点拨一二。

回到我的项目中,我一定是手贱了,在本地无意中以管理员身份启动了命令行工具,在部署测试时,手贱在一台下游机器上执行了同样的操作。

由于知识有限,个人的分析并不一定正确,碰到的情况也并不一定和你的相同,请多多多交流。

相关推荐
fuvuof13 分钟前
函数类型注释和Union联合类型注释
python
龙虎榜小红牛系统18 分钟前
Wordcloud也能生成一个,带html的词云图文件吗??
python·html·wordcloud
多彩电脑2 小时前
Python的tkinter如何把日志弄进文本框(Text)
python·用户界面
小馒头学python2 小时前
【Python爬虫五十个小案例】爬取豆瓣电影Top250
开发语言·爬虫·python
black0moonlight8 小时前
ISAAC Gym 7. 使用箭头进行数据可视化
开发语言·python
程序员黄同学8 小时前
Python 中如何创建多行字符串?
前端·python
一点一木9 小时前
AI与数据集:从零基础到全面应用的深度解析(超详细教程)
人工智能·python·tensorflow
A.sir啊9 小时前
Python知识点精汇:集合篇精解!
python·pycharm
周某人姓周9 小时前
利用爬虫爬取网页小说
爬虫·python
花生糖@9 小时前
OpenCV图像基础处理:通道分离与灰度转换
人工智能·python·opencv·计算机视觉