Excel interop not closing

Simple rule: avoid using double-dot-calling expressions, such as this:

var workbook = excel.Workbooks.Open(/*params*/)

…because in this way you create RCW objects not only for workbook, but for Workbooks, and you should release it too (which is not possible if a reference to the object is not maintained).

So, the right way will be:

var workbooks = excel.Workbooks;
var workbook = workbooks.Open(/*params*/)

//business logic here

Marshal.ReleaseComObject(workbook);
Marshal.ReleaseComObject(workbooks);
Marshal.ReleaseComObject(excel);

CJBS's user avatar

CJBS

15k6 gold badges86 silver badges135 bronze badges

answered Jun 28, 2013 at 14:50

Dzmitry Martavoi's user avatar

Dzmitry MartavoiDzmitry Martavoi

6,7916 gold badges38 silver badges57 bronze badges

6

Here is a snippet of code I wrote, because I had the same problem as you. Basically, you need to close the workbook, quit the application, and then release ALL of your COM objects (not just the Excel Application object). Finally, call the garbage collector for good measure.

    /// <summary>
    /// Disposes the current <see cref="ExcelGraph" /> object and cleans up any resources.
    /// </summary>
    public void Dispose()
    {
        // Cleanup
        xWorkbook.Close(false);
        xApp.Quit();

        // Manual disposal because of COM
        while (Marshal.ReleaseComObject(xApp) != 0) { }
        while (Marshal.ReleaseComObject(xWorkbook) != 0) { }
        while (Marshal.ReleaseComObject(xWorksheets) != 0) { }
        while (Marshal.ReleaseComObject(xWorksheet) != 0) { }
        while (Marshal.ReleaseComObject(xCharts) != 0) { }
        while (Marshal.ReleaseComObject(xMyChart) != 0) { }
        while (Marshal.ReleaseComObject(xGraph) != 0) { }
        while (Marshal.ReleaseComObject(xSeriesColl) != 0) { }
        while (Marshal.ReleaseComObject(xSeries) != 0) { }
        xApp = null;
        xWorkbook = null;
        xWorksheets = null;
        xWorksheet = null;
        xCharts = null;
        xMyChart = null;
        xGraph = null;
        xSeriesColl = null;
        xSeries = null;

        GC.Collect();
        GC.WaitForPendingFinalizers();
    }

answered Jun 28, 2013 at 14:50

qJake's user avatar

3

Rules — never use no more that one dot

— one dot

var range = ((Range)xlWorksheet.Cells[rowIndex, setColumn]);
var hyperLinks = range.Hyperlinks;
hyperLinks.Add(range, data);

— Two or more dots

 (Range)xlWorksheet.Cells[rowIndex, setColumn]).Hyperlinks.Add(range, data);

— Example

 using Microsoft.Office.Interop.Excel;

 Application xls = null;
 Workbooks workBooks = null;
 Workbook workBook = null;
 Sheets sheets = null;
 Worksheet workSheet1 = null;
 Worksheet workSheet2 = null;

 workBooks = xls.Workbooks;
 workBook = workBooks.Open(workSpaceFile);
 sheets = workBook.Worksheets;
 workSheet1 = (Worksheet)sheets[1];


// removing from Memory
 if (xls != null)
 {    
   foreach (Microsoft.Office.Interop.Excel.Worksheet sheet in sheets)
   {
      ReleaseObject(sheet);
   }

   ReleaseObject(sheets);
   workBook.Close();
   ReleaseObject(workBook);
   ReleaseObject(workBooks);

   xls.Application.Quit(); // THIS IS WHAT IS CAUSES EXCEL TO CLOSE
   xls.Quit();
   ReleaseObject(xls);

   sheets = null;
   workBook = null;
   workBooks = null;
   xls = null;

   GC.Collect();
   GC.WaitForPendingFinalizers();
   GC.Collect();
   GC.WaitForPendingFinalizers();
}

SztupY's user avatar

SztupY

10.2k8 gold badges67 silver badges86 bronze badges

answered Jan 9, 2014 at 15:44

Lawrence Thurman's user avatar

It is tricky to get rid of all references since you have to guess if calls like:

var workbook = excel.Workbooks.Open("")

Creates an instance of Workbooks that you do not hold a reference to.

Even references like:

targetRange.Columns.AutoFit()

Will create an instance of .Columns() without you knowing and not released properly.

I ended up writing a class holding a list of object references that could dispose all objects in reverse order.

The class has a list of objects and Add() functions for anything you reference as you use Excel interop that returns the object itself:

    public List<Object> _interopObjectList = new List<Object>();

    public Excel.Application add(Excel.Application obj)
    {
        _interopObjectList.Add(obj);
        return obj;
    }

    public Excel.Range add(Excel.Range obj)
    {
        _interopObjectList.Add(obj);
        return obj;
    }

    public Excel.Workbook add(Excel.Workbook obj)
    {
        _interopObjectList.Add(obj);
        return obj;
    }

    public Excel.Worksheet add(Excel.Worksheet obj)
    {
        _interopObjectList.Add(obj);
        return obj;
    }

    public Excel.Worksheets add(Excel.Worksheets obj)
    {
        _interopObjectList.Add(obj);
        return obj;
    }

    public Excel.Sheets add(Excel.Sheets obj)
    {
        _interopObjectList.Add(obj);
        return obj;
    }


    public Excel.Workbooks add(Excel.Workbooks obj)
    {
        _interopObjectList.Add(obj);
        return obj;
    }

Then to unregister objects I used the following code:

    //Release all registered interop objects in reverse order
    public void unregister()
    {
        //Loop object list in reverse order and release Office object
        for (int i=_interopObjectList.Count-1; i>=0 ; i -= 1)
        { ReleaseComObject(_interopObjectList[i]); }

        //Clear object list
        _interopObjectList.Clear();
    }


    /// <summary>
    /// Release a com interop object 
    /// </summary>
    /// <param name="obj"></param>
     public static void ReleaseComObject(object obj)
     {
         if (obj != null && InteropServices.Marshal.IsComObject(obj))
             try
             {
                 InteropServices.Marshal.FinalReleaseComObject(obj);
             }
             catch { }
             finally
             {
                 obj = null;
             }

         GC.Collect();
         GC.WaitForPendingFinalizers();
         GC.Collect();
         GC.WaitForPendingFinalizers();
     }

Then principle is to create the class and capture references like this:

//Create helper class
xlsHandler xlObj = new xlsHandler();

..

//Sample - Capture reference to excel application
Excel.Application _excelApp = xlObj.add(new Excel.Application());

..
//Sample - Call .Autofit() on a cell range and capture reference to .Columns() 
xlObj.add(_targetCell.Columns).AutoFit();

..

//Release all objects collected by helper class
xlObj.unregister();

Not perhaps code of great beauty but may inspire to something useful.

umlcat's user avatar

umlcat

4,0513 gold badges19 silver badges28 bronze badges

answered Nov 27, 2014 at 18:02

flodis's user avatar

flodisflodis

1,10312 silver badges9 bronze badges

In your code you have:

excel.Workbooks.Open(...)

excel.Workbooks is creating a COM object. You are then calling the Open function from that COM object. You are not, however, releasing the COM object when you have finished.

This is a common issue when dealing with COM objects. Basically, you should never have more than one dot in your expression because you will need to clean up the COM objects when you’ve finished.

The topic is simply too big to explore completely in an answer, but I think you’ll find Jake Ginnivan’s article on the subject extremely helpful: VSTO and COM Interop

If you get tired of all those ReleaseComObject calls, you may find this question helpful:
How to properly clean up Excel interop object in C#, 2012 edition

Community's user avatar

answered Jun 28, 2013 at 14:49

JDB's user avatar

JDBJDB

24.9k5 gold badges74 silver badges121 bronze badges

As stated in other answers, using two dots will create hidden references that cannot be closed by Marshal.FinalReleaseComObject. I just wanted to share my solution, which eliminates the need to remember Marshal.FinalReleaseComObject — it’s really easy to miss, and a pain to locate the culprit.

