locked
Can't kill Excel.Exe after using Excel component in vb.net

    Dotaz

  • Hi ,

    I'm using Excel.Application  generating excel report via vb.net 2003 , the problem is excel.exe process reminds even after  generated excel report closed  . I have been using  the followed code to close or dispose the process , but it does'nt  work either.

    thenks

     

    Dim procList() As Process = Process.GetProcesses()

    Dim k As Integer

    For k = 0 To procList.GetUpperBound(0) Step k + 1

        If procList(k).ProcessName = "EXCEL" Then

           procList(k).Close()

           procList(k).Dispose()

       End If

    Next

    12. prosince 2006 12:42

Odpovědi


  • When all else fails I use API function calls:

    Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Int32

    Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hwnd As Int32, ByVal wMsg As Int32, _

    ByVal wParam As Int32, ByVal lParam As Int32) As Int32

    Public Function TerminateExcel()

    Dim ClassName As String = "XLMain"

    Dim WindowHandle As Int32

    Dim ReturnVal As Int32

    Const WM_QUIT = &H12

    Do

    WindowHandle = FindWindow(ClassName, Nothing)

    If WindowHandle Then

    ReturnVal = PostMessage(WindowHandle, WM_QUIT, 0, 0)

    End If

    Loop Until WindowHandle = 0

    End Function

     

    19. prosince 2006 20:30

