我使用ExcelDNA开发XLL。
在其中,我有一个生活在我传递的DLL中的表单" ExcelDnaUtil.Application"作为成员,以促进表单和运行XLL的Excel实例之间的交互。
如果我使用以下命令在主线程中启动表单:
form1.show()
当我关闭表单然后关闭Excel时,Process Explorer显示Excel进程已正确处理。
如果我使用新主题启动表单:
Dim workerThread As Thread
workerThread = New Thread(Sub() form1.showdialog())
workerThread.Start()
当我关闭表单然后关闭Excel时,该过程仍保留在Process Explorer中。我小心不要在任何代码行中使用两个小数点,并将接口成员设置为"没有"关闭表格时。我没有使用" ReleaseCOMObject"正如其他文章所指出的那样,这是不好的做法。
问题:如何从单独的线程中正确处理Excel过程?
答案 0 :(得分:1)
不可能让COM东西正确地跨线程。
您永远不应该从另一个线程与Excel COM对象模型对话。如果你遵循这个简单的规则,你永远不必担心两个点,永远不必设置任何东西,永远不必调用任何ReleaseComObject黑客。 Excel将很好地关闭。
由于Excel是单线程的(实际上因为Excel COM对象模型位于单线程单元中),因此与另一个线程的Excel交谈没有任何性能优势 - 无论如何,内部它都被编组到主线程中。
如果您敢于从另一个线程与Excel通信,那么任何COM调用都可能随时失败。这是因为Excel仍处于“活动状态”并且可以进入“对象模型被挂起”的状态,导致所有COM调用失败(甚至COM消息过滤器都无法捕获的错误)。 Excel什么时候会进入如此顽固的模式?例如,当用户做一些疯狂的事情时,例如单击鼠标按钮。
如何在另一个线程上完成一些工作后回调Excel?当Excel处于COM调用安全的模式时,Excel-DNA有一个帮助程序,可以安排在主线程上完成的工作。您只需使用包含要完成的工作的委托来调用ExcelAsyncUtil.QueueAsMacro(...)
。这个调用可以在任何时候从任何线程进行,但代码只有在准备就绪时才会运行。
这有点笨拙的例子:
Public Module MyFunctions
Dim workerThread As Thread
Public Function OverwriteMe() As Object
Dim caller = ExcelDnaUtil.Application.Caller
workerThread = New Thread( _
Sub()
Thread.Sleep(5000)
ExcelAsyncUtil.QueueAsMacro( _
Sub()
caller.Value = "Done!!!"
End Sub)
End Sub)
workerThread.Start()
Return "Doing it..."
End Function
End Module