我希望有人可以帮助我从 Python 对 Excel 进行编码的跨进程调用。
我有一个通过 Python 启动的 Excel session ,我知道它会在需要从单独的 Python 进程访问时启动并运行。我已经通过使用来自 pythoncom 模块的 CoMarshalInterfaceInStream()
和 CoGetInterfaceAndReleaseStream()
调用的编码使一切正常工作,但我需要重复访问流(在我的情况下我只能设置一次),并且 CoGetInterfaceAndReleaseStream()
只允许一次访问接口(interface).
我相信我想要实现的目标可以用 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)
调用
pythoncom.CoMarshalInterface()
时出现此错误(我认为与第三个参数有关):"ValueError: argument is not a COM object (got type=instance)"
有谁知道我如何让这个简单的例子工作?
提前致谢
最佳答案
经过多次焦虑,我设法解决了我面临的问题,实际上我也将描述随后的问题。
首先,我猜测我最初的问题是在调用 pythoncom.CoMarshalInterface() 时的第三个参数是正确的。事实上,我应该引用我的 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()
我希望这可以帮助其他人克服我面临的障碍!