I use a generic IDisposable wrapper class which can be used on any COM object. It works like a charm, and it keeps everything nice and clean. I can even reuse private fields (e.g. this.worksheet). It also auto-releases the object when something throws an error, due to the nature of IDisposable (the Dispose method runs as a finally).

using Microsoft.Office.Interop.Excel;

public class ExcelService
{
    private _Worksheet worksheet;

    private class ComObject<TType> : IDisposable
    {
        public TType Instance { get; set; }

        public ComObject(TType instance)
        {
            this.Instance = instance;
        }

        public void Dispose()
        {
            System.Runtime.InteropServices.Marshal.FinalReleaseComObject(this.Instance);
        }
    }

    public void CreateExcelFile(string fullFilePath)
    {
        using (var comApplication = new ComObject<Application>(new Application()))
        {
            var excelInstance = comApplication.Instance;
            excelInstance.Visible = false;
            excelInstance.DisplayAlerts = false;

            try
            {
                using (var workbooks = new ComObject<Workbooks>(excelInstance.Workbooks))
                using (var workbook = new ComObject<_Workbook>(workbooks.Instance.Add()))
                using (var comSheets = new ComObject<Sheets>(workbook.Instance.Sheets))
                {
                    using (var comSheet = new ComObject<_Worksheet>(comSheets.Instance["Sheet1"]))
                    {
                        this.worksheet = comSheet.Instance;
                        this.worksheet.Name = "Action";
                        this.worksheet.Visible = XlSheetVisibility.xlSheetHidden;
                    }

                    using (var comSheet = new ComObject<_Worksheet>(comSheets.Instance["Sheet2"]))
                    {
                        this.worksheet = comSheet.Instance;
                        this.worksheet.Name = "Status";
                        this.worksheet.Visible = XlSheetVisibility.xlSheetHidden;
                    }

                    using (var comSheet = new ComObject<_Worksheet>(comSheets.Instance["Sheet3"]))
                    {
                        this.worksheet = comSheet.Instance;
                        this.worksheet.Name = "ItemPrices";
                        this.worksheet.Activate();

                        using (var comRange = new ComObject<Range>(this.worksheet.Range["A4"]))
                        using (var comWindow = new ComObject<Window>(excelInstance.ActiveWindow))
                        {
                            comRange.Instance.Select();
                            comWindow.Instance.FreezePanes = true;
                        }
                    }

                    if (this.fullFilePath != null)
                    {
                        var currentWorkbook = (workbook.Instance as _Workbook);
                        currentWorkbook.SaveAs(this.fullFilePath, XlFileFormat.xlWorkbookNormal);
                        currentWorkbook.Close(false);
                    }
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.WriteLine(ex.Message);
                throw;
            }
            finally
            {
                // Close Excel instance
                excelInstance.Quit();
            }
        }
    }
}

answered Jan 13, 2017 at 12:39

Riegardt Steyn's user avatar

Riegardt SteynRiegardt Steyn

5,2912 gold badges34 silver badges49 bronze badges

Alternatively, you can kill the Excel process as explained here.

First, import SendMessage function:

[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);

Then, send the WM_CLOSE message to the main window:

SendMessage((IntPtr)excel.Hwnd, 0x10, IntPtr.Zero, IntPtr.Zero);

answered Apr 12, 2016 at 11:01

Kevin Vuilleumier's user avatar

Cannot close Excel.exe after Interop process

Don’t make this too complicated!!
Just create a simple method and call that method as
follows :

// to kill the EXCELsheet file process from process Bar
private void KillSpecificExcelFileProcess() {
  foreach (Process clsProcess in Process.GetProcesses())
    if (clsProcess.ProcessName.Equals("EXCEL"))  //Process Excel?
      clsProcess.Kill();
    }

Mate Mrše's user avatar

Mate Mrše

7,85010 gold badges39 silver badges75 bronze badges

answered Apr 24, 2019 at 7:30

Jarus Rev's user avatar

1

@Denis Molodtsov in an attempt to be helpful suggested killing all processes named ‘EXCEL’. That seems to be asking for trouble. There are already many answers that describe ways to get the process to stop after the call to excel.quit() by playing nice with COM interop. This is best if you can make it work.

@Kevin Vuilleumier had a great suggestion to send WM_CLOSE to the Excel window. I plan to test this.

If for some reason you need to kill an Excel App Object’s Excel process, you can target it specifically using something like this:

  using System.Diagnostics;
  using System.Runtime.InteropServices;

// . . .

    [DllImport("user32.dll", SetLastError=true)]
    public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId);

// . . .

    uint excelAppPid;
    uint tid = GetWindowThreadProcessId(excel.Hwnd, out excelAppPid);

    if (tid)
    {
      Process excelAppProc = Process.GetProcessById($excelPid);
      if (excelAppProc)
      {
        excelAppProc.Kill();
      }
    }

I don’t have time to fully test in C#, but I ran a quick test in Powershell where I’m having a problem with Excel not terminating and this approach works.

It’s pretty straightforward. Excel App object’s Hwnd property is the Excel process’s hidden window handle. Pass excel.Hwnd to GetWindowThreadProcessId to get the process ID. Use that to open the process, finally invoke Kill().

At least we’re sure we’re killing the right process. Well, pretty sure. If the Excel process already terminated normally, it’s process ID could be reused by a new process. To limit this possibility, it’s important not to wait between calling excel.quit() and attempting to kill.

answered Oct 10, 2018 at 21:12

jimhark's user avatar

jimharkjimhark

4,9281 gold badge26 silver badges27 bronze badges

In case you are desperate. Do not use this approach unless you understand what it does:

foreach (Process proc in System.Diagnostics.Process.GetProcessesByName("EXCEL"))
{
  proc.Kill();
}

Note: This kill every process named «EXCEL».

I had to do it becase even though I’ve closed every single COM object in my code I still had stubborn Excel.exe process just hanging there. This is by no means the best solution, of course.

jimhark's user avatar

jimhark

4,9281 gold badge26 silver badges27 bronze badges

answered Jun 4, 2015 at 13:01

Denis Molodtsov's user avatar

4

I had same issue , we can solve the issue without any killing, we always forget to close interfaces which we have used form Microsoft.Office.Interop.Excel class so here is the code snippet and follow the structure and way have cleared objects , also keep an eye on Sheets interface in your code this is the main culprit we often close the application,Workbook,workbooks,range,sheet but we forget or unknowingly dont release the Sheets object or used interface so here is the code :

               Microsoft.Office.Interop.Excel.Application app = null;
        Microsoft.Office.Interop.Excel.Workbooks books = null;
        Workbook book = null;
        Sheets sheets = null;
        Worksheet sheet = null;
        Range range = null;

        try
        {
            app = new Microsoft.Office.Interop.Excel.Application();
            books = app.Workbooks;
            book = books.Add();
            sheets = book.Sheets;
            sheet = sheets.Add();
            range = sheet.Range["A1"];
            range.Value = "Lorem Ipsum";
            book.SaveAs(@"C:TempExcelBook" + DateTime.Now.Millisecond + ".xlsx");
            book.Close();
            app.Quit();
        }
        finally
        {
            if (range != null) Marshal.ReleaseComObject(range);
            if (sheet != null) Marshal.ReleaseComObject(sheet);
            if (sheets != null) Marshal.ReleaseComObject(sheets);
            if (book != null) Marshal.ReleaseComObject(book);
            if (books != null) Marshal.ReleaseComObject(books);
            if (app != null) Marshal.ReleaseComObject(app);
        }

answered Jan 20, 2017 at 6:39

kki's user avatar

After doing several tests on my own, checking different answers, this is the shortest code that makes the process go away just a few seconds later:

var excelApp = new Microsoft.Office.Interop.Excel.Application();
var workbooks = excelApp.Workbooks;
try
{
    var wb = workbooks.Open(filePath);

    // Use worksheet, etc.
    Worksheet sheet = wb.Worksheets.get_Item(1);
}
finally
{
    excelApp.Quit();
    Marshal.ReleaseComObject(workbooks);
    Marshal.ReleaseComObject(excelApp);
}

Despite the messages about the double-dot myth, in my own tests, if I don’t have a variable for Workbooks, the process would stay forever. It seems that indeed calling excelApp.Workbooks creates some objects in memory which prevent the Garbage Collector from disposing excel.exe. This means that this leaves the process in memory:

try
{
    // Do not
    var wb = excelApp.Workbooks.Open("");
}
finally
{
    excelApp.Quit();
    // Do not
    Marshal.ReleaseComObject(excelApp.Workbooks);
    Marshal.ReleaseComObject(excelApp);
}

answered Apr 24, 2021 at 2:33

Andrew's user avatar

AndrewAndrew

7,4822 gold badges32 silver badges42 bronze badges

This code worked on me.

        Excel.Application excelApp = null;
        Excel.Workbooks excelWorkbooks = null;
        Excel.Workbook excelWorkbook = null;
        Excel._Worksheet xlWorkSheet = null;
        Excel.Range range = null;

        excelApp = new Excel.Application();
        excelWorkbooks = excelApp.Workbooks;
        excelWorkbook = excelWorkbooks.Open(excelName);
        xlWorkSheet = (Excel.Worksheet)excelWorkbook.ActiveSheet;

        range = xlWorkSheet.Range["C3"] ;
        range.Value = "Update Data";
        Marshal.ReleaseComObject(range);

        xlWorkSheet.SaveAs(path);
            
        Marshal.ReleaseComObject(xlWorkSheet);
        excelWorkbook.Close();
        Marshal.ReleaseComObject(excelWorkbook);
        excelWorkbooks.Close();
        Marshal.ReleaseComObject(excelWorkbooks);
        excelApp.Quit();
        Marshal.ReleaseComObject(excelApp);

answered May 9, 2022 at 7:02

onoffon's user avatar

1

Inspired by @jimhark solution: In my program, I have to open simultaneously multiple Excel files. Therefore I had to tidy up some codes.

public void DoSomeExcelWork()
{ 
    OpenExcelFile(filePath, out Application excelApp, out Workbook workbook, out Process process);    

    // do some work on your excel.

    DisposeExcelFile(excelApp, workbook, process, false);
}

This is where the excel file gets opened.

private static void OpenExcelFile(string excelFilePath, out Application excelApp, out Workbook workbook, out Process process)
{
    excelApp = new Application();
    workbook = excelApp.Workbooks.Open(excelFilePath);
    process = ProcessUtility.GetExcelProcess(excelApp);
}

You don’t need to call Marshal stuff, since the process gets killed directly.

private static void DisposeExcelFile(Application excelApp, Workbook workbook, Process process, bool shouldSave = true)
{
    if (shouldSave)
        workbook.Save();

    excelApp.Application.Quit();
    process.Kill();      
}

This is where @jimhark ‘s solution comes in.

public static class ProcessUtility
{
    [DllImport("user32.dll")]
    static extern int GetWindowThreadProcessId(int hWnd, out int lpdwProcessId);

    public static Process GetExcelProcess(Microsoft.Office.Interop.Excel.Application excelApp)
    {
        int id;
        GetWindowThreadProcessId(excelApp.Hwnd, out id);
        return Process.GetProcessById(id);
    }
}

answered Nov 21, 2022 at 12:17

tataelm's user avatar

tataelmtataelm

6099 silver badges17 bronze badges

I have been plagued with this issue for years and finally came up with a good solution that should work for all use cases that I can think of. Whether you want your application to close the process after generating and saving or waiting until the user closes the window, along with ability to have multiple excel instances and never having the process linger.

Create this class:

using System;
using System.Runtime.InteropServices;
using Microsoft.Office.Interop.Excel;

namespace YourNameSpace
{
    public class MicrosoftApplications
    {
        [DllImport("user32.dll")]
        static extern int GetWindowThreadProcessId(int hWnd, out int lpdwProcessId);
        public class Excel
        {   
            public Excel()
            {
                Application = new Microsoft.Office.Interop.Excel.Application();
                RegisterExitEvent();
            }

            public Microsoft.Office.Interop.Excel.Application Application;
            
            private void RegisterExitEvent()
            {
                Application.WindowDeactivate -= XlApp_WindowDeactivate;
                Application.WindowDeactivate += XlApp_WindowDeactivate;
            }

            private void XlApp_WindowDeactivate(Workbook Wb, Window Wn)
            {
                Kill();
            }

            public void Kill()
            {
                int pid = 0;
                GetWindowThreadProcessId(Application.Hwnd, out pid);
                if (pid > 0)
                {
                    System.Diagnostics.Process p = System.Diagnostics.Process.GetProcessById(pid);
                    p.Kill();
                }
                Application = null;
                GC.Collect();
                GC.WaitForPendingFinalizers();
            }
        }
    }
}

And you can call it by:
YourNameSpace.MicrosoftApplications.Excel xlApp = new YourNameSpace.MicrosoftApplications.Excel();

Do whatever you need to do by calling xlApp.Application.whatever instead of xlApp.whatever and if the user exits the excel window(s) it will kill the process(es) that were used in the code. If you want to just generate a report behind the scenes but not display the form, then simply call xlApp.Kill(); to end that specific process.

Hope this helps someone, wish I knew this about 10 years ago.

answered Feb 10 at 18:42

LancourWestbrook's user avatar

İf you are handling it in one button u guys can get lastest process which you created and you can kill it.I used it.Have a good days.

//Exporting excel codes are here

System.Diagnostics.Process [] proc = System.Diagnostics.Process.GetProcessesByName(«excel»);
proc[proc.Length-1].Kill();

answered Jan 22, 2022 at 7:40

Stenquery's user avatar

1

  • Remove From My Forums
  • Question

  • I have read in other posts that all instances of com Objects must be closed before the excel interop closes. I have done that but it still won’t quite. I have made an extremely simple form

    Imports Excel = Microsoft.Office.Interop.Excel

    Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

    Dim excelApp As New Excel.Application

    Try

    ‘excelApp.Workbooks.Open(«C:Program FilesDISClientTemplatesTemplateMerge.xls»)

    ‘excelApp.Workbooks.Close()

    Catch ex As Exception

    MsgBox(ex)

    Finally

    excelApp.Quit()

    End Try

    End Sub

    End Class

    However when I run the program and close the form that pops up it still shows the excel.exe in my task manager any ideas?

    Jackie

Answers

  • u have to close all instances opened explicitely. that thing is by design.. when u do not close everything excel remains running.
    close each range(rng), worksheet(WS) declared and finally the workbooks… finally u have to quit the excel object too.
    preferably put these inside a finally block.

    while (interop.Marshal.ReleaseComObject(rng) > 0)
    {}

    while (interop.Marshal.ReleaseComObject(ws) > 0)
    {}

    while (interop.Marshal.ReleaseComObject(owb) > 0)
    {}

    excel.quit()

    while (interop.Marshal.ReleaseComObject(excel) > 0)
    {}

    This will work fine..

    -Abhishek Chatterjee

Никак не вычищается из памяти Excel.exe? Даже делаете все по шагам из статьи Office application does not quit after automation from Visual Studio .NET client? Уже решили убивать все весящие Excel-процессы командой Kill?

Я тоже несколько раз заходил на решение этой проблемы, но никак не мог понять, почему процесс Excel.exe никак не хочет вылезать из памяти? Между тем генерация Excel или Word документов частенько требуется в различных проектах.

Вопрос на засыпку: в какой строке будет утечка памяти?

   1:  Application application = new Application {Visible = false};
   2:  Workbook workbook = application.Workbooks.Add(Type.Missing);
   3:  Worksheet sheet = (Worksheet) workbook.ActiveSheet;
   4:   
   5:  ...
   6:   
   7:  sheet.Cells[1, 1] = "columnTitle";
   8:  sheet.Cells[1, 1].EntireColumn.AutoFit();
   9:   
  10:  ...
  11:   
  12:  Marshal.ReleaseComObject(sheet);
  13:   
  14:  workbook.Close(false, Type.Missing, Type.Missing);
  15:  Marshal.ReleaseComObject(workbook);
  16:   
  17:  application.Quit();
  18:  Marshal.ReleaseComObject(application);

