你好:)我是一个完全的初学者,当涉及到COM对象,任何帮助都是非常感谢的!
我正在编写一个Python程序,该程序应该以客户机/服务器的方式读取传入的MS-Word文档,即客户端发送请求(一个或多个MS-Word文档),服务器使用pythoncom和win32com从这些请求中读取特定内容。
因为我希望尽量减少客户机的等待时间(客户端需要来自服务器的状态消息),所以我不想为每个请求打开一个MS-Word实例。因此,我打算有一个运行MS-Word实例池,服务器可以从中选择。反过来,这意味着我必须在不同的线程中重用池中的这些实例,这就是现在造成麻烦的原因。在阅读结合多线程使用win32com之后,服务器的虚拟代码如下所示:
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对象的线程之间跳转?除此之外,为什么它第一次起作用,而不是第二次?
我在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()
函数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
发布于 2022-03-29 07:59:11
结果是您将从activePool获得的COM引用重新放入“CoGetInterfaceAndReleaseStream”中。但是这个引用是专门为这个新线程创建的,然后在这个新引用上调用CoMarshalInterThreadInterfaceInStream。
这就是问题所在。
您必须始终使用从创建的线程中获得的原始 COM引用,以便能够重复调用CoMarshalInterThreadInterfaceInStream
。
因此,要解决这个问题,您必须改变应用程序的工作方式,使用某种“使用中”标志,但不要触及原始的COM引用。
https://stackoverflow.com/questions/71618529
复制相似问题