Программа VB.NET Excel оставляет EXCEL.EXE плавающим после завершения - PullRequest
7 голосов
/ 24 февраля 2012

Я пишу программу, которая перебирает все листы в книге Excel и сохраняет каждый лист как свою собственную книгу.Это оказалось немного хитрее, чем я ожидал, поскольку метод Sheet.Copy создает странный объект (см. Здесь обсуждение MSDN, которое, я считаю, имеет отношение: http://msdn.microsoft.com/en-us/library/ms178779.aspx).

В любом случае, янайдено еще одно сообщение о переполнении стека , которое привело меня туда, где я нахожусь, что по сути завершено, за пределами одного зависшего процесса EXCEL.EXE, который остается после завершения программы (проверьте обновление на наличие еще одной проблемы, которая возникла, но я думаю, что они связаны).

Вот мой код:

Imports System.Data
Imports System.IO
Imports Microsoft.Office.Interop
Imports Office = Microsoft.Office.Core
Imports xlNS = Microsoft.Office.Interop.Excel
Imports System.Runtime.InteropServices

Class Form1

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

    'Get information from text boxes
    Dim InputLocation As String
    Dim OutputLocation As String

    InputLocation = InputLoc.Text & "\" & FileName.Text

    If OutputLoc.Text = "" Then
        OutputLocation = InputLoc.Text
    Else
        OutputLocation = OutputLoc.Text
    End If

    'Make file to save files in
    ' Get date and time in filename as well
    Dim TLDateTime As String
    Dim TLDay As String
    Dim TLMonth As Integer
    Dim TLYear As Integer
    Dim TLHour As Integer
    Dim TLMinute As Integer
    Dim TLDate As String
    Dim TLTime As String
    Dim TLSecond As Integer

    TLDay = DateTime.Now.Day
    TLMonth = DateTime.Now.Month
    TLYear = DateTime.Now.Year
    TLHour = DateTime.Now.Hour
    TLMinute = DateTime.Now.Minute
    TLSecond = DateTime.Now.Second

    Dim MyDate As New DateTime(TLYear, TLMonth, TLDay, TLHour, TLMinute, TLSecond)
    Dim MyString As String = MyDate.ToString("MMMddyyyy_HHmmss")
    TLDate = TLMonth.ToString + TLDay.ToString + TLYear.ToString
    TLTime = TLHour.ToString + TLMinute.ToString
    TLDateTime = TLDate + "_" + TLTime

    Try
        Directory.CreateDirectory(OutputLocation & "\" & "Field Sales Report Graphs " & TLDateTime)
        OutputLocation = OutputLocation & "\" & "Field Sales Report Graphs " & TLDateTime
    Catch
        MsgBox("Trying to create a file that exists, please delete it. If the file does not exist check to make sure your output location exists")
    End Try

    'Open up excel file with information in it

    Dim xlApp1 As Excel.Application
    Dim locs As Excel.Workbook

    Dim exportsheet As Excel.Worksheet
    xlApp1 = New Excel.Application
    xlApp1.Visible = True
    xlApp1.Application.DisplayAlerts = False
    locs = xlApp1.Workbooks.Open(InputLocation)

    'locsws = locs.ActiveSheet
    Dim wkshtcount = locs.Worksheets.Count - 1
    Dim fileNames As New ArrayList

    For counter = 1 To wkshtcount + 1
        'identify and copy sheet to move
        exportsheet = CType(locs.Worksheets(counter), Excel.Worksheet)
        fileNames.Add(exportsheet.Name)
        exportsheet.Copy(Type.Missing, Type.Missing)

        exportsheet = xlApp1.Workbooks("Book" & counter).Sheets(1)

        exportsheet.SaveAs(Filename:=OutputLocation & "\" & fileNames(counter - 1) & ".xlsx")

        'close excel and release com objects
        System.Runtime.InteropServices.Marshal.ReleaseComObject(exportsheet)
        exportsheet = Nothing
        System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp1.ActiveWorkbook)
        xlApp1.ActiveWorkbook.Close(False)

    Next
    'close excel and release com objects
    locs.Close(False)
    System.Runtime.InteropServices.Marshal.ReleaseComObject(locs)
    locs = Nothing
    xlApp1.Quit()
    System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp1)
    xlApp1 = Nothing


End Sub
End Class

Теперь я думаю, что проблема возникает в конце цикла, где я пытаюсь закрыть файл экспортаи новый рабочий лист, который он создает:

        'close excel and release com objects
        System.Runtime.InteropServices.Marshal.ReleaseComObject(exportsheet)
        exportsheet = Nothing
        xlApp1.Workbooks(fileNames(counter - 1)).Close(False)

Я не могу понять, что делать, чтобы выпустить ComObject для нового созданного рабочего листа. Я пробовал все виды вещей, но этовсегда выдает ошибку COM, когда я делаю это, и если я пытаюсь определить это как ничего (как я делаю с помощью таблицы экспорта), это говорит о том, что по умолчанию это только чтение, поэтому я не могу этого сделать. Похоже, это должно быть что-тотак просто, как:

System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp1.Workbooks(fileNames(counter - 1)))

Но это не так.Я попробовал несколько вариантов этого, и я думаю, что это имеет отношение к ссылке MSDN выше, но я не могу полностью разобраться, что делать.Таким образом, код работает для моих целей, не выходя из одного файла EXCEL.EXE после его завершения.

Что касается тестового файла, я просто использую файл Excel с 3 листами и помещаю некоторую информацию в каждыйи измените имя листа, чтобы было легко увидеть, работает он или нет.

Любые мысли будут оценены, спасибо.

