Pythoncom - 将相同的 COM 对象传递给多个线程
Pythoncom - Passing same COM object to multiple threads
你好 :) 在 COM 对象方面,我是一个完全的初学者,感谢任何帮助!
我正在开发一个 Python 程序,该程序应该以 client/server 方式读取传入的 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 对象的线程之间跳转?还有,为什么第一次可以,第二次不行?
我在 Whosebug 上搜索了使用 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
如果您将从 CoGetInterfaceAndReleaseStream. But this reference was created specially for this new thread and then you call CoMarshalInterThreadInterfaceInStream 获得的 COM 引用放回到“activePool”中,就会发生这种情况。
这是错误的。
您必须始终使用从创建它的线程获得的原始 COM 引用,以便能够重复调用CoMarshalInterThreadInterfaceInStream
。
因此,要解决此问题,您必须更改应用程序池的工作方式,使用某种“正在使用”标志,但不要触及原始 COM 引用。
你好 :) 在 COM 对象方面,我是一个完全的初学者,感谢任何帮助!
我正在开发一个 Python 程序,该程序应该以 client/server 方式读取传入的 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 对象的线程之间跳转?还有,为什么第一次可以,第二次不行?
我在 Whosebug 上搜索了使用 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
如果您将从 CoGetInterfaceAndReleaseStream. But this reference was created specially for this new thread and then you call CoMarshalInterThreadInterfaceInStream 获得的 COM 引用放回到“activePool”中,就会发生这种情况。
这是错误的。
您必须始终使用从创建它的线程获得的原始 COM 引用,以便能够重复调用CoMarshalInterThreadInterfaceInStream
。
因此,要解决此问题,您必须更改应用程序池的工作方式,使用某种“正在使用”标志,但不要触及原始 COM 引用。