.net - 使用 VB.NET 处理 Excel com 对象的正确方法?

标签 .net vb.net excel com

我有以下代码(从在线教程获得)。该代码可以正常工作,但我怀疑处理 Excel com 对象的方式有些不正确。我们真的需要调用GC.Collect吗?或者处理此 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

最佳答案

首先 - 您永远在执行操作时不必调用 Marshal.ReleaseComObject(...)Marshal.FinalReleaseComObject(...) Excel 互操作。这是一个令人困惑的反模式,但是任何有关此的信息(包括来自 Microsoft 的信息)表明您必须从 .NET 手动释放 COM 引用都是不正确的。事实上,.NET 运行时和垃圾收集器正确地跟踪和清理 COM 引用。对于您的代码,这意味着您可以删除整个 releaseObject(...) Sub 及其调用。

其次,如果要确保在进程结束时清除对进程外 COM 对象的 COM 引用(以便 Excel 进程关闭),则需要确保垃圾收集器运行。您可以通过调用 GC.Collect()GC.WaitForPendingFinalizers() 正确执行此操作。调用两次是安全的,end 确保循环也确实被清理。

第三,当在调试器下运行时,局部引用将被人为地保持事件状态,直到方法结束(以便局部变量检查起作用)。因此,GC.Collect() 调用对于通过同一方法清理 rng.Cells 等对象无效。您应该将执行 COM 互操作的代码从 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 答案中找到在调试器下保持事件的引用的问题。

关于.net - 使用 VB.NET 处理 Excel com 对象的正确方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10309365/

相关文章:

.net - 64 位 .NET 的优点/缺点是什么?

vb.net - LINQ 泛型 SUM - VB.NET

c# - .NET XMLDocument 编码问题

python - Pandas COUNTIF 每个数据框列值

c# - 从 C# Access VBA 函数

c# - Open XML SDK 2.0 - 如何更新电子表格中的单元格?

javascript - 类型 System.Collections.Generic.IDictionary 不支持反序列化数组

.net - 如何隔离 .Net Process 中的非托管代码崩溃

c# - 如何使用非标准(和变化的)属性名称反序列化 JSON(在 .NET 中)

c++ - 在 VB.NET 中使用 DLL 导入/声明以及 .NET 不使用的参数类型