10

如何从正在运行的对象中获取进程 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 中的所有资源是不可能的。我需要一个更好的选择。

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

我尝试使用Dim xProc As Process = Process.Start(ExcelPath)then usingxProc.Kill()这有时会起作用,除非使用正确的 Excel 对象XLApp = GetObject("Book1").Application或者XLApp = GetObject("", "Excel.Application")用户已经打开 Excel 窗口有点棘手。我需要一个更好的选择。

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

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

编辑:实际上我对如何让 Excel 很好地退出的重新讨论并不感兴趣。许多其他问题已经解决了这个问题。这里这里我只想杀了它;干净、准确、直接。我想杀死我开始的确切进程,而不是其他进程。

4

6 回答 6

11

其实没关系;我想到了。这是一个非常干净、目标明确的解决方案,可以杀死已启动的确切进程。它不会干扰用户可能打开的任何其他进程或文件。根据我的经验,在关闭文件并退出 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

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

于 2013-07-20T06:36:48.257 回答
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
于 2013-07-21T00:51:43.473 回答
2
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)
于 2015-11-19T10:29:54.940 回答
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.
    }
于 2014-05-15T21:36:27.563 回答
0

我在开发一个无人参与的应用程序时遇到了这个问题,该应用程序每天创建 50 到 100 个 Excel 工作簿并通过电子邮件发送它们。无论我做什么,任务管理器中都会出现一个 Excel 实例,有时甚至在 GC.WaitForPendingFinalizers() 之后。

由于该应用程序是无人值守的,我不能冒 Excel 吃掉机器的风险,所以我将所有方法组合成:

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

        Dim XLApp As Excel.Application
        Dim XLHwnd As Integer
        Dim XLProcID As Integer
        Dim XLProc As Process

        XLApp = New Excel.Application

        XLHwnd = XLApp.Hwnd
        GetWindowThreadProcessId(CType(XLHwnd, IntPtr), XLProcID)
        XLProc = Process.GetProcessById(XLProcID)

        DoStuffWithExcel()

        Try
            XLApp.Quit()
            System.Runtime.InteropServices.Marshal.ReleaseComObject(XLApp)
            XLApp = Nothing
            GC.Collect()
            GC.WaitForPendingFinalizers()

            If Not XLProc.HasExited Then
                XLProc.Kill()
            End If
        Catch ex As Exception
            XLApp = Nothing
        Finally
            GC.Collect()
        End Try

我尝试优雅地退出,但如果失败,我会终止进程,因为我必须这样做!

到目前为止,它表现得很好;-)

于 2019-03-07T19:04:39.127 回答
0

我找到了一种解决方法(VB 源代码):

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

首先 Marshal.ReleaseComObject() 所有更详细的对象并将它们设置为 Nothing。

关闭最后一个工作簿后将 Excel 窗口置于顶部,然后等待 1000 毫秒(工作机会的良好价值):

exlBook.Close()
Dim exlProcId As Integer = Nothing
GetWindowThreadProcessId(exlApp.Hwnd, exlProcId)
AppActivate(exlProcId)
Threading.Thread.Sleep(1000)
Marshal.ReleaseComObject(exlBook)
exlBook = Nothing

最后:

exlApp.Quit()
exlApp = Nothing
GC.Collect()
GC.WaitForPendingFinalizers()
GC.Collect()
GC.WaitForPendingFinalizers()

我在关闭工作簿后停止程序,然后打开Excel窗口,然后继续程序并关闭Excel进程时发现它。

亲切的问候:Thorsten,汉诺威,德国

于 2018-12-30T23:04:34.980 回答