繁体   English   中英

Pythoncom - 将相同的 COM object 传递给多个线程

[英]Pythoncom - Passing same COM object to multiple threads

你好:) 对于 COM 个对象,我是一个完全的初学者,感谢任何帮助!

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

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

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}")
    

到目前为止,一切都按预期工作,但是当我开始第二个请求时,类似于第一个请求:

(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 object 的线程之间跳转? 除此之外,为什么它第一次起作用但第二次不起作用?

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

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

    wordApp_id = pythoncom.CoMarshalInterThreadInterfaceInStream(pythoncom.IID_IDispatch, wordApp)
    readDocjob2 = threading.Thread(target=run_in_thread,args=(wordApp_id,pathOfFile2BeRead2), daemon=True)
    readDocjob2.start()

function run_in_thread 立即终止而不执行任何行,分别似乎 pythoncom.CoInitialize() 无法正常工作。 尽管脚本完成时没有任何错误消息。

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

发生的事情是您将从CoGetInterfaceAndReleaseStream获得的 COM 引用放回“activePool”。 但是这个引用是专门为这个新线程创建的,然后你在这个新引用上调用CoMarshalInterThreadInterfaceInStream

那就是错误的。

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

因此,要解决此问题,您必须更改应用程序池的工作方式,使用某种“正在使用”标志,但不要触及原始 COM 参考。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM