2013-07-20 283 views
4

如何从运行对象获取进程ID?如何从创建的“Excel.Application”对象获取进程ID?

Dim xlApp As Object = CreateObject("Excel.Application") 

我需要使用后期绑定,因为我不能保证我会得到这样使用Microsoft.Office.Interop.Excel将无法​​正常工作的版本。

'do some work with xlApp 

xlApp.Quit 
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp) 
xlApp = nothing 

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

我想在Excel上使用Process.Kill,但我不知道如何从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很好地退出。许多其他问题解决了这个问题。 herehere我只是想杀死它;干净,准确和直接。我想杀死我开始的确切过程,而不是其他的。

+0

*不使用更可靠的工作* [您是否泄漏COM对象而不是一个'。'?](http://stackoverflow.com/questions/2191489/releasing-temporary-com-objects) –

+0

这是在“不是答案”的领土,但你有没有考虑[EPPlus](http:///epplus.codeplex.com/)?它对Excel的功能有很好的支持,并且不会让进程悬而未决[在Office自动化界面带来的所有其他不好的事情中](http://support.microsoft.com/kb/257757)。 –

+0

@ ta.speot.is在Excel中声明和释放所有COM对象并不总是现实可行的。我花了不少时间在我的项目上尝试。不可行。 –

回答

2

其实没关系;我想到了。这是一个非常干净,精确针对性的解决方案,可以杀死已启动的确切流程。它不会干扰用户可能打开的任何其他进程或文件。根据我的经验,在关闭文件并退出Excel之后终止进程是处理Excel的最快和最简单的方法。 Here is a knowledge Base article描述了这个问题以及微软推荐的解决方案。

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

很多人不推荐杀死进程;见 How to properly clean up Excel interop objectsUnderstanding Garbage Collection in .net

在另一方面,许多人建议不要使用GC.Collect的。请参阅What's so wrong about using GC.Collect()?

务必关闭所有打开的工作簿,退出应用程序,释放xlApp对象。最后检查一下,看看这个过程是否还活着,如果是的话就杀了它。

​​

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

+4

这是**完全**是解决这个问题的错误方法。阅读[这个答案](http://stackoverflow.com/a/17131389/17034)了解发生了什么。 –

+0

@HansPassant非常有趣的一块。它照亮了GC垃圾收集。但很多人都说使用GC.Collect(); GC.WaitForPendingFinalizers();实际上是一项昂贵的操作。我不知道为什么在关闭文件和退出应用程序之后关闭Excel过程比GC更糟糕。搜集。我很乐意为此提供答案。 –

+0

@HansPassant感谢您对此的意见。我编辑了我的答案,以包含一些处理Excel的推荐方式的链接。 –

6

使用Marshal.ReleaseComObject()或杀死Excel.exe进程很丑,容易出错并且不必要的band-aids可以解决这个问题。从长远来看,这是非常有害的,this question显示可能发生的事情。正确的方法是调用GC.Collect(),但是请阅读this answer以了解为什么在调试程序时这不起作用。

解决方法很简单,您只需确保在不同的方法中调用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 
+0

就个人而言,我不会打扰Office Interop的东西,除非EPPlus无法在某些情况下操作电子表格特殊的Excel专用方式。 –

+0

@Hans Passant:GC会不会最终垃圾收集Excel(尽管它会自行处理)?在那种情况下,是否有任何使用GC.Collect来强制它?这条语句的真实性如下:如果“调用COM对象的至少一个成员而不将其分配给变量”,Excel不会被释放。请参阅http://stackoverflow.com/questions/158706/how-to-properly-clean-up-excel-interop-objects。 – Tarik

+0

它当然会。慢性问题是程序员实际上并不喜欢垃圾收集器的工作方式。他们做不明智的事情,比如停止分配内存,仍然期待GC收集垃圾。 –

0

对于C#应用:

[DllImport("user32.dll")] 
    private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); 

    uint iProcessId = 0; 

    //Get the process ID of excel so we can kill it later. 
    GetWindowThreadProcessId((IntPtr)ExcelObj.Hwnd, out iProcessId); 

    try 
    { 
     Process pProcess = Process.GetProcessById((int)iProcessId); 
     pProcess.Kill(); 
    } 
    catch (System.Exception) 
    { 
     //just ignore any failure. 
    } 
1
Public Declare Function GetWindowThreadProcessId Lib "user32" _ 
    (ByVal hwnd As Long, _ 
    ByRef lpdwProcessId As Long) As Long 

Function KillProcess(hwnd As Long) 
    Dim CurrentForegroundThreadID As Long 
    Dim strComputer As String 
    Dim objWMIService 
    Dim colProcessList 
    Dim objProcess 
    Dim ProcIdXL As Long 

    ProcIdXL = 0 
    CurrentForegroundThreadID = GetWindowThreadProcessId(hwnd, ProcIdXL) 

    strComputer = "." 

    Set objWMIService = GetObject _ 
    ("winmgmts:\\" & strComputer & "\root\cimv2") 
    Set colProcessList = objWMIService.ExecQuery _ 
    ("Select * from Win32_Process Where ProcessID =" & ProcIdXL) 
    For Each objProcess In colProcessList 
    objProcess.Terminate 
    Next 

End Function 


KillProcess (ExcelApplication.hwnd)