【问题标题】:Unable to kill excel processes无法杀死excel进程
【发布时间】:2013-10-07 11:48:39
【问题描述】:

我正在使用以下代码对 excel 文件进行一些复制和粘贴:

Imports Excel = Microsoft.Office.Interop.Excel
Imports System.IO
Imports System.Runtime.InteropServices

Private Sub btnCombine_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCombine.Click

    Dim xlAppSource As New Excel.Application
    Dim xlAppTarget As New Excel.Application
    Dim xlWbSource As Excel.Workbook
    Dim xlWbTarget As Excel.Workbook
    Dim xlsheetSource As Excel.Worksheet
    Dim xlsheetTarget As Excel.Worksheet
    Dim xlRangeSource As Excel.Range
    Dim xlRangeTarget As Excel.Range

    Dim Progress As Integer = 0
    pbrProgress.Minimum = 0
    pbrProgress.Maximum = amountOfFiles

    btnCombine.Enabled = False
    btnSelect.Enabled = False
    pbrProgress.Visible = True

    getSaveLocation()
    MakeExcelFile()

    'loop through all excel files to get the required data
    For i = 0 To amountOfFiles - 1


        Dim CurrentFile As String = strFileNames(i)
        Dim IntAmountOfRows As Integer = amountOfRows(CurrentFile)
        Dim intStartOfEmptyRow As Integer = amountOfRows(SummaryLocation)

        xlAppSource.DisplayAlerts = False
        xlAppTarget.DisplayAlerts = False

        'Set current workbook
        xlWbSource = xlAppSource.Workbooks.Open(CurrentFile)
        xlWbTarget = xlAppTarget.Workbooks.Open(SummaryLocation)

        'set current worksheet
        xlsheetSource = xlWbSource.ActiveSheet
        xlsheetTarget = xlWbTarget.ActiveSheet

        'copy range of data from source to target file
        xlRangeSource = xlsheetSource.Range("A2:k" & IntAmountOfRows)
        xlRangeSource.Copy()

        xlRangeTarget = xlsheetTarget.Range("A" & intStartOfEmptyRow)
        xlRangeTarget.PasteSpecial(Excel.XlPasteType.xlPasteValues)

        'save summary file before closing
        xlsheetTarget.SaveAs(SummaryLocation)

        'updating progress bar
        Progress = Progress + 1
        pbrProgress.Value = Progress

    Next

    'close excel
    xlWbSource.Close(True)
    xlWbTarget.Close(True)
    xlAppSource.Quit()
    xlAppTarget.Quit()

    xlAppSource.DisplayAlerts = True
    xlAppTarget.DisplayAlerts = True


    'Cleanup
    Marshal.ReleaseComObject(xlAppSource)
    Marshal.ReleaseComObject(xlAppTarget)
    Marshal.ReleaseComObject(xlWbSource)
    Marshal.ReleaseComObject(xlWbTarget)
    Marshal.ReleaseComObject(xlsheetSource)
    Marshal.ReleaseComObject(xlsheettarget)
    Marshal.ReleaseComObject(xlRangeSource)
    Marshal.ReleaseComObject(xlRangeTarget)

    xlAppSource = Nothing
    xlAppTarget = Nothing
    xlWbSource = Nothing
    xlWbTarget = Nothing
    xlsheetSource = Nothing
    xlsheetTarget = Nothing
    xlRangeSource = Nothing
    xlRangeTarget = Nothing

    MsgBox("Samenvoegen compleet")

    init()

End Sub

我已经尝试了 SO 上给出的所有解决方案:

  • 切勿在一条线上使用 2 个点
  • 我尝试过使用 marshall.ReleaseComObject
  • 我已尝试将对象设置为“无”

但是,每次我运行应用程序时,都会有 10-20 个 excel 进程仍在运行。