Обновление: я только что отключил видимость из своего основного приложения Excel, и все еще всплывающие окна, что наводит меня на мысль, что способ, которым я используюCopy создает новое приложение Excel, но я не совсем уверен, как на него ссылаться.Если кто-то и знает, как отключить видимость, это было бы очень полезно.

Окончательное обновление: если вероятность того, что какая-то бедная душа столкнется с той же проблемой, с которой я столкнулся, первое обновление должно решить,это, но также важно отметить, что excel.exe будет зависать, пока вы не закроете приложение.Я сообщаю код автоматизации в виде приложения Windows Form (чтобы сотрудники могли указать местоположение файла и т. Д.), И будет процесс Excel.exe, работающий, пока вы не закроете всплывающее окно из программы.Возможно, сборщик мусора не запускается до тех пор, пока вы не закроете окно приложения, или просто по какой-то другой причине просто зависает на экземпляре excel.exe.

Ответы [ 4 ]

3 голосов
/ 19 ноября 2012

Ниже приведен код, который работает для меня (обратите внимание, что я освобождаю объекты, что важно)

xlWorkBook.Close()
xlApp.Quit()
ReleaseObject(xlWorkSheet)
ReleaseObject(xlWorkBook)
ReleaseObject(xlApp)

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
2 голосов
/ 25 февраля 2012

Возможно, вам понадобится сделать следующее, если у вас есть не связанные объекты COM, которые создаются:

GC.Collect()
GC.WaitForPendingFinalizers()

Взято из этого сообщения на форуме MSDN: http://social.msdn.microsoft.com/Forums/en-US/netfxbcl/thread/cb5f7948-c229-483e-846b-a1cfbbcd86ca/

1 голос
/ 27 февраля 2012

Решением, с которым я наконец столкнулся, было добавление

System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp1.ActiveWorkbook)
xlApp1.ActiveWorkbook.Close()

к циклу.Я нашел этот ответ в комментариях Первый ответ на другой пост переполнения стека.По сути, проблема, с которой я столкнулся, заключается в том, что метод worksheet.copy создает объект рабочей книги без ссылки, но оказывается, что на него можно ссылаться, ссылаясь на активную таблицу.Если вы хотите сделать с ним больше, чем просто выбросить его за дверь, как я, я думаю, вы могли бы создать новый объект рабочей книги и назначить его или, как подсказывает ссылка, на которую я ссылаюсь, вы можете сохранить его как что-то еще.С моей точки зрения, это просто нормально выпустить его после сохранения нужной мне таблицы, и это убрало мой процесс зависания Excel.exe.

Если вы хотите немного более элегантный вариант кода, вы должны проверить пост Рона Торнамбегде он делает для каждого цикла, мне удалось получить не правильно вместо того, что я создаю.По сути, вы хотели бы использовать его цикл в моем коде, и вы бы все настроили.Спасибо как всегда переполнение стека.

1 голос
/ 25 февраля 2012

Хорошо, это работает для меня на очень простом листе. Дайте знать, если у вас появятся вопросы.

Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    'Get information from text boxes
    Dim InputLocation As String
    Dim OutputLocation As String

    InputLocation = InputLoc.Text & "\" & Filename.Text

    If OutputLoc.Text = "" Then
        OutputLocation = InputLoc.Text
    Else
        OutputLocation = OutputLoc.Text
    End If

    'Make file to save files in
    ' Get date and time in filename as well
    Dim TLDateTime As String
    Dim TLDay As String
    Dim TLMonth As Integer
    Dim TLYear As Integer
    Dim TLHour As Integer
    Dim TLMinute As Integer
    Dim TLDate As String
    Dim TLTime As String
    Dim TLSecond As Integer

    TLDay = DateTime.Now.Day
    TLMonth = DateTime.Now.Month
    TLYear = DateTime.Now.Year
    TLHour = DateTime.Now.Hour
    TLMinute = DateTime.Now.Minute
    TLSecond = DateTime.Now.Second

    Dim MyDate As New DateTime(TLYear, TLMonth, TLDay, TLHour, TLMinute, TLSecond)
    Dim MyString As String = MyDate.ToString("MMMddyyyy_HHmmss")
    TLDate = TLMonth.ToString + TLDay.ToString + TLYear.ToString
    TLTime = TLHour.ToString + TLMinute.ToString
    TLDateTime = TLDate + "_" + TLTime

    Try
        Directory.CreateDirectory(OutputLocation & "\" & "Field Sales Report Graphs " & TLDateTime)
        OutputLocation = OutputLocation & "\" & "Field Sales Report Graphs " & TLDateTime
    Catch
        MsgBox("Trying to create a file that exists, please delete it. If the file does not exist check to make sure your output location exists")
    End Try

    'Open up excel file with information in it
    Dim xlApp1 As Excel.Application = New Excel.Application
    xlApp1.Visible = True
    xlApp1.Application.DisplayAlerts = False

    Dim locs As Excel.Workbook
    locs = xlApp1.Workbooks.Open(InputLocation)
    Dim fileNames As New ArrayList
    Dim counter As Integer = 1
    For Each ws As Excel.Worksheet In locs.Worksheets
        fileNames.Add(ws.Name)
        ws = xlApp1.Workbooks(counter).Sheets(1)
        ws.SaveAs(Filename:=OutputLocation & "\" & fileNames(counter - 1) & ".xlsx")
        'close excel and release com objects
        System.Runtime.InteropServices.Marshal.ReleaseComObject(ws)
        xlApp1.Workbooks(counter).Close(False)
        counter += 1
    Next
    System.Runtime.InteropServices.Marshal.ReleaseComObject(locs)
    locs = Nothing
    xlApp1.Quit()
    System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp1)
    xlApp1 = Nothing
End Sub
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...