使用pythoncom在Python进程之间封送COM对象 [英] Marshaling COM objects between Python processes using pythoncom
问题描述
我希望有人能够帮助我从Python向Excel进行复杂的跨进程调用。
I'm hoping someone can help me with being able to make a marshaled cross-process call into Excel from Python.
我有一个通过Python启动的Excel会话我知道当需要从单独的Python进程访问它时,它将启动并运行。使用pythoncom模块中的 CoMarshalInterfaceInStream()
和 CoGetInterfaceAndReleaseStream()
进行封送处理,我已经可以按需工作我需要重复访问该流(在我的情况下只能设置一次),并且 CoGetInterfaceAndReleaseStream()
允许仅一次访问该接口。
I have an Excel session initiated via Python that I know will be up and running when it needs to be accessed from a separate Python process. I have got everything working as desired using marshaling with CoMarshalInterfaceInStream()
and CoGetInterfaceAndReleaseStream()
calls from the pythoncom module, but I need repeat access to the stream (which I can only set up once in my case), and CoGetInterfaceAndReleaseStream()
allows once-only access to the interface.
我相信我想实现的目标可以使用 CreateStreamOnHGlobal()
, CoMarshalInterface()
和 CoUnmarshalInterface()
,但由于我没有传递正确的参数,因此几乎无法使它正常工作。
I believe that what I would like to achieve can be done with CreateStreamOnHGlobal()
, CoMarshalInterface()
and CoUnmarshalInterface()
but am unable to get it working, almost certainly because I am not passing in the correct parameters.
我没有详细描述我的主要情况,而是建立了一个简单的示例程序,如下所示-显然,这是在相同的过程中进行的,但一次仅一步!以下代码段可以正常工作:
Rather than describe in detail my main scenario, I have set up a simple example program as follows - obviously this takes place in the same process but one step at a time! The following snippet works fine:
import win32com.client
import pythoncom
excelApp = win32com.client.DispatchEx("Excel.Application")
marshalledExcelApp = pythoncom.CoMarshalInterThreadInterfaceInStream(pythoncom.IID_IDispatch, excelApp)
xlApp = win32com.client.Dispatch(
pythoncom.CoGetInterfaceAndReleaseStream(marshalledExcelApp, pythoncom.IID_IDispatch))
xlWb = xlApp.Workbooks.Add()
xlWs = xlWb.Worksheets.Add()
xlWs.Range("A1").Value = "AAA"
但是,当我尝试以下操作时:
However, when I try the following:
import win32com.client
import pythoncom
excelApp = win32com.client.DispatchEx("Excel.Application")
myStream = pythoncom.CreateStreamOnHGlobal()
pythoncom.CoMarshalInterface(myStream,
pythoncom.IID_IDispatch,
excelApp,
pythoncom.MSHCTX_LOCAL,
pythoncom.MSHLFLAGS_TABLESTRONG)
myUnmarshaledInterface = pythoncom.CoUnmarshalInterface(myStream, pythoncom.IID_IDispatch)
调用 pythoncom.CoMarshalInterface()时出现此错误(我想这与第三个参数有关)
:
"ValueError: argument is not a COM object (got type=instance)"
有人知道我如何使这个简单的示例正常工作吗?
Does anyone know how I can get this simple example working?
预先感谢
推荐答案
经过一番焦虑,我设法解决了我所面临的问题,以及后来遇到的问题。我还将描述。
After much angst, I have managed to resolve the issue I was facing, and indeed subsequent ones which I will also describe.
首先,我猜对了我的最初问题是调用pythoncom.CoMarshalInterface()的第三个参数是正确的。实际上,我应该一直引用excelApp变量的 oleobj 属性:
First, I was correct in guessing that my initial problem was with the 3rd parameter in the call to pythoncom.CoMarshalInterface(). In fact, I should have been making a reference to the oleobj property of my excelApp variable:
pythoncom.CoMarshalInterface(myStream,
pythoncom.IID_IDispatch,
excelApp._oleobj_,
pythoncom.MSHCTX_LOCAL,
pythoncom.MSHLFLAGS_TABLESTRONG)
但是,这次我在调用pythoncom.CoUnmarshalInterface()时遇到了另一条错误消息:
However, I then faced a different error message, this time in the call to pythoncom.CoUnmarshalInterface():
com_error: (-2147287010, 'A disk error occurred during a read operation.', None, None)
事实证明,这是由于流指针需要在使用之前通过Seek()方法进行重置的原因:
It turned out that this was due to the fact that the stream pointer needs to be reset prior to use, with the Seek() method:
myStream.Seek(0,0)
最后,尽管大多数方面都可以正常工作,但我发现尽管在在代码末尾之前,如果将Excel对象编组并明确将所有变量设置为None,那么我将面临僵尸Excel进程。尽管事实是pythoncom._GetInterfaceCount()返回0。
Finally, although most aspects were working correctly, I found that despite using Quit() on the marshalled Excel object and explicitly setting all variables to None prior to the end of the code, I was left with a zombie Excel process. This was despite the fact that pythoncom._GetInterfaceCount() was returning 0.
事实证明,我必须显式清空通过调用CoReleaseMarshalData()创建的流)(首先必须重新设置流指针)。因此,整个示例代码段如下所示:
It turns out that I had to explicitly empty the stream I had created with a call to CoReleaseMarshalData() (having first reset the stream pointer again). So, the example code snippet in its entirety looks like this:
import win32com.client
import pythoncom
pythoncom.CoInitialize()
excelApp = win32com.client.DispatchEx("Excel.Application")
myStream = pythoncom.CreateStreamOnHGlobal()
pythoncom.CoMarshalInterface(myStream,
pythoncom.IID_IDispatch,
excelApp._oleobj_,
pythoncom.MSHCTX_LOCAL,
pythoncom.MSHLFLAGS_TABLESTRONG)
excelApp = None
myStream.Seek(0,0)
myUnmarshaledInterface = pythoncom.CoUnmarshalInterface(myStream, pythoncom.IID_IDispatch)
unmarshalledExcelApp = win32com.client.Dispatch(myUnmarshaledInterface)
# Do some stuff in Excel in order to prove that marshalling has worked.
unmarshalledExcelApp.Visible = True
xlWbs = unmarshalledExcelApp.Workbooks
xlWb = xlWbs.Add()
xlWss = xlWb.Worksheets
xlWs = xlWss.Add()
xlRange = xlWs.Range("A1")
xlRange.Value = "AAA"
unmarshalledExcelApp.Quit()
# Clear the stream now that we have finished
myStream.Seek(0,0)
pythoncom.CoReleaseMarshalData(myStream)
xlRange = None
xlWs = None
xlWss = None
xlWb = None
xlWbs = None
myUnmarshaledInterface = None
unmarshalledExcelApp = None
myStream = None
pythoncom.CoUninitialize()
我希望这可以帮助其他人克服我所遇到的障碍!
I hope that this helps someone else out there to overcome the obstacles I faced!
这篇关于使用pythoncom在Python进程之间封送COM对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!