C#interop:在将新工作表添加到现有文件后,excel进程不会退出

C# interop: excel process not exiting after adding new worksheet to existing file

本问题已经有最佳答案,请猛点这里访问。

0

我在这里读过许多其他关于在使用.NET Excel Interop时管理COM引用的线程,以确保Excel进程在退出时正确退出,到目前为止,这些技术工作得很好,但最近我在向现有工作簿文件添加新工作表时遇到了一个问题。

下面的代码将留下僵尸Excel进程。

如果将工作表添加到新创建的工作簿文件中,它将退出。如果我运行不包括.Add()行的代码,它将很好地退出。(我正在读取的现有文件是由注释掉的代码创建的空文件)

有什么想法吗?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
//using Excel = Microsoft.Office.Interop.Excel;
//using System.Runtime.InteropServices;
public static void AddTest()
{
  string filename = @"C:\addtest.xls";
  object m = Type.Missing;
  Excel.Application excelapp = new Excel.Application();
  if (excelapp == null) throw new Exception("Can't start Excel");
  Excel.Workbooks wbs = excelapp.Workbooks;

  //if I create a new file and then add a worksheet,
  //it will exit normally (i.e. if you uncomment the next two lines
  //and comment out the .Open() line below):
  //Excel.Workbook wb = wbs.Add(Excel.XlWBATemplate.xlWBATWorksheet);
  //wb.SaveAs(filename, m, m, m, m, m,
  //          Excel.XlSaveAsAccessMode.xlExclusive,
  //          m, m, m, m, m);

  //but if I open an existing file and add a worksheet,
  //it won't exit (leaves zombie excel processes)
  Excel.Workbook wb = wbs.Open(filename,
                               m, m, m, m, m, m,
                               Excel.XlPlatform.xlWindows,
                               m, m, m, m, m, m, m);

  Excel.Sheets sheets = wb.Worksheets;

  //This is the offending line:
  Excel.Worksheet wsnew = sheets.Add(m, m, m, m) as Excel.Worksheet;

  //N.B. it doesn't help if I try specifying the parameters in Add() above

  wb.Save();
  wb.Close(m, m, m);

  //overkill to do GC so many times, but shows that doesn't fix it
  GC();
  //cleanup COM references
  //changing these all to FinalReleaseComObject doesn't help either
  while (Marshal.ReleaseComObject(wsnew) > 0) { }
  wsnew = null;
  while (Marshal.ReleaseComObject(sheets) > 0) { }
  sheets = null;
  while (Marshal.ReleaseComObject(wb) > 0) { }
  wb = null;
  while (Marshal.ReleaseComObject(wbs) > 0) { }
  wbs = null;
  GC();
  excelapp.Quit();
  while (Marshal.ReleaseComObject(excelapp) > 0) { }
  excelapp = null;
  GC();
}

public static void GC()
{
  System.GC.Collect();
  System.GC.WaitForPendingFinalizers();
  System.GC.Collect();
  System.GC.WaitForPendingFinalizers();
}


我也做过类似的事情。我创建一个Excel文件或打开一个现有文件。我删除所有的工作表并添加我自己的工作表。以下是我用来确保关闭所有引用的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
            workbook.Close(true, null, null);
            excelApp.Quit();

            if (newSheet != null)
            {
                System.Runtime.InteropServices.Marshal.ReleaseComObject(newSheet);
            }
            if (rangeSelection != null)
            {
            System.Runtime.InteropServices.Marshal.ReleaseComObject(rangeSelection);
            }
            if (sheets != null)
            {
                System.Runtime.InteropServices.Marshal.ReleaseComObject(sheets);
            }
            if (workbook != null)
            {
                System.Runtime.InteropServices.Marshal.ReleaseComObject(workbook);
            }
            if (excelApp != null)
            {
                System.Runtime.InteropServices.Marshal.ReleaseComObject(excelApp);
            }

            newSheet = null;
            rangeSelection = null;
            sheets = null;
            workbook = null;
            excelApp = null;

            GC.Collect();

我已经用许多不同的选项测试了这个,但还没有失败。


以下是我的完整代码,用于终止使用Office12.NET Interop库创建的Excel:享受,-艾伦。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;
using Microsoft.Office.Interop.Excel;

class Program
{

    /// <summary>
    /// Win32 API import for getting the process Id.
    /// The out param is the param we are after. I have no idea what the return value is.
    /// </summary>
    [DllImport("user32.dll")]
    private static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, out IntPtr ProcessId);

    static void Main(string[] args)
    {
        var app = new Application();
        IntPtr hwnd = new IntPtr(app.Hwnd);
        IntPtr processId;
        IntPtr foo = GetWindowThreadProcessId(hwnd, out processId);
        Process proc = Process.GetProcessById(processId.ToInt32());
        proc.Kill(); // set breakpoint here and watch the Windows Task Manager kill this exact EXCEL.EXE
        app.Quit(); // should give you a"Sorry, I can't find this Excel session since you killed it" Exception.
    }
}


我没有现成的代码,但我也遇到了类似的问题。如果我记得正确的话,我最终会检索excel实例的进程ID,并终止它(经过一段合适的等待时间,当另一个方法失败时)。

我想我用过:

excel object hwnd属性上的GetWindowThreadProcessId(通过p/invoke)获取进程ID,然后使用Process.GetProcessById获取进程对象。一旦我这样做了,我会在这个过程中给Kill打电话。

编辑:我不得不承认,这不是理想的解决方案,但是如果你找不到没有被释放的流氓界面,那么这将以真正的蛋壳/大锤的方式修复它。;)

edit2:您不必立即在进程对象上调用Kill…你可以先打电话给Close,然后再打给Kill


这对我来说很好,没有任何例外。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Public Class ExcelHlpr

    Declare Function EndTask Lib"user32.dll" (ByVal hWnd As IntPtr, ByVal ShutDown As Boolean, ByVal Force As Boolean) As Integer

    Dim cXlApp As Microsoft.Office.Interop.Excel.Application

    Public Function GetExcel() As Microsoft.Office.Interop.Excel.Application
        cXlApp = New Microsoft.Office.Interop.Excel.Application
        Return cXlApp
    End Function

    Public Function EndExcel() As Integer
        Dim xlHwnd As New IntPtr(cXlApp.Hwnd)
        Return EndTask(xlHwnd, False, True)
    End Function

End Class

安德鲁,这是我找到的代码。我想我把它贴在这里给其他遇到的人:

2


我使用的是vb.net 3.5 sp1,以下代码仍保持Excel.exe打开状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
        xlWorkbook.Close(SaveChanges:=False)
        xlApplication.Quit()

        System.Runtime.InteropServices.Marshal.ReleaseComObject(xlRange)
        System.Runtime.InteropServices.Marshal.ReleaseComObject(xlWorksheet)
        System.Runtime.InteropServices.Marshal.ReleaseComObject(xlSheets)
        System.Runtime.InteropServices.Marshal.ReleaseComObject(xlWorkbook)
        System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApplication)

        xlRange = Nothing
        xlWorksheet = Nothing
        xlSheets = Nothing
        xlWorkbook = Nothing
        xlApplication = Nothing

        GC.GetTotalMemory(False)
        GC.Collect()
        GC.WaitForPendingFinalizers()

        GC.Collect()
        GC.WaitForPendingFinalizers()
        GC.Collect()
        GC.GetTotalMemory(True)


我知道这不是很有建设性,但我按照上面所示对代码进行了测试,我的Excel进程按预期退出,我的C:addtest.xls有8张新工作表,没有Excel进程正在运行。
我想知道的是Interop版本吗?我用11&12测试过。