【问题讨论】:

  • 您忘记了 tmpSource 和 tmpTarget。实际上,您不需要它们,可以将 xlWbTarget = tmpTarget.Open(SummaryLocation) 替换为 xlWbTarget = xlAppTarget.Workbooks.Open(SummaryLocation)。在对象退出后不确定 DisplayAlerts 的确切点。您也可以取消注释最后一堆 Nothing(至少是 App 对象)。如果你做了所有这些,当你关闭你的应用程序时,就不会有任何正在运行的 Excel 进程。
  • PS:我现在意识到(在阅读下面的评论后)您正在循环内创建/处理 Excel 对象。您不需要这样做(实际上非​​常不推荐,正是因为与释放 Excel 对象相关的问题);您可以一遍又一遍地使用相同的对象,并且只在关闭应用程序之前释放它们。如果您需要打开不同的 Excel 窗口,请根据需要创建尽可能多的对象。
  • 我制作 tmpTarget 的原因是因为在另一个问题上,人们被告知不要在互操作中使用超过 1 个点 (.)
  • 我不明白最后的推理,但如果你愿意,你可以随意使用 tmpTarget;虽然你也必须释放它。无论如何,正如我之前的 PS 中所说,您应该更改代码:Excel 对象释放过程应该在最后完成一次,当您不再需要对象时。在循环内部,您应该保留相同的对象,或者创建与不同 Excel 窗口一样多的不同对象(所有这些都应在完成后释放)。
  • 如果在完成循环(以及对象的所有操作)之后,您编写对象释放代码(通过考虑所有对象 + 最后的 Nothing)。当您关闭应用程序时,不会有任何正在运行的 Excel 实例。要进行测试,请确保没有来自先前执行的 Excel 对象(启动时没有正在运行的 Excel 进程)。

标签: vb.net visual-studio-2010 excel process


【解决方案1】:
Option Explicit

Sub Main()

    Dim xlApp As Excel.Application
    Set xlApp = New Excel.Application

    xlApp.Visible = False
    xlApp.DisplayAlerts = False

    Dim xlWb As Workbook
    Set xlWb = xlApp.Workbooks.Open("C:\...\path")

    Dim xlSht As Worksheet
    Set xlSht = xlWb.Sheets(1)

    xlSht.Range("A1") = "message from " & ThisWorkbook.FullName

    xlWb.Saved = True
    xlWb.Save
    xlWb.Close

    xlApp.Quit

End Sub

每次都为我工作,不会让任何 Excel 进程挂在任务管理器中。

注意:如果您的代码在某些时候中断,并且您没有正确处理已打开的对象,那么它们只会挂在任务管理器的进程选项卡中。如果您尚未在代码中实现错误处理,请启动 here

只考虑这个作为替代

Option Explicit

Sub Main()

    On Error GoTo ErrHandler
    Dim xlApp As Excel.Application
    Set xlApp = New Excel.Application

    xlApp.Visible = False
    xlApp.DisplayAlerts = False

    Dim xlWb As Workbook
    Set xlWb = xlApp.Workbooks.Open("C:\...\path")

    Dim xlSht As Worksheet
    Set xlSht = xlWb.Sheets(1)

    xlSht.Range("A1") = "message from " & ThisWorkbook.FullName

    xlWb.Saved = True
    xlWb.Save
    xlWb.Close

    xlApp.Quit
    Exit Sub

ErrHandler:
    xlWb.Saved = True
    xlWb.Save
    xlWb.Close

    xlApp.Quit
End Sub