Те, кто прочитал Office application does not quit…, смело скажу во 2ой и буду абсолютно правы. Те, кто нашли GOTCHA: Com Interop, захотят исправить код на:

   1:  Application application = new Application {Visible = false};
   2:  Workbooks workbooks = application.Workbooks;
   3:  Workbook workbook = workbooks.Add(Type.Missing);
   4:  Worksheet sheet = (Worksheet) workbook.ActiveSheet;
   5:   
   6:  ...
   7:   
   8:  Range range = sheet.Cells;
   9:  range[1, 1] = "columnTitle";
  10:  range[1, 1].EntireColumn.AutoFit();
  11:   
  12:  Marshal.ReleaseComObject(range);
  13:   
  14:  ...
  15:   
  16:  Marshal.ReleaseComObject(sheet);
  17:   
  18:  workbook.Close(false, Type.Missing, Type.Missing);
  19:  Marshal.ReleaseComObject(workbook);
  20:   
  21:  Marshal.ReleaseComObject(workbooks);
  22:   
  23:  application.Quit();
  24:  Marshal.ReleaseComObject(application);

И все бы хорошо, только строчка 10 до сих пор «протекает». Исправить это можно так:

   1:  ...
   2:   
   3:  var currentCell = (Range)range[1, 1];
   4:  Range entireColumn = currentCell.EntireColumn;
   5:  entireColumn.AutoFit();
   6:   
   7:  Marshal.ReleaseComObject(entireColumn);
   8:  Marshal.ReleaseComObject(currentCell);
   9:   
  10:  ...

В комментариях несколько полезных советов по этой теме…

Hey there,

This is an issue I had for a long time aswell. I’m afraid that I did not find a solution by using the Interop. However I did create some code that killed the process automatically for me. Now I don’t know if this is what you want because i found it somewhat vague but here it goes:

First we have to Import a Dll to make use of the GetWindowThreadProcessId() function. (Don’t mind the class names it’s just my code).

using System.Runtime.InteropServices;

...

public partial class Quotation : Form
{
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

...
}

Then we are going to create a variable which is going to store the processId. (I included your variable names for this)

uint processId = 0;

if (xlsApplication != null)
{
   if (xlsApplication.Workbooks != null)
   {
      if (xlsApplication.Workbooks.Count < 0)
      {
         GetWindowThreadProcessId(new IntPtr(xlsApplication.Hwnd), out processId);
      
      
      }
   }
}

And finally the cleanup. The killing of the process needs to be inside try and catch statements. This because the closing of Excel does appear to happen sometimes.

if (xlsApplication != null)
{
   if (xlsApplication.Workbooks != null)
   {
      if (xlsApplication.Workbooks.Count < 0)
      {
         xlsWorkbook.Close(true, @"C:test.xls", null);
         xlsApplication.Workbooks.Close();
         xlsApplication.Quit();
         missing = null;
         ReleaseObjects();
         r_Time = null;
         r_Name = null;
         r_ReceivedTime = null;
         r_Received = null;
         r_OrderedTime = null;
         r_Ordered = null;
         xlsRange = null;
         xlsWorksheet = null;
         xlsWorkbook = null;
         xlsApplication = null;
      }
   }
}

try
{
    if (processId != 0)
    {
        Process excelProcess = Process.GetProcessById((int)processId);
        excelProcess.CloseMainWindow();
        excelProcess.Refresh();
        excelProcess.Kill();
    }
}
catch
{
    
}

GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();

I hope this helps

@Денис Молодцов, пытаясь быть полезным, предложил убить все процессы под названием «EXCEL». Это, кажется, напрашивается на неприятности. Уже есть много ответов, которые описывают способы остановки процесса после вызова excel.quit(), играя с COM-взаимодействием. Это лучше, если вы можете заставить его работать.

@Kevin Vuilleumier было отличное предложение отправить WM_CLOSE в окно Excel. Я планирую проверить это.

Если по какой-то причине вам нужно убить процесс Excel App Object Excel, вы можете нацелиться на него специально, используя что-то вроде этого:

  using System.Diagnostics;
  using System.Runtime.InteropServices;

// . . .

    [DllImport("user32.dll", SetLastError=true)]
    public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId);

// . . .

    uint excelAppPid;
    uint tid = GetWindowThreadProcessId(excel.Hwnd, out excelAppPid);

    if (tid)
    {
      Process excelAppProc = Process.GetProcessById($excelPid)
      if (excelAppProc)
      {
        excelAppProc.Kill()
      }
    }

У меня нет времени на полное тестирование в С#, но я провел быстрый тест в Powershell, где у меня возникла проблема с прекращением работы Excel, и этот подход работает.

Это довольно просто. Свойство объекта приложения Excel Свойство Hwnd является дескриптором скрытого окна процесса Excel. Передайте excel.Hwnd в GetWindowThreadProcessId, чтобы получить идентификатор процесса. Используйте это, чтобы открыть процесс, наконец, вызовите Kill().