Všechny reakce

  • Hi,

    that is really not the supposed way of releasing a COM server ;)

    Call System.Runtime.InteropServices.Marshal.ReleaseComObject on every COM variable when you do not need that COM variable any more. Unfortunately COM wrappers do not follow the IDisposable pattern so you cannot use the using statement but have to remember to put that call in your code at the appropriate places.

    After the last COM reference is released excel.exe should go away if it was not started interactively before.

    --
    SvenC

    12. prosince 2006 12:53
  • Sadly that (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.

     

    I use this:

     

    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

    14. prosince 2006 18:51
  • Hi Swade,

    you might be right about Excel but I am not convinced by your code. Every COM wrapper variable has to be passed to ReleaseComObject. In your case you do not call ReleaseComObject on Workbook and Worksheet. Setting them to Nothing has no immediate effect as those objects are just collectable if not referenced any more, but the collection and with that the Release call on the internal COM interface pointer is only down upon garbage collection.

    Good candidates for holding references one might forget are event handlers. I don't know how the WithEvents semantics are when it comes to remove the event connections. Being explicit with AddHandler and RemoveHandler might help.

    If Excel is still not exiting you might try calling GC.Collect(). Yes, I know, this is bad practice in general, but if you really used all possible ways to release your COM "connections" to Excel and there still seems to be some object holding onto Excel which is maybe unreachable and so collectable, that might be less "rude" than killing excel.exe.

    If that all doesn't work, well, go for killing excel...

    --
    SvenC

    14. prosince 2006 21:24
  • I agree with you 100%.  I hadn't thought of releasing the other COM objects.  I was merely using the MSDN reference.  I had been adding the other stuff to get it to work (killing the process) and found that it works that way.  I'll try yours.
    18. prosince 2006 16:18
  • Still didn't work.  It may be easier using Office 2003, but Office XP just will not release without killing the proc.  Even if I do it in debug and then shut down the program (where it should drop the connection to the COM object), it still stays connected. 

    Workbook.Save()

    Workbook.Close()

    Application.Quit()

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

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

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

    Worksheet = Nothing

    Workbook = Nothing

    Application = Nothing

    GC.Collect()

    Dim proc As System.Diagnostics.Process

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

    proc.Kill()

    Next

    18. prosince 2006 16:42
  • If you have a few references to each object, then you need to call ReleaseComObject once for each reference. ReleaseComObject returns a count of the number of references remaining, so you can use a loop and keep calling it until the count <1.
    In v2.0 of the framework you get FinalReleaseComObject that does the loop for you.
    19. prosince 2006 18:02

  • When all else fails I use API function calls:

    Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Int32

    Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hwnd As Int32, ByVal wMsg As Int32, _

    ByVal wParam As Int32, ByVal lParam As Int32) As Int32

    Public Function TerminateExcel()

    Dim ClassName As String = "XLMain"

    Dim WindowHandle As Int32

    Dim ReturnVal As Int32

    Const WM_QUIT = &H12

    Do

    WindowHandle = FindWindow(ClassName, Nothing)

    If WindowHandle Then

    ReturnVal = PostMessage(WindowHandle, WM_QUIT, 0, 0)

    End If

    Loop Until WindowHandle = 0

    End Function

     

    19. prosince 2006 20:30
  • The FinalReleaseCOMObject and all forms of it do not kill the EXCEL.EXE in the windows task manager.  I've run loops, do until, etc.... and nothing. 

    Unless an actual Microsoft VB Programmer (someone who developed it for MS) comes on here and gives me the specific code that kills an instance of excel using the Marhsall.ReleaseComObject or Marshall.FinalReleaseComObject then I will continue to work under the assumption that Excel XP cannot be shut down without process.kill().

    It just doesn't release.  Period.  (For Office XP)

    19. prosince 2006 21:56
  • thank's
    20. prosince 2006 10:59
  • Use a new thread to do all the excel stuff.  Do still use the marshal stuff but once the thread quits excel will quit if you called excel.quit.
    31. prosince 2006 11:38
  •  

    Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hwnd As Int32, ByVal wMsg As Int32, _
    ByVal wParam As Int32, ByVal lParam As Int32) As Int32

    Const WM_QUIT = &H12

    Dim App As new Excel.Application

    Do Excel stuff

     PostMessage(App.Hwnd, WM_QUIT, 0, 0)

     

    Rather than terminate all instances of Excel this will terminate the instance you created in code.

     

    Denton

    • Navržen jako odpověď Ashpush 21. března 2012 16:42
    23. ledna 2007 2:25
  • Hi,

    The above method will kill the process. However if there are multiple applications or batch program running independant of eachother and each of them create their own excel object, the above method will kill all of the processes.

    Is there anyway to uniquely identify the process pertaining to your application so that we can try to find the process by its id and kill the same?

    Another way is to do the following

    objExcel.Workbooks.Close()

    objExcel.Quit()

    Marshal.ReleaseComObject(objExcel)

    objExcel = Nothing

    GC.Collect()

    GC.WaitForPendingFinalizers()

    GC.Collect()

    But using GC is a performance issue. However this can be used if it does not occur frequently, in the sense that this GC.Collect does not happen at places where the user will have frequent access.

     

     

    23. ledna 2007 10:22
  • I strongly agree with Denton! Every other approach is a waste of time and energy. This is simple and effective.

     

     Denton wrote:

     

    Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hwnd As Int32, ByVal wMsg As Int32, _
    ByVal wParam As Int32, ByVal lParam As Int32) As Int32

    Const WM_QUIT = &H12

    Dim App As new Excel.Application

    Do Excel stuff

     PostMessage(App.Hwnd, WM_QUIT, 0, 0)

     

    Rather than terminate all instances of Excel this will terminate the instance you created in code.

     

    Denton

    23. ledna 2007 15:25
  • you could try this

    Public Declare Function GetWindowThreadProcessId Lib "user32.dll" (ByVal hWnd As IntPtr, ByRef lpdwProcessId As Integer) As Integer

    Public Declare Function FindWindow Lib "user32.dll" Alias "FindWindowA" _

    (ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr

     

    Dim proc As System.Diagnostics.Process

    Dim intPID As Integer

    Dim intResult As Integer

    Dim iHandle As IntPtr

    Dim strVer As String

    Try

    objExcel = New Excel.Application

    objExcel.Caption = "test"

    objExcel.DisplayAlerts = False

    strVer = objExcel.Version

    iHandle = IntPtr.Zero

    If CInt(strVer) > 9 Then

    iHandle = New IntPtr(CType(objExcel.Parent.Hwnd, Integer))

    Else

    iHandle = FindWindow(Nothing, objExcel.Caption)

    End If

    objExcel.Workbooks.Close()

    objExcel.Quit()

    Marshal.ReleaseComObject(objExcel)

    objExcel = Nothing

    intResult = GetWindowThreadProcessId(iHandle, intPID)

    proc = System.Diagnostics.Process.GetProcessById(intPID)

    proc.Kill()

    Catch ex As Exception

    End Try

    this will kill only the associated excel.exe and not all the exe's in the task manager.

     

    Cheers

    Ganesh

    25. ledna 2007 11:39
  • If your using excel and are using the COM Class library can you provide a bit info on how your trying to close the com object down using the object model, the behaviour your seeing and some information about the machine configuration your trying this on.

    I'm trying to pinpoint if there is some issue with Interop with Office 2003 which causes an issue.

     

    27. ledna 2007 3:35
  • Hwnd property is available only for object library 10.0 onwards. for ones below 10.0 you need to use the FindWindow method of the user32.dll to obtain the window handle.

    The code is posted above my me.

    Cheers

    Ganesh

    29. ledna 2007 7:46
  • Yes. there is an issue. I get errors while trying to instantiate a 11.0 object library excel object. I get the error as "server execution failed" i have absolutely no clue what it means. It is not related to permissions as i have given permissions for ASPNET to access excel.

    Can anybody tell me what the problem couldbe?

    29. ledna 2007 7:49
  • The exact error that i get whn creating 11.0 excel object is

    System.Runtime.InteropServices.COMException

    ErrorCode : -2146959355

    Helplink : Nothing

    InnerException : Nothing

    Message : "Server execution failed"

    What should i do?

     

    30. ledna 2007 8:48
  • Hi,

     

    I am working in XP and i had real problem with closing xl from my .net application. Your code helped me very much.

    Thank you.

    7. února 2007 10:04
  •  

    Perfect!  Just what I needed.  Thank you very much!
    15. ledna 2008 19:40
  • Code Snippet

    Imports System.Runtime.InteropServicesEnd Class

    Public Class Form1
        Public ExcelApplication As Object
        Public lstExcelObject As New System.Collections.Generic.Queue(Of Process)

        <DllImport("USER32.DLL", EntryPoint:="IsWindowVisible", CharSet:=CharSet.Ansi)> _
        Private Shared Function IsWindowVisible(ByVal hwnd As Integer) As Integer
        End Function

        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
            Dim lstNewProcessID As New System.Collections.Generic.Dictionary(Of Integer, Process)
            Dim lstOldProcessID As New System.Collections.Generic.Dictionary(Of Integer, Process)

            Dim localAll As Process()                                          
     
            localAll = Process.GetProcesses()
     

            For i As Integer = 0 To localAll.Length - 1
     
                If localAll(i).ProcessName.ToUpper = "EXCEL" Then
                    lstOldProcessID.Add(localAll(i).Id, localAll(i))
                End If
            Next

            ExcelApplication = CreateObject("Excel.Application")

            Dim newProcess As Process = Nothing
            localAll = Process.GetProcesses()
            For i As Integer = 0 To localAll.Length - 1
     
                If localAll(i).ProcessName.ToUpper = "EXCEL" AndAlso Not lstOldProcessID.ContainsKey(localAll(i).Id) Then
                    newProcess = localAll(i)
                    Exit For
                End If
            Next

            lstExcelObject.Enqueue(newProcess)

            ExcelApplication.Visible = True

            ExcelApplication.workbooks.add()

            ReleaseComObject(ExcelApplication)
            GC.Collect()

            StartExcelWatcher()
        End Sub

        Public Sub ReleaseComObject(ByRef objComObject As Object)
            Dim count As Integer = System.Runtime.InteropServices.Marshal.ReleaseComObject(objComObject)
            For i As Integer = count To 0 Step -1
                System.Runtime.InteropServices.Marshal.ReleaseComObject(objComObject)
            Next

            objComObject = Nothing
        End Sub

        Public Sub StartExcelWatcher()
            Dim objThread As System.Threading.Thread = New Threading.Thread(AddressOf ExcelWatcher)
            objThread.Start()
        End Sub

        Public Sub ExcelWatcher()
            If lstExcelObject.Count > 0 Then
                Dim objExcelApp As Process = lstExcelObject.Dequeue()
                Try
                    Do
                        If objExcelApp Is Nothing OrElse objExcelApp.HasExited Then
                            Exit Do
                        ElseIf IsWindowVisible(objExcelApp.MainWindowHandle) = 0 Then
                            objExcelApp.Kill()
                            Exit Do
                        End If

                        Threading.Thread.Sleep(2000)
                    Loop
                Catch ex As Exception

                End Try
                objExcelApp = Nothing
                GC.Collect()
            End If
        End Sub


    This is a sample ,I have writed it to close the Excel Process.
    Test it ,Hope that it can help to some extent to you:)
    29. ledna 2008 1:46
  • Hi i have big trouble to close excel file this peice or code work just fine in most cases when you use Excel 2003 wiht proper dlls


    C Dim i As Integer = 0

                For i = 1 To xlWrkBook.Sheets.Count
                    xlWrkSheet = xlWrkBook.Sheets(i)
                    System.Runtime.InteropServices.Marshal.ReleaseComObject(xlWrkSheet)
                    xlWrkSheet = Nothing
                Next
                For i = 1 To xlApp.Workbooks.Count
                    xlWrkBook = xlApp.Workbooks(i)
                    xlWrkBook.Close(False)
                    System.Runtime.InteropServices.Marshal.ReleaseComObject(xlWrkBook)
                    xlWrkBook = Nothing
                Next

                xlApp.Quit()
                System.Threading.Thread.Sleep(4000)
                System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp)
                xlApp = Nothing
                System.Threading.Thread.CurrentThread.CurrentCulture = oldCI

                GC.Collect()
                GC.WaitForPendingFinalizers()ode Snippet

     but I don't have any success with 2007 I don't know why , it is easy to kill the process but i still have problem to find out which is the proper process to kill , it is not good to kill all excel process on server , so I have to retrieve tre process id when the excel application object is generated  but i find ity difficult  i don't open excel in open window state otherwise it si easy to determine the right process, so please if tou have any solution help .Closing excel cause me so much pain  try several thing but there are still some problems .

    http://www.thescarms.com/dotNet/ExcelObject.aspx

    http://www.devcity.net/PrintArticle.aspx?ArticleID=239
    this links i find helpful check them out you

    17. března 2008 13:35

  • For a great answer, read SWADE's post..

    I was lost until I saw the Light from Swade's VB Lantern....

    ahhh a pretty lighthouse in the distance...

    BUt seriously... worked great... I was so close but nowhere near the KILL Processes FOR loop..


    Dim proc As System.Diagnostics.Process

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



    Thanks,
    DAVID
    10. dubna 2008 8:00
  • All due respect to swade's solution, denton's answer is the one I've been searching for for a week and works like a charm.

     

    Now I've gotta go learn how dangit???

     

    From one happy Excel killa!!

     

    10. dubna 2008 23:20
  • 50 Ways to Kill Excel
    http://www.devcity.net/Articles/239/1/article.aspx
    by Scott Rutherford
    Published on 6/14/2006
    16. března 2009 13:52

  • Calling System.Runtime.InteropServices.Marshal.ReleaseComObject() does not release the app from the memory. What you need to do is open the workbook using GetObject().

    Please check out the link.

    http://social.msdn.microsoft.com/Forums/en-US/innovateonoffice/thread/65f9721f-e1a1-4757-8273-a77f526eee49


    Hope this helps...
    4. srpna 2009 7:12
  • You can try this if you want kill one explicit window....


    ---->


     Microsoft.Office.Interop.Excel.Application aplicacion = null;
     aplicacion = (Microsoft.Office.Interop.Excel.Application)new Microsoft.Office.Interop.Excel.ApplicationClass();

                    wBook = aplicacion.Workbooks.Open(excelfile,
                         Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing,
                         Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);
            
    // write your code to modify your workbook...

    wBook.Save();
    wBook.Close();

    IntPtr pointer=new IntPtr(aplicacion.Hwnd);

    //killing this unique process (DON´T WORRY IF YOU HAVE OTHERS EXCELS PROCESS OPEN)

    aplicacion.Visible = true;

                foreach (System.Diagnostics.Process proc in System.Diagnostics.Process.GetProcessesByName("Excel"))
                {
                    if (proc.MainWindowHandle == pointer)
                        proc.Kill();
                }
    27. srpna 2009 11:25
  • Denton's answer is the one I used. works well I did have to put objApp.Quit() before PostMessage. 

    objApp.Quit()
    PostMessage(objApp.Hwnd, WM_QUIT, 0, 0)

    A geek at heart
    5. února 2010 17:44
  • Hi,

      I am using excel com compoent to read the content in excel and close the same. Initially i use to iterate the sheets name from the excel sheet and show it in my combobox. Users will pick the sheet name and import the contents to the database. Everything works fine regarding the data. The problem comes when i release the object.

    The Excel object that i open during import is closed but the object while fetching the sheets is not closed.One Excel.exe object is still open and i could locate this in the task manager.

    When i do the above process for the second time, both the excel.exe objects that i opened  are closed now. But still the previously opened object is not closed. How can i close this object.

          ExcelWB.Close()
          ExcelAPP.Quit()
          ReleaseObject(ExcelAPP)
          ReleaseObject(ExcelWB)
          ReleaseObject(ExcelWS)
    
    Private Sub ReleaseObject(ByVal obj As Object)
        Try
          System.Runtime.InteropServices.Marshal.ReleaseComObject(obj)
          obj = Nothing
        Catch ex As Exception
          MessageBox.Show(ex.ToString())
        Finally
          obj = Nothing
          GC.Collect()
        End Try
      End Sub

    The above code is what iam using to release the object.

    the code "ReleaseObject(ExcelWS)" will not be used while binding the combobox as i will not be opening the  Worksheets.

    19. července 2010 6:03
  • Surenyfs,

    I may not be fully understanding the overall design of your app, but it seems to me as if you could use the same Excel object for both enumerating the worksheets (populating combobox) and pushing data from the chosen worksheet to your external database. From your description it seems like you are using the same Excel workbook for check your sheet names and for the source of data to be pushed out to your database, so, you don't have to establish that 2nd Excel object. Use the first one, and clean it up after you're done with the data transfer. Your procedure for taking care of Excel cleanup looks good to me. I successfully use a routine that's similar to yours (I'm pulld data into an Excel file and saving it) - see excerpts below:

    Imports System.Runtime.InteropServices.Marshal

    Imports Microsoft.Office.Interop

    ...

    ...

     

        Function StartExcel(Optional ByVal IsVisible As Boolean = True) As Excel.Application

            Try

                Dim objExcel As New Microsoft.Office.Interop.Excel.ApplicationClass

                If Not objExcel.Ready Then

                    System.Threading.Thread.Sleep(cmTimeoutForGettingFileServer)

                End If

                objExcel.Visible = IsVisible

                If Not objExcel.Ready Then

                    System.Threading.Thread.Sleep(cmTimeoutForGettingFileServer)

                End If

                Return objExcel

            Catch exe As ApplicationException

                blnError = True

                My.Application.Log.WriteEntry("Error during StartExcel: " & exe.Message, TraceEventType.Critical)

                Return Nothing

            Catch ex As Exception

                blnError = True

                My.Application.Log.WriteEntry("Error during StartExcel: " & ex.Message, TraceEventType.Critical)

                Return Nothing

            End Try

        End Function

     

        Sub ForceExcelToQuit(ByVal objExcel As Excel.Application)

            Try

                If Not objExcel Is Nothing Then

                    objExcel.Quit()

                    ReleaseComObject(objExcel)

                End If

            Catch exe As ApplicationException

                blnError = True

                My.Application.Log.WriteEntry("Error during ForceExcelToQuit: " & exe.Message, TraceEventType.Warning)

            Catch ex As Exception

                blnError = True

                My.Application.Log.WriteEntry("Error during ForceExcelToQuit : " & ex.Message, TraceEventType.Warning)

            Finally

                objExcel = Nothing

                GC.Collect()

            End Try

        End Sub

     

     

    ...

    ...

     

     

    objExcel = StartExcel(blnIsVisible)

            Try

                strFileName = cmTemplateLocation

                objWorkbook = objExcel.Workbooks.Add(strFileName)

                objSheet = objWorkbook.Sheets(1)

     

                DataTableToExcelSheet(dt, objSheet, 2, 1, 1, MDT, NumberOfWeekdays)

     

                If Not blnIsVisible Then

                    If strSaveFilename <> "" Then

                        objWorkbook.SaveAs(strSaveFilename, Excel.XlFileFormat.xlWorkbookDefault, cmDestinationPassword)

                        If Not objWorkbook.Saved Then

                            Throw New Exception("Unable to save the resulting Excel file: " & strSaveFilename & ".")

                        End If

                    End If

                    objSheet = Nothing

                    objWorkbook.Close()

                    objWorkbook = Nothing

     

                    ForceExcelToQuit(objExcel)

                End If

            Catch exd As DataException

                blnError = True

                If blnIsVisible Then MsgBox(exd.ToString, MsgBoxStyle.Exclamation, "Error populating workbook. Data issue.")

                ForceExcelToQuit(objExcel)

            Catch ex As Exception

                blnError = True

                If blnIsVisible Then MsgBox(ex.ToString, MsgBoxStyle.Exclamation, "Error populating workbook")

                ForceExcelToQuit(objExcel)

            End Try

     


    Dominik Ras mintol1@poczta.onet.pl
    • Navržen jako odpověď Dominik Ras 31. srpna 2011 18:04
    23. července 2010 20:33
  • So Great Swade!! thanks alot..... this works fine for me.. i use win2003 server and the last loop solves the problem..

    thanks again

    4. listopadu 2010 9:30
  • Excellent !!!

    I have been looking for many days.

     

    Dim proc As System.Diagnostics.Process

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

    This work for me.

    29. září 2011 10:31
  • We can use the below code for kill the current excel process, It avoids kill all the excel applications.

     I hope it will be useful.

     

    internal void closeApplication()

    {

     

    if (app != null && !app.Visible)

    {

     

    if (app != null)

    {

    app.Quit();

    app =

    null;

    }

    System.Diagnostics.

    Process[] Processes;

    Processes = System.Diagnostics.

    Process.GetProcessesByName("EXCEL");

     

    foreach (System.Diagnostics.Process p in Processes)

    {

     

    if (p.MainWindowTitle.Trim() == "")

    p.Kill();

    }

    }

    }

     

     
    • Navržen jako odpověď Naunt_On 25. října 2011 10:21
    30. září 2011 11:12
  •  

     

    i would recomend to take a look at this

     

    http://www.codeproject.com/KB/cs/ExcelAutomation.aspx

     

    It works

    10. října 2011 21:43
  • For Each proc In System.Diagnostics.Process.GetProcessesByName("EXCEL")
                        If proc.MainWindowTitle.Trim() = "" Then
                            proc.Kill()
                        End If
    Next
    

    Thanks, this works really fine on me. :)
    25. října 2011 10:24
  • I have the issue mentioned above where I get two EXCEL.EXE items in the process list.

    I use the following (just going by memory on the exact method names):

    Dim ExcelApp as Object = CreateObject("Excel.Application")
    Dim ExcelWB as Object = ExcelApp.OpenWorkbook(...)
    Dim xlsSheet as Object = ExcelWB.Sheets(index)
    

    I reassign xlsSheet to access all of the sheets as needed

    Problems/Questions:

    1. I put everything in a class that implements IDisposable so that if my application crashes the COM object should get properly released but the EXCEL.EXE processes don't disappear until I close the VB.NET DLL dialog and then close the host application which is AutoCAD (VB.NET DLL running under AutoCAD). I haven't yet tried the Marshal.ReleaseCOMOBject suggestions above.
    2. What is the exact reason for two EXCEL.EXE processes?
    3. I don't reference Excel at all in my code as a project reference, everything is just Object. In my VBA days I found that referencing a specific version of Excel on my dev machine didn't translate well when the end-user had a different version of Excel on their machine. Perhaps I don't need to worry about that anymore and use code similar to Dominik's in one of the above posts? Perhaps performance would improve without the late binding as well? Advice on that would be appreciated.
    4. If all you are doing is reading values from an Excel spreadsheet, it would be nice if Microsoft released a non-COM .NET interface for that, independent of EXCEL.EXE being on the machine. Is nothing like that available?

    Thanks,

    Greg

    • Upravený GTVic 21. listopadu 2011 19:06
    21. listopadu 2011 19:03
  •   So, I figured while this question is quite old, the problem is fresh even today I might as well throw in another little tidbit of information that seems overlooked quite often. 

     

       Often when I'm working with Excel from VB.Net I'm simply getting some info and closing it... And, like any good little code-monkey when I have a situation like that, I stick all of it in a separate function and simply return the info I need... So what can go wrong here? Simply put, I could return a value and exit my function UNINTENTIONALLY and thus never get to the goodie bits of dropping excel and quitting it. 

     

       Now, I did see the app.kill loop on this thread... That works great as long as (someone else mentioned) you don't need to one of the instances of excel already open. It is poor practice to assume no one would ever have another instance of excel open while running your app, in fact you could cost a co-worker many frustrating hours trying to redo something they had done when you inadvertently killed their instance... I suggest against this if anyone else will be using your program. Instead, try this first... Here is some sample code to return a value from excel... 

     

            Dim xlApp As New Excel.Application
            Dim xlWB As Excel.Workbook = xlApp.Workbooks.Open(My.Settings.xlPath.ToString, False, True)
            Dim xlWs As Excel.Worksheet = DirectCast(xlWB.Sheets("Master List"), Excel.Worksheet)
            xlApp.Visible = False
    
            Return xlWs.Range("M1").Value
    
            NAR(xlWs)
            xlWB.Close(False)
            NAR(xlWB)
            xlApp.Quit()
            NAR(xlApp)
    
    
    
            GC.Collect()
            GC.WaitForPendingFinalizers()
    
    


      Note the 'Return xlws.range("M2").value' statement in the middle of the code... This indicates I'm working with a function and returning that value... quite slick code, not assigning a variable, saving memory... hmmm, but actually once .net hits that return line it leaves the function and goes back to your main calling routine... This is what is leaving excel open at this point and nothing you change afterwards will do much of anything... You could spend hours making loops, tests, and debugging to find that were it not for that little line, this could have all been averted. 

     

       "Note: This can happen in routines where you are not just returning a single value, as well. Perhaps you have a loop with a check and once all boxes are populated you dump out of that routine. Finding places where you are letting a routine dump out early are the key to solving this delimma.

      So here is some code, inserting a variable, assigning it, and then not returning the value until I have fully dumped out of excel...

     

            Dim xlApp As New Excel.Application
            Dim xlWB As Excel.Workbook = xlApp.Workbooks.Open(My.Settings.xlPath.ToString, False, True)
            Dim xlWs As Excel.Worksheet = DirectCast(xlWB.Sheets("Master List"), Excel.Worksheet)
            xlApp.Visible = False
    
            '''inserting a variable
            Dim res As Integer
    
            res = xlWs.Range("M1").Value
    
            NAR(xlWs)
            xlWB.Close(False)
            NAR(xlWB)
            xlApp.Quit()
            NAR(xlApp)
    
    
    
            GC.Collect()
            GC.WaitForPendingFinalizers()
    
            '''ahhh excel has closed and I can return my variable
            Return res
    

    Hope this helps... And lastly, for anyone wondering about the "NAR" routine, that is code taken from MSDN help on the issue... here is the routine for that...

     

        Private Sub NAR(ByVal o As Object)
            Try
                System.Runtime.InteropServices.Marshal.ReleaseComObject(o)
            Catch ex As Exception
            Finally
                o = Nothing
            End Try
        End Sub
    

       This is basically saving you from retyping sys....runtim....intero... etc. etc. etc... and catching any failures to turn them to nothing... 

    15. prosince 2011 5:15
  • This very interesting and long running discussion caught my attention and has solved my seemingly insoluble problem. I do not know very much about Visual Basic but have used VBA for many years. A set of routines I had written in VBA seemed to me to be useful to a wider audience so I set to, to learn enough VB (using express edition 2010) to produce an executable file in VB. The process is 3 stage.  1  Create an Excel workbook and populate sheet3 with some data (a default set of Accounts codes). 2. User customises this data which forms the essential element in the final product. 3 Programme produces a twelve sheet workbook carrying a special interface on one sheet which has 14 buttons to activate a set of macros in VBA contained in 4 modules. End result – a virtually automated process with applicability in many environments. Total lines of VB code 1225. 5 user forms, four text files for vba codes, 2 readme files, and a partridge in a pear tree.

    The problem was that I had to exit the application at the end of stage 1 and stage 2 to avoid being left with an instance of Excel running in the background unseen which might  pose a threat to an otherwise safe environment. I wrote a routine using GetObject that would identify close and release any other instance of Excel but it would not close the remnants of my programme though it could identify their presence.

    I tried all that was within my limited knowledge sprinkling ReleaseComObjects, and GCs at every conceivable point where exits, closures or exceptions occurred but all to no avail. Then I lighted upon this post.

    I have tried Denton’s solution and LO! It works. Like Michael1569 I put the App Quit in front of it. I cannot pretend to understand it all, I am too long in the tooth for that but I am impressed with and grateful for the forums and the co-operative spirit they engender.

    MARGAVEN

    17. prosince 2011 2:09
  • try to Close and dipose ur excel workbook object this will work

    excelBook.Close()
    excelBook = Nothing
    excelApp = Nothing


    A.Venkatesan

    14. února 2012 10:40
  • This indeed works!

    Thank you!

    21. března 2012 16:42
  • if (p.MainWindowTitle.Trim() ==""

    p.Kill();

    This works like charm...


    --Amos

    22. března 2012 6:20
  • Hi,

    you can also try this C# / VB.NET Excel component that doesn't use Excel Interop so there is no need to for Excel application and need for killing it.

    Here is a sample VB.NET code:

    Dim ef As New ExcelFile
     
    ' Loads Excel file.
    ef.LoadXls("filename.xls")
     
    ' Selects first worksheet.
    Dim ws As ExcelWorksheet = ef.Worksheets(0)
     
    ' Displays the value of first cell in the messageBox.
    MessageBox.Show(ws.Cells("A1").Value.ToString())

    20. dubna 2012 9:04
  • Work great!!!!!!

    Thanks

    29. července 2012 13:34
  • I got similar problem and dont know how to find the good way. I try to explain mayby some could help:

    I have a lot automated excel by vb.net. But i got a big problem with one of my projects which is working with two workbooks. Anyway the EXCEL.EXE staying in taskmanager. This is my code i am using:

    Imports Excel = Microsoft.Office.Interop.Excel
    
    Module Main
    
        'Create Excel objects
        Public objApp As New Excel.Application
    
        Public WorkDirectory = "C:\Users\user\Documents\Visual Studio 2005\Projects\Working with Excels\"
    
        Sub Main()
            Try
                With objApp
    
    .Workbooks.Open(Filename:=Local & "MyMain.xls")
    .Workbooks.Open(Filename:=Local & "Other.xls")
    ..........
    ..........
    'operations on two workbooks...
    ..........
    ..........
    
    .Workbooks("Other.xls").Close(True)
    .Workbooks("MyMain.xls").Close(True)
    
    End With
    
            Catch ex As Exception
                Console.WriteLine("Error occurs: " & ex.ToString)
            Finally
    
                objApp.Quit()
                NAR(objApp)
                GC.Collect()
                GC.WaitForPendingFinalizers()
    
            End Try
    
        End Sub
    
    End Module
    
    ------------------------------------------
    
    Module RealaseExcelObjects
    
        Public Sub NAR(ByVal o As Object)
            Try
                System.Runtime.InteropServices.Marshal.ReleaseComObject(o)
            Catch
            Finally
                o = Nothing
            End Try
        End Sub
    
    End Module

    Anyway after that i have still the EXCEL.EXE in processes.

    I found out also this topic, mayby it could be helpfull in my case but i dont know how to implement it in my code:

    http://social.msdn.microsoft.com/Forums/en-US/innovateonoffice/thread/65f9721f-e1a1-4757-8273-a77f526eee49?prof=required

    Could you please help me, what to do?

    12. listopadu 2012 12:59
  • I am no expert in this field - but I laboured greatly to overcome this problem. Are you sure that every possible exit from the application is suitably catered for with release of object. My programme has a number of exit points plus of course those which take place when an error occurs and which hopefully you will have covered by try catch procedures. I put the following on all my exit points and on every try catch as well.

     ' objexcel.ActiveWorkbook.Close(SaveChanges:=False)

     ' objexcel.Quit()

     ' objexcel = Nothing

     ' Me.Close()

    This seems to work, for all forseeable events and I don't need GC. However if an unforseen error occurs that is not caught in a try catch the Excel application will almost certainly still be running in the background not visible. I found that the only way to then get rid of it is to power down and start again. Don't forget to save everything first. In order to easily see whether this state existed I wrote this routine which is handy to use when trying to find out what the state of affairs is at any one time. You need just one fomr with two command buttons.

    'Public Class Form1

    '    Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hwnd As 'Int32, ByVal wMsg As Int32, _

    '  ByVal wParam As Int32, ByVal lParam As Int32) As Int32
    '

    '   Const WM_QUIT = &H12

    '    Public Sub Button1_Click(ByVal sender As System.Object, ByVal e As 'System.EventArgs) Handles Button1.Click

    '        Dim errnum As Integer

     '       ' Test to see if a copy of Excel is already running.

     '       ' Private Sub testExcelRunning()

     '       On Error Resume Next

     '       ' GetObject called without the first argument returns a

     '       ' reference to an instance of the application. If the

     '       ' application is not already running, an error occurs.

     '       Err.Clear()

    '

    '        Dim excelObj As Object = GetObject(, "Excel.Application")

    '        If Err.Number = 0 Then

    '            errnum = Err.Number

    '            MsgBox("1. Excel is running. Errnum is " & errnum)

     '           excelObj.Quit()

     '           PostMessage(excelObj.Hwnd, WM_QUIT, 0, 0)

     '           ReleaseComObject(excelObj)

    '

     '           GC.WaitForPendingFinalizers()

     '           GC.Collect()

     '           Err.Clear()

    '

     '       Else

     '           errnum = Err.Number

     '           MsgBox("2. Excel is not running. Errnum is " & errnum)

     '       End If

     '       Err.Clear()

     '       ReleaseComObject(excelObj)

     '       excelObj = Nothing

     '   End Sub

     '   Friend Sub ReleaseComObject(ByVal o As Object)

     '       Try

     '           System.Runtime.InteropServices.Marshal.FinalReleaseComObject(o)

     '       Catch

     '       Finally

     '           If o IsNot Nothing Then o = Nothing

     '       End Try

     '       MsgBox("This sub was called")

     '   End Sub

    '    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As 'System.EventArgs) Handles Button2.Click

    '        Me.Close()

    '    End Sub

    I also put in a routine to test for any Excel running when my program starts because it doesn’t like the presence of a parallel application, and to close it if it finds one before running.

    Hope this may be of some help 

    Avenmarg


    <//span>

    2. prosince 2012 22:05