使用pythoncom在Python进程之间编组COM对象

时间:2014-04-04 13:03:01

标签: python excel com marshalling pythoncom

我希望有人可以帮助我能够通过Python对Excel进行封送的跨进程调用。

我有一个通过Python启动的Excel会话,我知道它将在需要从单独的Python进程访问时启动并运行。我使用来自pythoncom模块的CoMarshalInterfaceInStream()CoGetInterfaceAndReleaseStream()调用来完成所有工作,但我需要重复访问流(我只能在我的情况下设置一次),{ {1}}允许只访问一次界面。

我相信我想要实现的目标可以通过CoGetInterfaceAndReleaseStream()CreateStreamOnHGlobal()CoMarshalInterface()来完成,但我无法让它发挥作用,几乎可以肯定,因为我没有通过正确的参数。

我没有详细描述我的主要场景,而是按照以下方式设置了一个简单的示例程序 - 显然这是在同一个过程中进行的,但一次只进行一步!以下代码段工作正常:

CoUnmarshalInterface()

然而,当我尝试以下内容时:

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"

我在调用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) 时收到此错误(我想这与第3个参数有关):

pythoncom.CoMarshalInterface()

有谁知道如何让这个简单的例子有效?

提前致谢

1 个答案:

答案 0 :(得分:2)

经过深深的焦虑,我已经设法解决了我所面临的问题,以及随后我将要描述的问题。

首先,我猜测我的初始问题是调用pythoncom.CoMarshalInterface()时的第3个参数是正确的。实际上,我应该引用我的excelApp变量的 oleobj 属性:

pythoncom.CoMarshalInterface(myStream, 
                             pythoncom.IID_IDispatch, 
                             excelApp._oleobj_, 
                             pythoncom.MSHCTX_LOCAL, 
                             pythoncom.MSHLFLAGS_TABLESTRONG)

然而,我接着遇到了一个不同的错误信息,这次是在pythoncom.CoUnmarshalInterface()的调用中:

com_error: (-2147287010, 'A disk error occurred during a read operation.', None, None)

事实证明,这是因为在使用之前需要使用Seek()方法重置流指针:

myStream.Seek(0,0) 

最后,虽然大多数方面都正常工作,但我发现尽管在编组的Excel对象上使用了Quit()并在代码结束之前将所有变量显式设置为None,但我还是留下了一个僵尸Excel进程。尽管事实上pythoncom._GetInterfaceCount()返回0

事实证明,我必须通过调用CoReleaseMarshalData()显式清空我创建的流(首先重新重置流指针)。因此,整个示例代码段如下所示:

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()

我希望这有助于其他人克服我遇到的障碍!