2012-04-25 88 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的答案在調試器下保持活動。