По крайней мере, мы уверены, что убиваем правильный процесс. Ну, почти уверен. Если процесс Excel уже нормально завершен, его идентификатор процесса может быть повторно использован новым процессом. Чтобы ограничить эту возможность, важно не ждать между вызовом excel.quit() и попыткой уничтожения.

  • Home
  • VBForums
  • Visual Basic
  • Visual Basic .NET
  • VS 2017 Problem Closing Excel Interop App Properly

  1. Feb 3rd, 2018, 02:09 PM


    #1

    Slabs1960 is offline

    Thread Starter


    Addicted Member

    Slabs1960's Avatar


    Problem Closing Excel Interop App Properly

    I am using Microsoft.Office.Interop.Excel and Microsoft.Office.Interop.Excel to import data from a Excel spreadsheet (Office 365). This is new to me and I have been playing around with the code to get it working.

    I have managed to import the data and then display it with a MessageBox (for testing purposes). This data will eventually be imported into a bound DGV.

    The problem I am having to closing the Excel instance. No matter what I do Excel does not close. For every time I run the app an instance of Excel remains.

    I have tried:

    Code:

    System.Runtime.InteropServices.Marshal.ReleaseComObject(xlRangeEyepieces)
    System.Runtime.InteropServices.Marshal.ReleaseComObject(xlWorkSheetEyepieces)
    
    xlAppEyepieces.Workbooks.Close()
    
    xlAppEyepieces.Quit()
    System.Runtime.InteropServices.Marshal.ReleaseComObject(xlWorkBookEyepieces)
    System.Runtime.InteropServices.Marshal.ReleaseComObject(xlAppEyepieces)
    xlAppEyepieces = Nothing

    My Code:

    Code:

    Private Sub MnuImportFromExcel_Click_1(sender As Object, e As EventArgs) Handles mnuImportFromExcel.Click
    
            Dim strCellValue As String = ""
    
            Dim strColumn1 As String = ""
            Dim strColumn2 As String = ""
            Dim strColumn3 As String = ""
            Dim strColumn4 As String = ""
    
            Dim strTestString As String = ""
            Dim strSheetName As String = ""
    
    
            Try
                Dim xlAppEyepieces As New Excel.Application
                Dim xlWorkBookEyepieces As Excel.Workbook
                Dim xlWorkSheetEyepieces As Excel.Worksheet
                Dim xlRangeEyepieces As Excel.Range
    
                ' Open Excel Spreadsheet.
                xlWorkBookEyepieces = xlAppEyepieces.Workbooks.Open("D:TempAstroCompanion BackupsCopy of AstroCompanionData2.xlsx")
    
                ' Loop over all sheets.
                For i As Integer = 1 To xlWorkBookEyepieces.Sheets.Count
    
                    ' Get sheet.
                    xlWorkSheetEyepieces = CType(xlWorkBookEyepieces.Sheets(i), Worksheet)
    
    
                    ' Get range.
                    xlRangeEyepieces = xlWorkSheetEyepieces.UsedRange
    
                    ' Load all cells into 2d array.
                    Dim array(,) As Object = CType(xlRangeEyepieces.Value(XlRangeValueDataType.xlRangeValueDefault), Object(,))
    
                    System.Runtime.InteropServices.Marshal.ReleaseComObject(xlRangeEyepieces)
                    System.Runtime.InteropServices.Marshal.ReleaseComObject(xlWorkSheetEyepieces)
    
                    ' Scan the cells.
                    If array IsNot Nothing Then
    
                        ' Get bounds of the array.
                        Dim NumberOfRows As Integer = array.GetUpperBound(0)  'Rows
                        Dim NumberOfColumns As Integer = array.GetUpperBound(1)  'Columns
    
                        Dim intTest As Integer = 1
                        Dim strTestExellValue As String = ""
    
                        ' Loop over all elements.
                        For j As Integer = 2 To NumberOfRows
    
                            For x As Integer = 1 To NumberOfColumns
    
                                Try
                                    strCellValue = array(j, x).ToString
    
                                    Select Case x
                                        Case 1
                                            strColumn1 = strCellValue
                                        Case 2
                                            strColumn2 = strCellValue
                                        Case 3
                                            strColumn3 = strCellValue
                                        Case 4
                                            strColumn4 = strCellValue
                                    End Select
    
    
                                Catch ex As Exception
                                    MessageBox.Show(ex.Message, Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Error)
                                Finally
                                    'Nothing
                                End Try
    
    
                            Next
    
    
                            strTestString = strColumn1 & " ---> " & strColumn2 & " ---> " & strColumn3 & " ---> " & strColumn4
    
                            MessageBox.Show(strTestString, "File - Test", MessageBoxButtons.OK, MessageBoxIcon.Information)
    
    
                            intTest = intTest + 1
    
                            If intTest = 4 Then
                                Exit For
                            End If
    
                        Next
    
    
    
                    End If
                Next
    
                xlAppEyepieces.Workbooks.Close()
    
                xlAppEyepieces.Quit()
                System.Runtime.InteropServices.Marshal.ReleaseComObject(xlWorkBookEyepieces)
                System.Runtime.InteropServices.Marshal.ReleaseComObject(xlAppEyepieces)
                xlAppEyepieces = Nothing
    
            Catch ex As Exception
                MessageBox.Show(ex.Message, Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Error)
            Finally
    
            End Try
    
        End Sub

    What am I missing?

    I can fix what stupid does, but, I cannot fix stupid


  2. Feb 3rd, 2018, 03:24 PM


    #2

    Re: Problem Closing Excel Interop App Properly

    Try this after «Quit»

    Code:

                Marshal.FinalReleaseComObject(xlWorkSheet)
                Marshal.FinalReleaseComObject(xlWorkBook)
                Marshal.FinalReleaseComObject(xlApp)
                xlApp = Nothing

    U


  3. Feb 3rd, 2018, 04:35 PM


    #3

    Slabs1960 is offline

    Thread Starter


    Addicted Member

    Slabs1960's Avatar


    Re: Problem Closing Excel Interop App Properly

    Quote Originally Posted by wes4dbt
    View Post

    Try this after «Quit»

    Code:

                Marshal.FinalReleaseComObject(xlWorkSheet)
                Marshal.FinalReleaseComObject(xlWorkBook)
                Marshal.FinalReleaseComObject(xlApp)
                xlApp = Nothing

    U

    No change. Thanks

    I have been testing the code. I have noticed, that sometimes the Excel instance is closed. Other times, not. All Excel instances are closed when the application is closed. Your thoughts?

    I can fix what stupid does, but, I cannot fix stupid


  4. Feb 3rd, 2018, 04:47 PM


    #4

    Re: Problem Closing Excel Interop App Properly

    Hi,

    try ending like this…

    Code:

    'your code...
       oXL.Quit()
            releaseObject(oXL)
            releaseObject(oWB)
            End_Excel_App(datestart, dateend)
        End Sub
    
        Private Sub releaseObject(ByVal obj As Object)
            Try
                System.Runtime.InteropServices.Marshal.ReleaseComObject(obj)
                obj = Nothing
            Catch ex As Exception
                obj = Nothing
            Finally
                GC.Collect()
            End Try
        End Sub
    
        Dim datestart As Date = Date.Now
        Dim dateend As Date = Date.Now
    
        Private Sub End_Excel_App(ByVal datestart As Date, ByVal dateEnd As Date)
            Dim xlp() As Process = Process.GetProcessesByName("Microsoft Excel")
            For Each Process As Process In xlp
                If Process.StartTime >= datestart And Process.StartTime <= dateEnd Then
                    Process.Kill()
                    Exit For
                End If
            Next
        End Sub

    regards
    Chris

    to hunt a species to extinction is not logical !
    since 2010 the number of Tigers are rising again in 2016 — 3900 were counted. with Baby Callas it’s 3901, my wife and I had 2-3 months the privilege of raising a Baby Tiger.


  5. Feb 3rd, 2018, 04:49 PM


    #5

    Re: Problem Closing Excel Interop App Properly

    I’ve also used this,

    Code:

                xlApp.Quit()
                GC.Collect()
                GC.WaitForPendingFinalizers()
    
                Marshal.FinalReleaseComObject(xlWorkSheet)
                Marshal.FinalReleaseComObject(xlWorkBook)
                Marshal.FinalReleaseComObject(xlApp)
                xlApp = Nothing

    But you will probably get people telling you there’s no reason to call the GC. At the time it was the way I could get the Excel instance to close consistently.


  6. Feb 4th, 2018, 10:14 AM


    #6

    Slabs1960 is offline

    Thread Starter


    Addicted Member

    Slabs1960's Avatar


    Re: Problem Closing Excel Interop App Properly

    Quote Originally Posted by ChrisE
    View Post

    Hi,

    try ending like this…

    Code:

    'your code...
       oXL.Quit()
            releaseObject(oXL)
            releaseObject(oWB)
            End_Excel_App(datestart, dateend)
        End Sub
    
        Private Sub releaseObject(ByVal obj As Object)
            Try
                System.Runtime.InteropServices.Marshal.ReleaseComObject(obj)
                obj = Nothing
            Catch ex As Exception
                obj = Nothing
            Finally
                GC.Collect()
            End Try
        End Sub
    
        Dim datestart As Date = Date.Now
        Dim dateend As Date = Date.Now
    
        Private Sub End_Excel_App(ByVal datestart As Date, ByVal dateEnd As Date)
            Dim xlp() As Process = Process.GetProcessesByName("Microsoft Excel")
            For Each Process As Process In xlp
                If Process.StartTime >= datestart And Process.StartTime <= dateEnd Then
                    Process.Kill()
                    Exit For
                End If
            Next
        End Sub

    regards
    Chris

    Thanks Chris. Tried it. Does not work.

    I thought maybe I made a mistake with the name in «Dim xlp() As Process = Process.GetProcessesByName(«Microsoft Excel»)». So, I tried «Microsoft Excel (32 bit)» and «EXCEL.EXE».
    No go.

    I can fix what stupid does, but, I cannot fix stupid


  7. Feb 4th, 2018, 10:17 AM


    #7

    Slabs1960 is offline

    Thread Starter


    Addicted Member

    Slabs1960's Avatar


    Re: Problem Closing Excel Interop App Properly

    Quote Originally Posted by wes4dbt
    View Post

    I’ve also used this,

    Code:

                xlApp.Quit()
                GC.Collect()
                GC.WaitForPendingFinalizers()
    
                Marshal.FinalReleaseComObject(xlWorkSheet)
                Marshal.FinalReleaseComObject(xlWorkBook)
                Marshal.FinalReleaseComObject(xlApp)
                xlApp = Nothing

    But you will probably get people telling you there’s no reason to call the GC. At the time it was the way I could get the Excel instance to close consistently.

    Thanks. Tried it. Still does not work.

    I can fix what stupid does, but, I cannot fix stupid


  8. Feb 4th, 2018, 12:06 PM


    #8

    Re: Problem Closing Excel Interop App Properly

    Hi,

    here the complete sample, try it a look if Excel is still open.

    Code:

    Option Strict On
    
    Imports Excel
    
    Public Class Form1
        Public Enum Excel_Colors
            rgbAliceBlue = 16775408          'Alice Blue
            rgbAntiqueWhite = 14150650       'Antique White
            rgbAqua = 16776960               'Aqua
            rgbAquamarine = 13959039         'Aquamarine
            rgbAzure = 16777200              'Azure
            rgbBeige = 14480885              'Beige
            rgbBisque = 12903679             'Bisque
            rgbBlack = 0                     'Black
            rgbBlanchedAlmond = 13495295     'Blanched Almond
            rgbBlue = 16711680               'Blue
            rgbBlueViolet = 14822282         'Blue Violet
            rgbBrown = 2763429               'Brown
            rgbBurlyWood = 8894686           'Burly Wood
            rgbCadetBlue = 10526303          'Cadet Blue
            rgbChartreuse = 65407            'Chartreuse
            rgbCoral = 5275647               'Coral
            rgbCornflowerBlue = 15570276     'Cornflower Blue
            rgbCornsilk = 14481663           'Cornsilk
            rgbCrimson = 3937500             'Crimson
            rgbDarkBlue = 9109504            'Dark Blue
            rgbDarkCyan = 9145088            'Dark Cyan
            rgbDarkGoldenrod = 755384        'Dark Goldenrod
            rgbDarkGray = 11119017           'Dark Gray
            rgbDarkGreen = 25600             'Dark Green
            rgbDarkGrey = 11119017           'Dark Grey
            rgbDarkKhaki = 7059389           'Dark Khaki
            rgbDarkMagenta = 9109643         'Dark Magenta
            rgbDarkOliveGreen = 3107669      'Dark Olive Green
            rgbDarkOrange = 36095            'Dark Orange
            rgbDarkOrchid = 13382297         'Dark Orchid
            rgbDarkRed = 139                 'Dark Red
            rgbDarkSalmon = 8034025          'Dark Salmon
            rgbDarkSeaGreen = 9419919        'Dark Sea Green
            rgbDarkSlateBlue = 9125192       'Dark Slate Blue
            rgbDarkSlateGray = 5197615       'Dark Slate Gray
            rgbDarkSlateGrey = 5197615       'Dark Slate Grey
            rgbDarkTurquoise = 13749760      'Dark Turquoise
            rgbDarkViolet = 13828244         'Dark Violet
            rgbDeepPink = 9639167            'Deep Pink
            rgbDeepSkyBlue = 16760576        'Deep Sky Blue
            rgbDimGray = 6908265             'Dim Gray
            rgbDimGrey = 6908265             'Dim Grey
            rgbDodgerBlue = 16748574         'Dodger Blue
            rgbFireBrick = 2237106           'Fire Brick
            rgbFloralWhite = 15792895        'Floral White
            rgbForestGreen = 2263842         'Forest Green
            rgbFuchsia = 16711935            'Fuchsia
            rgbGainsboro = 14474460          'Gainsboro
            rgbGhostWhite = 16775416         'Ghost White
            rgbGold = 55295                  'Gold
            rgbGoldenrod = 2139610           'Goldenrod
            rgbGray = 8421504                'Gray
            rgbGreen = 32768                 'Green
            rgbGreenYellow = 3145645         'Green Yellow
            rgbGrey = 8421504                'Grey
            rgbHoneydew = 15794160           'Honeydew
            rgbHotPink = 11823615            'Hot Pink
            rgbIndianRed = 6053069           'Indian Red
            rgbIndigo = 8519755              'Indigo
            rgbIvory = 15794175              'Ivory
            rgbKhaki = 9234160               'Khaki
            rgbLavender = 16443110           'Lavender
            rgbLavenderBlush = 16118015      'Lavender Blush
            rgbLawnGreen = 64636             'Lawn Green
            rgbLemonChiffon = 13499135       'Lemon Chiffon
            rgbLightBlue = 15128749          'Light Blue
            rgbLightCoral = 8421616          'Light Coral
            rgbLightCyan = 9145088           'Light Cyan
            rgbLightGoldenrodYellow = 13826810   'LightGoldenrodYellow
            rgbLightGray = 13882323          'Light Gray
            rgbLightGreen = 9498256          'Light Green
            rgbLightGrey = 13882323          'Light Grey
            rgbLightPink = 12695295          'Light Pink
            rgbLightSalmon = 8036607         'Light Salmon
            rgbLightSeaGreen = 11186720      'Light Sea Green
            rgbLightSkyBlue = 16436871       'Light Sky Blue
            rgbLightSlateGray = 10061943     'Light Slate Gray
            rgbLightSteelBlue = 14599344     'Light Steel Blue
            rgbLightYellow = 14745599        'Light Yellow
            rgbLime = 65280                  'Lime
            rgbLimeGreen = 3329330           'Lime Green
            rgbLinen = 15134970              'Linen
            rgbMaroon = 128                  'Maroon
            rgbMediumAquamarine = 11206502   'Medium Aquamarine
            rgbMediumBlue = 13434880         'Medium Blue
            rgbMediumOrchid = 13850042       'Medium Orchid
            rgbMediumPurple = 14381203       'Medium Purple
            rgbMediumSeaGreen = 7451452      'Medium Sea Green
            rgbMediumSlateBlue = 15624315    'Medium Slate Blue
            rgbMediumSpringGreen = 10156544  'Medium Spring Green
            rgbMediumTurquoise = 13422920    'Medium Turquoise
            rgbMediumVioletRed = 8721863     'Medium Violet Red
            rgbMidnightBlue = 7346457        'Midnight Blue
            rgbMintCream = 16449525          'Mint Cream
            rgbMistyRose = 14804223          'Misty Rose
            rgbMoccasin = 11920639           'Moccasin
            rgbNavajoWhite = 11394815        'Navajo White
            rgbNavy = 8388608                'Navy
            rgbNavyBlue = 8388608            'Navy Blue
            rgbOldLace = 15136253            'Old Lace
            rgbOlive = 32896                 'Olive
            rgbOliveDrab = 2330219           'Olive Drab
            rgbOrange = 42495                'Orange
            rgbOrangeRed = 17919             'Orange Red
            rgbOrchid = 14053594             'Orchid
            rgbPaleGoldenrod = 7071982       'Pale Goldenrod
            rgbPaleGreen = 10025880          'Pale Green
            rgbPaleTurquoise = 15658671      'Pale Turquoise
            rgbPaleVioletRed = 9662683       'Pale Violet Red
            rgbPapayaWhip = 14020607         'Papaya Whip
            rgbPeachPuff = 12180223          'Peach Puff
            rgbPeru = 4163021                'Peru
            rgbPink = 13353215               'Pink
            rgbPlum = 14524637               'Plum
            rgbPowderBlue = 15130800         'Powder Blue
            rgbPurple = 8388736              'Purple
            rgbRed = 255                     'Red
            rgbRosyBrown = 9408444           'Rosy Brown
            rgbRoyalBlue = 14772545          'Royal Blue
            rgbSalmon = 7504122              'Salmon
            rgbSandyBrown = 6333684          'Sandy Brown
            rgbSeaGreen = 5737262            'Sea Green
            rgbSeashell = 15660543           'Seashell
            rgbSienna = 2970272              'Sienna
            rgbSilver = 12632256             'Silver
            rgbSkyBlue = 15453831            'Sky Blue
            rgbSlateBlue = 13458026          'Slate Blue
            rgbSlateGray = 9470064           'Slate Gray
            rgbSnow = 16448255               'Snow
            rgbSpringGreen = 8388352         'Spring Green
            rgbSteelBlue = 11829830          'Steel Blue
            rgbTan = 9221330                 'Tan
            rgbTeal = 8421376                'Teal
            rgbThistle = 14204888            'Thistle
            rgbTomato = 4678655              'Tomato
            rgbTurquoise = 13688896          'Turquoise
            rgbViolet = 15631086             'Violet
            rgbWheat = 11788021              'Wheat
            rgbWhite = 16777215              'White
            rgbWhiteSmoke = 16119285         'White Smoke
            rgbYellow = 65535                'Yellow
            rgbYellowGreen = 3329434         'Yellow Green
        End Enum
    
       
       
        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
            Dim a As Integer
            Dim Exceldaten(9, 2) As String
            For a = 0 To 9
                Exceldaten(a, 0) = Chr(65 + a)
                Exceldaten(a, 1) = Chr(48 + a)
                Exceldaten(a, 2) = "Column 3 - Row " & (a + 2)
            Next
            Dim oXL As Excel.Application
            Dim oWB As Excel.Workbook
            Dim oSheet As Excel.Worksheet
            oXL = CType(CreateObject("Excel.Application"), Excel.Application)
            oWB = oXL.Workbooks.Add
            oSheet = CType(oWB.ActiveSheet, Excel.Worksheet)
            'oXL.Visible = True
            oSheet.Range("A1").Value = "vbForums.com"
            oSheet.Range("B1").Value = "my Number"
            oSheet.Range("C1").Value = "Sssssss"
            'Excelformat
            oSheet.Range("A1").HorizontalAlignment = Excel.XlHAlign.xlHAlignRight
            oSheet.Range("E1").HorizontalAlignment = Excel.XlHAlign.xlHAlignRight
            oSheet.Range("A1").ColumnWidth = 10
            oSheet.Range("B1").ColumnWidth = 10
            oSheet.Range("C1").ColumnWidth = 15.5
            With oSheet.Range("A1", "C1")
                .Font.Bold = True
                .Font.Color = Excel_Colors.rgbOrangeRed
                .VerticalAlignment = Excel.XlVAlign.xlVAlignCenter
            End With
    
            'add something to calculate
            oSheet.Range("D2").Value = 10.25
            oSheet.Range("D3").Value = 101.25
            oSheet.Range("D4").Value = 123330.65
            oSheet.Range("D5").Value = 1255.25
          
            oSheet.Range("E6").Value = "20.10.2017 " & TimeOfDay
            oSheet.Range("E7").Value = String.Format("{0:dd.MM.yyyy HH:mm:ss}", Date.Now)
    
    
    
            'Dropdown Start ................
            'create a Dropdown , do you want to hide that column ?
            'Dim columnA As Excel.Range = CType(oSheet.Columns(2), Excel.Range)
            'columnA.Hidden = True
    
            'this will set the Dropdown Button in cell D4
            Dim range As Excel.Range = CType(oSheet.Cells(1, 4), Excel.Range)
    
    
            'what Range ? in this case B2:B70
            range.Validation.Add(Type:=Excel.XlDVType.xlValidateList, _
                AlertStyle:=Excel.XlDVAlertStyle.xlValidAlertStop, _
                Operator:=Excel.XlFormatConditionOperator.xlBetween, _
                Formula1:="=$B$2:$B$70")
            'Dropdown End...............
    
    
            'Data to Excel 
            oSheet.Range("A2", "C9").Value = Exceldaten
            'Calulate values, 
            'in German = "=Summe(D2:D5)"
            'in Englisch probably = "=Sum(D2:D5)"
            oSheet.Range("D7").Value = "=Summe(D2:D5)"
            oSheet.Range("D7").NumberFormatLocal = "#.##0,00"
            oSheet.Range("D7").Interior.Color = Excel_Colors.rgbCadetBlue
    
            'Save Excel
            oSheet.SaveAs("C:Exceldaten99.xls")
            oXL.Quit()
            releaseObject(oXL)
            releaseObject(oWB)
            End_Excel_App(datestart, dateend)
        End Sub
    
        Private Sub releaseObject(ByVal obj As Object)
            Try
                System.Runtime.InteropServices.Marshal.ReleaseComObject(obj)
                obj = Nothing
            Catch ex As Exception
                obj = Nothing
            Finally
                GC.Collect()
            End Try
        End Sub
    
        Dim datestart As Date = Date.Now
        Dim dateend As Date = Date.Now
    
        Private Sub End_Excel_App(ByVal datestart As Date, ByVal dateEnd As Date)
            Dim xlp() As Process = Process.GetProcessesByName("Microsoft Excel")
            For Each Process As Process In xlp
                If Process.StartTime >= datestart And Process.StartTime <= dateEnd Then
                    Process.Kill()
                    Exit For
                End If
            Next
        End Sub
    
      
    End Class

    regards
    Chris

    to hunt a species to extinction is not logical !
    since 2010 the number of Tigers are rising again in 2016 — 3900 were counted. with Baby Callas it’s 3901, my wife and I had 2-3 months the privilege of raising a Baby Tiger.


  9. Feb 4th, 2018, 12:18 PM


    #9

    Slabs1960 is offline

    Thread Starter


    Addicted Member

    Slabs1960's Avatar


    Re: Problem Closing Excel Interop App Properly

    Thanks Chris will give it a try later.

    I can fix what stupid does, but, I cannot fix stupid


  • Home
  • VBForums
  • Visual Basic
  • Visual Basic .NET
  • VS 2017 Problem Closing Excel Interop App Properly


Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
  • BB code is On
  • Smilies are On
  • [IMG] code is On
  • [VIDEO] code is On
  • HTML code is Off

