如何从创建的"Excel.Application"对象中获取进程ID?

D_B*_*ter 8 vb.net excel

如何从正在运行的对象中获取进程ID?

Dim xlApp As Object  = CreateObject("Excel.Application")
Run Code Online (Sandbox Code Playgroud)

我需要使用后期绑定,因为我无法保证我将获得哪个版本,因此使用Microsoft.Office.Interop.Excel将无法正常工作.

'do some work with xlApp

xlApp.Quit
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp)
xlApp = nothing
Run Code Online (Sandbox Code Playgroud)

此时Excel仍在后台运行.我熟悉使用变量的所有建议并释放它们然后使用:System.Runtime.InteropServices.Marshal.ReleaseComObject(o).这不能可靠地工作.我正在做的工作非常复杂.我使用多个文件用于每个循环等.无法在Excel中释放所有资源.我需要一个更好的选择.

我想Process.Kill在Excel 上使用,但我不知道如何从xlApp对象获取进程.我不想杀死所有Excel进程,因为用户可能打开了工作簿.

我尝试使用Dim xProc As Process = Process.Start(ExcelPath)然后使用xProc.Kill()有时工作除了使用XLApp = GetObject("Book1").ApplicationXLApp = GetObject("", "Excel.Application")如果用户已经打开Excel窗口使用正确的Excel对象有点棘手.我需要一个更好的选择.

我不能使用GetActiveObjectBindToMoniker获取Excel对象,因为它们仅在使用早期绑定时才能工作.例如Microsoft.Office.Interop.Excel

如何从正在运行的对象中获取进程ID?

编辑:其实我真的不想对如何让Excel很好地退出进行重新考虑.许多其他问题都解决了这个问题 在这里这里我只想杀死它; 干净,准确,直接.我想要杀死我开始的确切过程而不是其他过程.

D_B*_*ter 6

其实没关系;我想到了。这是一个非常干净,针对性强的解决方案,它会终止已启动的确切过程。它不会干扰用户可能已打开的任何其他进程或文件。以我的经验,在关闭文件并退出Excel之后杀死进程是处理Excel的最快,最简单的方法。这是一个知识库文章,描述了该问题以及Microsoft推荐的解决方案。

请注意,此解决方案不会终止Excel应用程序。如果没有正确处理任何指针,它只会杀死空的进程外壳。当我们打电话时,Excel本身确实退出了xlApp.quit()。可以通过尝试附加正在运行的Excel应用程序来确认这一点,因为Excel根本没有运行,该应用程序将失败。

许多人不建议终止该过程。请参阅 如何正确清理Excel互操作对象了解.net中的垃圾收集

另一方面,许多人不建议使用GC.Collect。请参阅使用GC.Collect()有什么问题?

确保关闭所有打开的工作簿,退出应用程序,释放xlApp对象。最后检查该进程是否仍然存在,如果是,则将其终止。

Private Declare Auto Function GetWindowThreadProcessId Lib "user32.dll" (ByVal hwnd As IntPtr, _
              ByRef lpdwProcessId As Integer) As Integer

Sub testKill()

    'start the application
    Dim xlApp As Object = CreateObject("Excel.Application")

    'do some work with Excel

    'close any open files

    'get the window handle
    Dim xlHWND As Integer = xlApp.hwnd

    'this will have the process ID after call to GetWindowThreadProcessId
    Dim ProcIdXL As Integer = 0

    'get the process ID
    GetWindowThreadProcessId(xlHWND, ProcIdXL)

    'get the process
    Dim xproc As Process = Process.GetProcessById(ProcIdXL)

    'Quit Excel
    xlApp.quit()

    'Release
    System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp)

    'set to nothing
    xlApp = Nothing

    'kill it with glee
    If Not xproc.HasExited Then
        xproc.Kill()
    End If

End Sub
Run Code Online (Sandbox Code Playgroud)

一旦意识到可以从Excel中获取窗口句柄,则只需要该函数即可从窗口句柄获取进程ID。因此,GetWindowThreadProcessId如果有人知道vb.net的获取方式,我将不胜感激。

  • 这是“完全”解决此问题的错误方法。阅读[this answer](http://stackoverflow.com/a/17131389/17034)以了解发生了什么。 (5认同)
  • http://stackoverflow.com/questions/21310221/how-to-debug-slow-office-application-interop-constructor (3认同)

Han*_*ant 6

使用Marshal.ReleaseComObject()或杀死Excel.exe进程是非常丑陋,容易出错和不必要的创可贴.从长远来看,这个问题非常有害,这个问题显示了可能发生的事情.正确的方法是调用GC.Collect(),但阅读此答案以了解为什么在调试程序时这种方法无效.

解决方法很简单,您只需要确保以不同的方法调用GC.Collect().这可确保您的Excel对象引用不再在范围内.因此,执行此操作的程序的粗略轮廓将是:

Sub Main()
    DoOfficeStuff()
    GC.Collect()
    GC.WaitForPendingFinalizers()
    '' Excel.exe will now be gone
    '' Do more work
    ''...
End Sub

Sub DoOfficeStuff()
    Dim xlApp As Object = CreateObject("Excel.Application")
    '' etc..
End Sub
Run Code Online (Sandbox Code Playgroud)

  • 对此感到抱歉,但指出程序员使用错误的解决方案来解决他的问题被认为是SO问题的合法答案.更大的想法是,解决方案并不仅仅对提问者有用,任何有朝一日搜索这个问题的人都可能更倾向于不故意杀死一个进程.他们也应该这样做.你当然可以看到这种感觉吗?你可以选择一个答案来让你感觉更好. (2认同)