Вопрос по excel, .net, com, vb.net – Правильный способ избавиться от объекта com com с помощью VB.NET?

3

У меня есть следующий код (полученный из онлайн-учебника). Код работает, но я подозреваю, что способ избавиться от объекта com Excel несколькоnot proper, Нужно ли нам действительно звонить в GC.Collect? Или как лучше избавиться от этого объекта Excel?

<code>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
</code>
возможный дубликатHow to properly clean up Excel interop objects in C# Petr Abdulin
Вы никогда не должныneed звонитьGC.Collect() Seph
@Seph - это то, что требуется для вызова «releaseObject () Sub»; чтобы избавиться от объекта com com? Pan Pizza
@ Петр Абдулин, C # для меня иностранный язык. Не дублировать точно. Я даже изо всех сил пытаюсь понять принятый ответ в так называемом дубликате. Pan Pizza

Ваш Ответ

4   ответа
0

и даже собственное решение Microsoft не работает (Вот если хочешь посмотреть). У меня есть приложение vb.net, которое экспортирует данные в шаблон Excel. В идеале, когда пользователь закрывает окно Excel, он завершает процесс, но это не так, потому что, как указано в статье Microsoft, vb.net по-прежнему ссылается на него.

Вам нужно убить процесс самостоятельно, для этого есть процедура, описанная ниже:

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

Однако это приведет к уничтожению всех экземпляров Excel, и у пользователя могут быть открыты другие окна Excel, которые могут быть отключены без сохранения, поэтому я пришел к этому (рабочая книга, которую я использую, называется «Шаблон 5 лучших проблем») :

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

Он просматривает имя окна, а не имя процесса, и убивает только связанный с ним процесс. Это единственный способ заставить Excel правильно закрыться, ничего не испортив.

Это неприятный хак, который убивает процесс из ОС, не выделяя объекты, которые вы использовали для создания этих процессов. Это приведет к пустым объектам, которые будут генерировать исключения при следующем использовании.
6

@PanPizza C # и VB.; с конца строки,Worksheets sheets = ... становитсяDim sheets Worksheets = ..., Если вы заинтересованы в том, чтобы стать лучше в программировании, вам действительно следует научиться переходить между этими двумя примерами .NET, представленными только в одном или другом, и вы действительно ограничиваете себя.

Как уже упоминалось в этом ответе:Как правильно очистить объекты взаимодействия Excel? & quot; Никогда не используйте две точки & quot; это означает, что всегда уходите в единый подобъект и никогда не делайте этогоDim oWS AS Excel.Worksheet = oExcel.Worksheets.Open(...) всегда переходите к рабочей книге, а затем переходите к рабочему листу, а не непосредственно изExcel.Application.

Как правило, вам нужно освободить свои элементы в порядке, обратном тому, в котором они были созданы. В противном случае вы убираете ноги из-под ваших других ссылок, и они не будут правильно освобождены.

Обратите внимание, как вы создаете приложение Excel (oExcel), затем Excel Workbook (oBook) и, наконец, Excel Worksheet (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 пул нераспределенной памяти, чтобы он мог легко копировать объекты в этой памяти, а не запрашивать у ОС больше памяти.

@Seph Спасибо за комментарии. я согласна с тем чтоGC.Collect() в большинстве случаев не требуется, потому что это происходит естественным образом, но иногда, когда вы работаете с COM-объектами, это необходимо. Для меня после освобождения всех объектов мне пришлось позвонитьGC.Collect() с последующимGC.WaitForPendingFinalizers(), Есть несколько комментариевhere об этом.
спасибо за объяснение Pan Pizza
@Seph, мне нравится & quot; ваше объяснение root-ссылки, но с VSTO оно идет немного дальше:jake.ginnivan.net/vsto-com-interop
0

что я хочу что-нибудь почистить. Я понимаю, что это обычно не является необходимым, но при работе с COM-объектами это иногда необходимо. Смотрите эту ссылку для получения дополнительной информацииhttps://www.add-in-express.com/creating-addins-blog/2013/11/05/release-excel-com-objects/

После освобождения объектов спроситеGC прибратьсяCollect() а такжеWaitForPendingFinalizers(), Ссылка выше заявляет, что необходимо вызвать эти методы дважды, чтобы полностью удалить COM-объекты из памяти. В моем случае вызов этих методов когда-то работал, но, возможно, стоит вызвать его дважды.

oBook.Close()
oExcel.Quit()

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

GC.Collect()
GC.WaitForPendingFinalizers()
GC.Collect()
GC.WaitForPendingFinalizers()
5

never должен позвонитьMarshal.ReleaseComObject(...) или жеMarshal.FinalReleaseComObject(...) при выполнении Excel взаимодействия. Это запутанный анти-паттерн, но любая информация об этом, в том числе от Microsoft, которая указывает, что вы должны вручную выпускать ссылки COM из .NET, неверна. Дело в том, что среда выполнения .NET и сборщик мусора правильно отслеживают и очищают ссылки COM. Для вашего кода это означает, что вы можете удалить весьreleaseObject(...) Саб и звонки на него.

Во-вторых, если вы хотите убедиться, что ссылки COM на внепроцессный COM-объект очищены после завершения процесса (чтобы процесс Excel закрылся), вам необходимо убедиться, что сборщик мусора работает. Вы делаете это правильно с помощью звонковGC.Collect() а такжеGC.WaitForPendingFinalizers(), Вызов дважды безопасен, завершение гарантирует, что циклы также будут очищены.

В-третьих, при работе под отладчиком локальные ссылки будут искусственно поддерживаться до конца метода (чтобы проверка локальных переменных работала). Так что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.

Это интересно, потому что при изучении мнения на эту тему я впервые вижу пример, подобный вашему. Кажется, что анти-шаблон широко распространен в примерах.

Похожие вопросы