Forum Rules


Click Here to Expand Forum to Full Width

Hi,

I am using C#.net using interop to read to and write from Excel and I am having trouble closing Excel after using it.

I have had this issue on and off for years, but it was under control until I upgraded to Windows 10, Excel
 365/2016, Visual Studio 15.8.1, and
Microsoft.Office.Interop.Excel.15.0.4795.1000.  I now seem to be completely unable to close an Excel process after it has been opened.

I open Excel as follows:

  	 GCHandle rcwHandle;
        _App = new Microsoft.Office.Interop.Excel.Application();
        rcwHandle = GCHandle.Alloc(_App, GCHandleType.Normal);

but rcwHandle does NOT get allowcated! It is always rcwHandle.IsAllocated==false and it is not initialized.

I (try to) dispose Excel as follows:

     if (disposing)
            {
                if (_App != null &&rcwHandle.IsAllocated)
                {
                    _App.DisplayAlerts = false;
                    for (int bookCount = _App.Workbooks.Count; bookCount > 0; bookCount--)
                    {
                        Workbook book = _App.Workbooks[bookCount];
                        for (int sheetCount = book.Sheets.Count; sheetCount > 0; sheetCount--)
                        {
                            Worksheet sheet = (Worksheet)book.Sheets[sheetCount];
                            System.Runtime.InteropServices.Marshal.FinalReleaseComObject(sheet);
                            sheet = null;
                        }
                        System.Runtime.InteropServices.Marshal.FinalReleaseComObject(book.Sheets);
                        System.Runtime.InteropServices.Marshal.FinalReleaseComObject(book);
                        book = null;
                    }
                    System.Runtime.InteropServices.Marshal.FinalReleaseComObject(_App.Workbooks);
                    _App.Quit();
                    System.Runtime.InteropServices.Marshal.FinalReleaseComObject(_App);
                    _App = null;
                }

                    //http://msdn.microsoft.com/en-us/library/aa679806(v=office.11).aspx
                    GC.Collect();
                GC.WaitForPendingFinalizers();
                GC.Collect();
                GC.WaitForPendingFinalizers();
            }

