2012-04-25 82 views
2

我有以下代码(从在线教程中获得)。该代码正在工作,但我怀疑处理Excel COM对象的方式有点不合适。我们是否真的需要调用GC.Collect?或者什么是处理这个Excel COM对象的最佳方式?使用VB.NET处理Excel com对象的正确方法?

Public Sub t1() 
    Dim oExcel As New Excel.Application 
    Dim oBook As Excel.Workbook = oExcel.Workbooks.Open(TextBox2.Text) 

    'select WorkSheet based on name 
    Dim oWS As Excel.Worksheet = CType(oBook.Sheets("Sheet1"), Excel.Worksheet) 
    Try 

     oExcel.Visible = False 
     'now showing the cell value 
     MessageBox.Show(oWS.Range(TextBox6.Text).Text) 

     oBook.Close() 
     oExcel.Quit() 

     releaseObject(oExcel) 
     releaseObject(oBook) 
     releaseObject(oWS) 
    Catch ex As Exception 
     MsgBox("Error: " & ex.ToString, MsgBoxStyle.Critical, "Error!") 
    End Try 
End Sub 

Private Sub releaseObject(ByVal obj As Object) 
    Try 
     System.Runtime.InteropServices.Marshal.ReleaseComObject(obj) 
     obj = Nothing 
    Catch ex As Exception 
     obj = Nothing 
    Finally 
     GC.Collect() 
    End Try 
End Sub 
+4

