How do I get the Process ID from a created "Excel.Application" object?

Using Marshal.ReleaseComObject() or killing the Excel.exe process are ugly, error prone and unnecessary band-aids for this problem. And highly detrimental in the long run, this question shows what can happen. The proper way to do it is by calling GC.Collect(), but read this answer to understand why this tends to not work when you debug your program.

The workaround is simple, you just need to make sure that you call GC.Collect() in a different method. Which ensures that your Excel object references are no longer in scope. So the rough outline of a program that does this right would be:

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

Actually never mind; I figured it out. This is a very clean, precisely targeted solution that kills the exact process that was started. It doesn't interfere with any other process or file that the user might have open. In my experience killing the process after closing files and quitting Excel is the fastest and easiest way to deal with Excel. Here is a knowledge Base article describing the problem and Microsoft's recommended solution.

Please note that this solution does NOT kill the Excel application. It only kills the empty process shell if any pointers have not been properly disposed. Excel itself DOES actually quit when we call xlApp.quit(). This can be confirmed by trying to attach the running Excel application which will fail because Excel is not running at all.

Many people don't recommend killing the process; See How to properly clean up Excel interop objects and Understanding Garbage Collection in .net

On the other hand many people don't recommend using GC.Collect. See What's so wrong about using GC.Collect()?

Be sure to Close any open workbooks, Quit the application, Release the xlApp object. Finally check to see if the process is still alive and if so then kill it.

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

Once I realized that I could get the window handle from Excel, then I just needed the function to get the process ID from the window handle. Hence GetWindowThreadProcessId If anyone knows a vb.net way to get that I would be grateful.

Tags:

Vb.Net

Excel