首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Pythoncom -将同一个COM对象传递给多个线程

Pythoncom -将同一个COM对象传递给多个线程
EN

Stack Overflow用户
提问于 2022-03-25 14:23:58
回答 1查看 293关注 0票数 4

你好:)我是一个完全的初学者,当涉及到COM对象,任何帮助都是非常感谢的!

我正在编写一个Python程序,该程序应该以客户机/服务器的方式读取传入的MS-Word文档,即客户端发送请求(一个或多个MS-Word文档),服务器使用pythoncom和win32com从这些请求中读取特定内容。

因为我希望尽量减少客户机的等待时间(客户端需要来自服务器的状态消息),所以我不想为每个请求打开一个MS-Word实例。因此,我打算有一个运行MS-Word实例池,服务器可以从中选择。反过来,这意味着我必须在不同的线程中重用池中的这些实例,这就是现在造成麻烦的原因。在阅读结合多线程使用win32com之后,服务器的虚拟代码如下所示:

代码语言:javascript
运行
复制
import pythoncom, win32com.client, threading, psutil, os, queue, time, datetime

appPool = {'WINWORD.EXE': queue.Queue()}

def initAppPool():
    global appPool
    wordApp = win32com.client.DispatchEx('Word.Application')
    appPool["WINWORD.EXE"].put(wordApp) # For testing purpose I only use one MS-Word instance currently

def run_in_thread(appid, path):
    #open doc, read do some stuff, close it and reattach MS-Word instance to pool
    pythoncom.CoInitialize()
    wordApp = win32com.client.Dispatch(pythoncom.CoGetInterfaceAndReleaseStream(appid, pythoncom.IID_IDispatch))
    doc = wordApp.Documents.Open(path)
    time.sleep(3) # read out some content ...
    doc.Close()
    appPool["WINWORD.EXE"].put(wordApp)

if __name__ == '__main__':
    initAppPool()

    pathOfFile2BeRead1 = r'C:\Temp\file4.docx'
    pathOfFile2BeRead2 = r'C:\Temp\file5.doc'

    #treat first request
    wordApp = appPool["WINWORD.EXE"].get(True, 10) 
    pythoncom.CoInitialize()
    wordApp_id = pythoncom.CoMarshalInterThreadInterfaceInStream(pythoncom.IID_IDispatch, wordApp) 
    readDocjob1 = threading.Thread(target=run_in_thread,args=(wordApp_id,pathOfFile2BeRead1), daemon=True)
    readDocjob1.start() 

    #wait here until readDocjob1 is done 
    wait = True
    while wait:
        try:
            wordApp = appPool["WINWORD.EXE"].get(True, 1)
            wait = False
        except queue.Empty:
            print(f"[{datetime.datetime.now()}] error: appPool empty")
        except BaseException as err:
            print(f"[{datetime.datetime.now()}] error: {err}")

到目前为止,一切都按预期工作,但当我启动与第一个请求类似的第二个请求时:

代码语言:javascript
运行
复制
(x) wordApp_id = pythoncom.CoMarshalInterThreadInterfaceInStream(pythoncom.IID_IDispatch, wordApp)
    readDocjob2 = threading.Thread(target=run_in_thread,args=(wordApp_id,pathOfFile2BeRead2), daemon=True)
    readDocjob2.start()

我收到以下错误消息:(x)标记行的“应用程序称为为不同线程封送的接口”。

我想这就是为什么我必须使用pythoncom.CoGetInterfaceAndReleaseStream在具有相同COM对象的线程之间跳转?除此之外,为什么它第一次起作用,而不是第二次?

我在StackOverflow上寻找不同的解决方案,它们使用CoMarshalInterface而不是CoMarshalInterThreadInterfaceInStream,但它们都给了我相同的错误。我现在真的很困惑。

编辑:修复了注释中提到的错误后,我遇到了一个神秘的行为。执行第二个作业时:

代码语言:javascript
运行
复制
    wordApp_id = pythoncom.CoMarshalInterThreadInterfaceInStream(pythoncom.IID_IDispatch, wordApp)
    readDocjob2 = threading.Thread(target=run_in_thread,args=(wordApp_id,pathOfFile2BeRead2), daemon=True)
    readDocjob2.start()

函数run_in_thread在没有执行任何行的情况下立即终止,似乎pythoncom.CoInitialize()没有正常工作。但是,脚本在没有任何错误消息的情况下完成。

代码语言:javascript
运行
复制
def run_in_thread(instance,appid, path):
    #open doc, read do some stuff, close it and reattach MS-Word instance to pool
    pythoncom.CoInitialize()
    wordApp = win32com.client.Dispatch(pythoncom.CoGetInterfaceAndReleaseStream(appid, pythoncom.IID_IDispatch))
    doc = wordApp.Documents.Open(path)
    time.sleep(3) # read out some content ...
    doc.Close()
    instance.flag = True
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-03-29 07:59:11

结果是您将从activePool获得的COM引用重新放入“CoGetInterfaceAndReleaseStream”中。但是这个引用是专门为这个新线程创建的,然后在这个新引用上调用CoMarshalInterThreadInterfaceInStream

这就是问题所在。

您必须始终使用从创建的线程中获得的原始 COM引用,以便能够重复调用CoMarshalInterThreadInterfaceInStream

因此,要解决这个问题,您必须改变应用程序的工作方式,使用某种“使用中”标志,但不要触及原始的COM引用。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71618529

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档