The main
section of this does not ever get called as
rcwHandle.IsAllocated is false. I tried removing the requirement for rcwHandle.IsAllocated to enter then main section, but Excel processes stayed open even with all that marshalling and releasing.

I don’t really understand garbage collection and interop, so I don’t know what to try next. I would appreciate any help.

Thanks!

Ethan


Ethan Strauss

  • Moved by

    Thursday, August 30, 2018 5:45 AM

Simple rule: avoid using double-dot-calling expressions, such as this:

var workbook = excel.Workbooks.Open(/*params*/)

…because in this way you create RCW objects not only for workbook, but for Workbooks, and you should release it too (which is not possible if a reference to the object is not maintained).

So, the right way will be:

var workbooks = excel.Workbooks;
var workbook = workbooks.Open(/*params*/)

//business logic here

Marshal.ReleaseComObject(workbook);
Marshal.ReleaseComObject(workbooks);
Marshal.ReleaseComObject(excel);

It is tricky to get rid of all references since you have to guess if calls like:

var workbook = excel.Workbooks.Open("")

Creates an instance of Workbooks that you do not hold a reference to.

Even references like:

targetRange.Columns.AutoFit()

Will create an instance of .Columns() without you knowing and not released properly.

I ended up writing a class holding a list of object references that could dispose all objects in reverse order.