[如何在C#中正确清理Excel互操作对象]可能的重复(http://stackoverflow.com/questions/158706/how-to-properly-clean-up-excel-interop-objects-in-c -sharp) – 2012-04-25 04:43:56

+0

@Petr Abdulin,C#是我的一门外语。不完全重复。我甚至很难理解所谓的重复中接受的答案。 – 2012-04-25 05:19:51

+0

你不应该*需要*调用'GC.Collect()' – Seph 2012-04-25 06:00:48

回答

6

@PanPizza C#和VB.NET非常相似,从行的末尾去除;Worksheets sheets = ...变得Dim sheets Worksheets = ...。如果你有兴趣在编程方面做得更好,你应该真正学会如何在两者之间进行转换,尽管许多.NET示例只在一个或另一个中提供,并且你真的限制了自己。

在本答复中提到:How do I properly clean up Excel interop objects?“不要用两个点”,这意味着总是下台成一个单一的子对象,从来没有做到这一点Dim oWS AS Excel.Worksheet = oExcel.Worksheets.Open(...)总是下台工作簿,然后从下台到工作表,从不直接Excel.Application

作为一般规则,您需要做的是按照与创建它们相反的顺序释放项目。否则,你会从其他引用下取出脚,并且它们不会正确释放。

注意如何创建Excel应用程序(oExcel),然后Excel工作簿(oBook),然后最后Excel工作表(oWS),你需要释放它们以相反的顺序。

这样你的代码就变成了:

oBook.Close() 
    oExcel.Quit() 

    releaseObject(oWS) 
    releaseObject(oBook) 
    releaseObject(oExcel) 
Catch ex As Exception 

,只是从Sub releaseObject(ByVal obj As Object)

Finally 
    GC.Collect() 

它并不需要完全删除此代码,GC自然发生的,并不指望您的应用程序立即腾出内存中,.NET为未分配的内存分配池,以便它可以轻松地在此内存中实例化对象,而不必向操作系统请求更多内存。

+0

感谢您的解释 – 2012-04-25 06:15:11

+0

@ Seph我喜欢你的根目录解释,但与VSTO它比这更进一步:http://jake.ginnivan.net/vsto-com-interop – 2012-04-25 06:37:23

+0

@ Seph感谢您的意见。我同意'GC.Collect()'在大多数情况下不是必需的,因为它自然发生,但有时当您使用COM对象时,它是必需的。对于我来说,在释放所有对象之后,我必须调用GC.Collect(),然后再调用GC.WaitForPendingFinalizers()。关于它的一些评论[这里](https://www.add-in-express.com/creating-addins-blog/2013/11/05/release-excel-com-objects/)。 – dustinrwh 2015-05-21 15:47:05

1

我已经搜索并搜索了这个,甚至微软自己的解决方案不起作用(Here,如果你想看看)。我有一个将数据导出到Excel模板的vb.net应用程序。理想情况下,当用户关闭Excel窗口时,它会终止进程,但并不是因为如Microsoft文章中所述,vb.net仍在引用它。

你需要自己杀的过程中,有一个过程,如下做到这一点:

For Each p As Process In Process.GetProcesses 
    If p.ProcessName = "EXCEL.EXE" Then p.Kill 
Next 

然而,这会杀了Excel的所有实例,用户可能有其他Excel打开窗户,将得到不保存关机,所以我想出了这个(我使用该工作簿被称为“五大问题模板”):

For Each p As Process In Process.GetProcesses 
    If InStr(p.MainWindowTitle, "Top 5 Issues Template") <> 0 Then p.Kill 
Next 

这看起来由窗口名称,而不是进程名,只有杀死与之相关的过程。这是我能够正确关闭Excel而不会搞乱任何东西的唯一方法。

0

对我来说关键是让GarbageCollector(GC)知道我想清理一些东西。我意识到这通常不是必需的,但是当使用COM对象时,有时需要这样做。请参阅此链接以获取更多信息https://www.add-in-express.com/creating-addins-blog/2013/11/05/release-excel-com-objects/

释放对象后,请致电Collect()WaitForPendingFinalizers()以要求清除GC。上面的链接指出为了从内存中完全删除COM对象,有必要调用这些方法两次。在我的情况下,调用这些方法曾经工作过,但可能值得两次。

oBook.Close() 
oExcel.Quit() 

releaseObject(oExcel) 
releaseObject(oBook) 
releaseObject(oWS) 

GC.Collect() 
GC.WaitForPendingFinalizers() 
GC.Collect() 
GC.WaitForPendingFinalizers() 
4

首先 - 你永远做的Excel互操作时调用Marshal.ReleaseComObject(...)Marshal.FinalReleaseComObject(...)。这是一个令人困惑的反模式,但任何有关此信息(包括来自Microsoft的信息)都表明您必须从.NET手动发布COM引用是不正确的。事实是,.NET运行时和垃圾收集器正确地跟踪和清理COM引用。对于您的代码,这意味着您可以删除整个releaseObject(...) Sub并调用它。其次,如果您希望确保在流程结束时清理COM对进程外COM对象的引用(以便Excel进程将关闭),则需要确保运行垃圾收集器。您可以通过拨打GC.Collect()GC.WaitForPendingFinalizers()来正确执行此操作。调用两次是安全的,最终确保循环也被清除。第三,在调试器下运行时,局部引用将人为地保持活动直到方法结束(以便局部变量检查工作)。因此GC.Collect()调用对于使用相同方法清除对象rng.Cells无效。您应该将执行GC互操作的代码从GC清理分解为单独的方法。

的一般模式是:

Sub WrapperThatCleansUp() 

    ' NOTE: Don't call Excel objects in here... 
    '  Debugger would keep alive until end, preventing GC cleanup 

    ' Call a separate function that talks to Excel 
    DoTheWork() 

    ' Now Let the GC clean up (twice, to clean up cycles too) 
    GC.Collect()  
    GC.WaitForPendingFinalizers() 
    GC.Collect()  
    GC.WaitForPendingFinalizers() 

End Sub 

Sub DoTheWork() 
    Dim app As New Microsoft.Office.Interop.Excel.Application 
    Dim book As Microsoft.Office.Interop.Excel.Workbook = app.Workbooks.Add() 
    Dim worksheet As Microsoft.Office.Interop.Excel.Worksheet = book.Worksheets("Sheet1") 
    app.Visible = True 
    For i As Integer = 1 To 10 
     worksheet.Cells.Range("A" & i).Value = "Hello" 
    Next 
    book.Save() 
    book.Close() 
    app.Quit() 

    ' NOTE: No calls the Marshal.ReleaseComObject() are ever needed 
End Sub 

有很多关于这个问题的虚假信息和混乱,包括MSDN和StackOverflow上很多帖子。

什么最终说服我有一个更仔细的看看,找出正确的建议是这个职位https://blogs.msdn.microsoft.com/visualstudio/2010/03/01/marshal-releasecomobject-considered-dangerous/一起发现问题与一些StackOverflow的答案在调试器下保持活动。