【讨论】:

  • 很抱歉,但这不是 Interop Excel 的工作方式。 Excel 进程仍然存在,您必须做很多工作才能删除它们(按照 OP 所做的那样)。不知道为什么您的代码看不到它,可能是因为 .Visible = False 或仅使用一个 Excel 对象,但这肯定是一个有问题的问题。
  • 正如您在我的代码中看到的,我已经在我的应用程序中使用了 .save、.close 和 .quit。但是,它继续堆积 excel 进程,直到我的应用程序崩溃,因为它认为有人仍然打开了我正在尝试使用的 excel 文件。 `注意:在我运行应用程序之前,我确保所有的excel进程都被终止,所以它不能是旧实例
  • @Gutanoth 你读过关于错误处理的部分吗?您需要在代码中使用它来控制正在发生的事情。在 ErrHandler 中添加 debug.print 行,以查看处理文件时是否有错误。 Killing Excel 进程不被视为错误处理,而是一种逃避麻烦的方法。如果您在宏中编码了正确的错误处理,那么您不应该留下任何不需要的进程。
  • @mehow 是的,抱歉,我在发布更新后的问题后将其添加到我的应用程序中。
  • 啊!我知道这里有什么问题。 OP 不是要求 VBA/宏解决方案,而是要求 VB.NET(通过 Interop 与 Excel 耦合)。您的答案和您的建议适用于 VBA,但不完全适用于 VB.NET(主要是正在运行的 Excel 实例位)。
【解决方案2】:

The Interop Marshal release doesn't always work because Excel XP and lower suck at releasing. I had to use your loop, but replace the Process.Close() and Process.Quit() with Process.Kill(). Works like a charm. Just be careful that you want ALL versions of excel.exe to be killed, because they will.

我用这个:

Workbook.Save()
Workbook.Close()
Application.Quit()

System.Runtime.InteropServices.Marshal.ReleaseComObject(Application)

Worksheet = Nothing
Workbook = Nothing
Application = Nothing

Dim proc As System.Diagnostics.Process

For Each proc In System.Diagnostics.Process.GetProcessesByName("EXCEL")
proc.Kill()
Next

【讨论】:

  • 毫无疑问,您的代码有效。如果我说我从未使用过它,我会撒谎;但只有在没有其他选择的情况下才建议使用这种方法。这里还有其他选择。
  • 我正在制作这个供其他工程师使用。我担心当他们在使用我的应用程序时还使用 Excel 做其他事情时,他们会失去工作
【解决方案3】:

当您对 Excel 互操作执行任何操作时,它会创建一个新进程。

杀死所有 Excel 进程的问题在于,您可能会杀死正在使用的进程。你可以杀死所有已经运行了一段时间(即从某个时间戳)的进程:

Dim proc As System.Diagnostics.Process
Dim info As ManagementObject
Dim search As New ManagementObjectSearcher("SELECT ProcessId FROM Win32_process WHERE caption = 'Excel'")
For Each info In search.Get()

    'The string will give you the time that the process started
    'You can then subtract that from the current time to see if you want to kill the process
    Dim TheString As String = info.GetText(TextFormat.Mof).ToString

    'Kill the process according to its ID
    proc = System.Diagnostics.Process.GetProcessById(Mid$(TheString, _
                                                    (Len(TheString) - 8), 4))
    proc.CloseMainWindow()
    proc.Refresh()
    If proc.HasExited Then GoTo NoKill
    proc.Kill()
NoKill:
Next

您需要添加:

Imports System.Management

并将“System.Management”的引用添加到您的项目中。

【讨论】:

  • 您的代码看起来很像 Coder 建议的代码。但我不想“不小心”杀死其他用户在使用我的应用程序时正在使用的 excel 进程
  • 因此,如果进程已打开超过 X 分钟,您只能终止该进程。我正在从我这里的一些代码中复制,但我认为我们使用 Val(Mid$(TheString, 57, 2)) 来获取 Excel 进程开始的那一刻。如果您调试 TheString,您会看到它包含进程的创建日期/时间。顺便说一句,进程名称是 EXCEL.exe 而不仅仅是 EXCEL。
  • 您也可以使用“SELECT * FROM Win32_process WHERE caption = 'EXCEL.exe'” 这将为您提供有关该过程的更多信息。那里应该有一些东西可以让您查看您启动了哪些进程 - 然后根据该数据终止该进程。
  • 此外,“SELECT * FROM Win32_process”查询仅显示当前用户的进程。这可能有助于您确定您创建了哪些流程。
  • 真的很抱歉,但是对于这个问题,使用 SQL 对我来说似乎是一件非常复杂的事情
【解决方案4】:

您可以从 Excel.Application hwnd 获取进程 ID 并使用它来安全地终止进程。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-08-05
    • 2015-06-08
    • 2015-06-01
    • 2016-07-13
    • 1970-01-01
    • 2015-01-06
    • 2018-05-07
    • 1970-01-01
    相关资源
    最近更新 更多