The class has a list of objects and Add() functions for anything you reference as you use Excel interop that returns the object itself:

    public List<Object> _interopObjectList = new List<Object>();

    public Excel.Application add(Excel.Application obj)
    {
        _interopObjectList.Add(obj);
        return obj;
    }

    public Excel.Range add(Excel.Range obj)
    {
        _interopObjectList.Add(obj);
        return obj;
    }

    public Excel.Workbook add(Excel.Workbook obj)
    {
        _interopObjectList.Add(obj);
        return obj;
    }

    public Excel.Worksheet add(Excel.Worksheet obj)
    {
        _interopObjectList.Add(obj);
        return obj;
    }

    public Excel.Worksheets add(Excel.Worksheets obj)
    {
        _interopObjectList.Add(obj);
        return obj;
    }

    public Excel.Sheets add(Excel.Sheets obj)
    {
        _interopObjectList.Add(obj);
        return obj;
    }


    public Excel.Workbooks add(Excel.Workbooks obj)
    {
        _interopObjectList.Add(obj);
        return obj;
    }

Then to unregister objects I used the following code:

    //Release all registered interop objects in reverse order
    public void unregister()
    {
        //Loop object list in reverse order and release Office object
        for (int i=_interopObjectList.Count-1; i>=0 ; i -= 1)
        { ReleaseComObject(_interopObjectList[i]); }

        //Clear object list
        _interopObjectList.Clear();
    }


    /// <summary>
    /// Release a com interop object 
    /// </summary>
    /// <param name="obj"></param>
     public static void ReleaseComObject(object obj)
     {
         if (obj != null && InteropServices.Marshal.IsComObject(obj))
             try
             {
                 InteropServices.Marshal.FinalReleaseComObject(obj);
             }
             catch { }
             finally
             {
                 obj = null;
             }

         GC.Collect();
         GC.WaitForPendingFinalizers();
         GC.Collect();
         GC.WaitForPendingFinalizers();
     }

Then principle is to create the class and capture references like this:

//Create helper class
xlsHandler xlObj = new xlsHandler();

..

//Sample - Capture reference to excel application
Excel.Application _excelApp = xlObj.add(new Excel.Application());

..
//Sample - Call .Autofit() on a cell range and capture reference to .Columns() 
xlObj.add(_targetCell.Columns).AutoFit();

..

//Release all objects collected by helper class
xlObj.unregister();

Not perhaps code of great beauty but may inspire to something useful.

Here is a snippet of code I wrote, because I had the same problem as you. Basically, you need to close the workbook, quit the application, and then release ALL of your COM objects (not just the Excel Application object). Finally, call the garbage collector for good measure.

    /// <summary>
    /// Disposes the current <see cref="ExcelGraph" /> object and cleans up any resources.
    /// </summary>
    public void Dispose()
    {
        // Cleanup
        xWorkbook.Close(false);
        xApp.Quit();

        // Manual disposal because of COM
        while (Marshal.ReleaseComObject(xApp) != 0) { }
        while (Marshal.ReleaseComObject(xWorkbook) != 0) { }
        while (Marshal.ReleaseComObject(xWorksheets) != 0) { }
        while (Marshal.ReleaseComObject(xWorksheet) != 0) { }
        while (Marshal.ReleaseComObject(xCharts) != 0) { }
        while (Marshal.ReleaseComObject(xMyChart) != 0) { }
        while (Marshal.ReleaseComObject(xGraph) != 0) { }
        while (Marshal.ReleaseComObject(xSeriesColl) != 0) { }
        while (Marshal.ReleaseComObject(xSeries) != 0) { }
        xApp = null;
        xWorkbook = null;
        xWorksheets = null;
        xWorksheet = null;
        xCharts = null;
        xMyChart = null;
        xGraph = null;
        xSeriesColl = null;
        xSeries = null;

        GC.Collect();
        GC.WaitForPendingFinalizers();
    }

Rules — never use no more that one dot

— one dot

var range = ((Range)xlWorksheet.Cells[rowIndex, setColumn]);
var hyperLinks = range.Hyperlinks;
hyperLinks.Add(range, data);

— Two or more dots

 (Range)xlWorksheet.Cells[rowIndex, setColumn]).Hyperlinks.Add(range, data);

— Example

 using Microsoft.Office.Interop.Excel;

 Application xls = null;
 Workbooks workBooks = null;
 Workbook workBook = null;
 Sheets sheets = null;
 Worksheet workSheet1 = null;
 Worksheet workSheet2 = null;

 workBooks = xls.Workbooks;
 workBook = workBooks.Open(workSpaceFile);
 sheets = workBook.Worksheets;
 workSheet1 = (Worksheet)sheets[1];


// removing from Memory
 if (xls != null)
 {    
   foreach (Microsoft.Office.Interop.Excel.Worksheet sheet in sheets)
   {
      ReleaseObject(sheet);
   }

   ReleaseObject(sheets);
   workBook.Close();
   ReleaseObject(workBook);
   ReleaseObject(workBooks);

   xls.Application.Quit(); // THIS IS WHAT IS CAUSES EXCEL TO CLOSE
   xls.Quit();
   ReleaseObject(xls);

   sheets = null;
   workBook = null;
   workBooks = null;
   xls = null;

   GC.Collect();
   GC.WaitForPendingFinalizers();
   GC.Collect();
   GC.WaitForPendingFinalizers();
}

Hi,

I am using the following code to open an existing Excel file and saving it as different format.

However the Excel.exe process is not closing…

  

Imports Microsoft.Office.Interop
      
Dim strPath As String
        Dim strFile As String
        Dim strExtension As String
        Dim strPathFile As String

        strPath = "c:test"
        strFile = "test"
        strExtension = ".xls"
        strPathFile = strPath + strFile + strExtension



        Try

            Dim xlApp = New Excel.Application
            xlApp.Application.DisplayAlerts = False
            Dim xlWorkBooks As Excel.Workbooks = xlApp.Workbooks
            Dim xlWorkBook As Excel.Workbook = xlWorkBooks.Open(strPathFile, System.Reflection.Missing.Value, System.Reflection.Missing.Value, _
                                                  System.Reflection.Missing.Value, System.Reflection.Missing.Value, System.Reflection.Missing.Value, _
                                                  System.Reflection.Missing.Value, System.Reflection.Missing.Value, System.Reflection.Missing.Value, _
                                                  System.Reflection.Missing.Value, System.Reflection.Missing.Value, System.Reflection.Missing.Value, _
                                                  System.Reflection.Missing.Value, System.Reflection.Missing.Value, System.Reflection.Missing.Value)
            Dim xlSheets As Excel.Sheets = xlWorkBook.Sheets
            Dim xlWorkSheet As Excel.Worksheet = xlSheets(1)

            strFile = "test2"
            strPathFile = strPath + strFile + strExtension

            xlWorkSheet.SaveAs(strPathFile, Microsoft.Office.Interop.Excel.XlFileFormat.xlExcel8, Type.Missing, Type.Missing, False, False, _
                              Microsoft.Office.Interop.Excel.XlSaveAsAccessMode.xlNoChange, _
                              Microsoft.Office.Interop.Excel.XlSaveConflictResolution.xlLocalSessionChanges, Type.Missing, Type.Missing)

            MsgBox("open")
            xlWorkBook.Close()
            xlWorkBooks.Close()
            xlApp.Quit()

            System.Runtime.InteropServices.Marshal.ReleaseComObject(xlWorkSheet)
            System.Runtime.InteropServices.Marshal.ReleaseComObject(xlSheets)
            System.Runtime.InteropServices.Marshal.ReleaseComObject(xlWorkBook)
            System.Runtime.InteropServices.Marshal.ReleaseComObject(xlWorkBooks)
            System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp)

        Catch ex As Exception
            MsgBox("error")
        End Try

Open in new window

Понравилась статья? Поделить с друзьями:
  • Excel interop create file
  • Excel interop column width
  • Excel interop cells value
  • Excel intermediate что это
  • Excel interior что это