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


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

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

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

    image

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

    win32com是如何工作的

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

    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)决定的,看图:

    image

    在注册表中搜索关键词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中运行出错

    image

    图二:在vscode中运行正常

    image

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

    情景还原

    截图中涉及到的是上面提到的下游机器之一,不过是在本地的开发环境中,是一套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变成多进程,如下:

    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无误,也无法启动目标客户端。

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

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

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

  • 相关阅读:
    【英语:基础高阶_经典外刊阅读】L6.解题真功夫—阅读理解选择题
    从源文件到可执行文件:源文件的预处理、编译、汇编、链接
    Ceph — 架构
    部署SpringBoot+SpringCloud+Vue项目——半途而废版
    基于STM32的智能快递箱(快递驿站)设计
    python中的图像增强技术
    【无标题】
    安卓系统手机便签app使用哪一款?
    java基于springboot+vue的汉服推广与交流平台
    TypeScript学习日志-第二十六天(weakMap,weakSet,set,map)
  • 原文地址:https://www.cnblogs.com/mooremok/p/17973455