在C#中关闭Excel应用程序进程后访问数据

98
我正在使用C#编写一个应用程序,该程序打开一个Excel模板文件进行读/写操作。我希望当用户关闭应用程序时,Excel应用程序进程已关闭,且不保存Excel文件。在多次运行应用程序后查看我的任务管理器。
我使用以下代码打开Excel文件:
public Excel.Application excelApp = new Excel.Application();
public Excel.Workbook excelBook;
excelBook = excelApp.Workbooks.Add(@"C:/pape.xltx");

我使用以下代码来访问数据:

Excel.Worksheet excelSheet = (Worksheet)(excelBook.Worksheets[1]);
excelSheet.DisplayRightToLeft = true;
Range rng;
rng = excelSheet.get_Range("C2");
rng.Value2 = txtName.Text;

我在stackoverflow上看到了类似的问题,比如这个问题这个问题,以及测试答案,但都不起作用。


1
Excel和Word非常慢,并且存在许多怪癖,例如您遇到的问题。这些文件是带有一些XML和其他内容的zip文件。还有Open XML SDK(或者可能是更近期的东西)可以打开文档。这样的代码也可以在本地没有安装Office的情况下工作。考虑不使用Excel。 - Sten Petrov
22个回答

1

我将写下我所做的,以防未来有人需要帮助。

在执行保存另存为退出之前进行此操作可以解决我的问题。其他方法可能也需要这样做。

m_pXLApp->PutDisplayAlerts( VARIANT_FALSE );

不再出现悬挂的EXCEL.EXE

从我所了解到的微软文档中,这被设计为使用户不再收到提示保存工作的消息,但也适用于其他消息,如兼容性问题。

这些消息从来没有在我这里显示过,但是有些东西阻止了Excel退出。尝试跟踪COM对象上的ref计数之后,这是我剩下的唯一一种“千钧一发”的方法。

希望这能帮助某人。


1
using System;
using Excel = Microsoft.Office.Interop.Excel;

private Excel.Worksheet excelSheet;
private Excel.Workbook wb;
private Excel.Application excel;
public void Close()
{
    wb.Close(true);
    excel.Quit();
    System.Runtime.InteropServices.Marshal.ReleaseComObject(excel);
    System.Runtime.InteropServices.Marshal.ReleaseComObject(wb);
    if(excelSheet!=null)
       System.Runtime.InteropServices.Marshal.ReleaseComObject(excelSheet);
    GC.Collect();
}

在关闭应用程序和工作簿后,调用GC collect以强制进行垃圾回收。


1

解决这个问题的另一个方法是保存你正在使用的Excel程序的ProcessID。然后当你完成程序时,你可以专门终止该Excel进程,而不会影响其他Excel进程。

我从this的答案中得到了解决方案。想在这里分享一下

所以首先,在类方法之外添加这些代码行

// The DllImport requires -- Using System.Runtime.InteropServices;
[DllImport("user32.dll", SetLastError = true)]
private static extern int GetWindowThreadProcessId(IntPtr hwnd, ref int lpdwProcessId);

之后,

现在,在您选择的方法中添加以下行。 这些行将丢弃您正在处理的特定 Excel 进程。

按需修改它们,但逻辑相同。

        if (ExcelApp != null)
        {
            int excelProcessId = 0;

            //your Excel Application variable has access to its Hwnd property
            GetWindowThreadProcessId(new IntPtr(ExcelApp.Hwnd), ref excelProcessId);

            // you need System.Diagnostics to use Process Class
            Process ExcelProc = Process.GetProcessById(excelProcessId);

            if (ExcelProc != null)
            {
                ExcelProc.Kill();
            }
        }

因此,你的程序总体应该是这样的。
class Program
{
    [DllImport("user32.dll", SetLastError = true)]
    private static extern int GetWindowThreadProcessId(IntPtr hwnd, ref int lpdwProcessId);

    static void Main(string[] args)
    {
        Application ExcelApp = new Application();

        _Workbook ExcelWrkBook = ExcelApp.Workbooks.Open(filePath);

        _Worksheet ExcelWrkSht = ExcelWrkBook.ActiveSheet;

        ExcelWrkSht.Cells[1, 2] = "70";

        if (ExcelApp != null)
        {
            int excelProcessId = 0; // simple declare, zero is merely a place holder

            GetWindowThreadProcessId(new IntPtr(ExcelApp.Hwnd), ref excelProcessId);

            Process ExcelProc = Process.GetProcessById(excelProcessId);

            if (ExcelProc != null)
            {
                ExcelProc.Kill();
            }
        }

    }
}

我已经测试过了,它可以像任务管理器中显示的那样删除我的 Excel 进程。


1
大多数方法都有效,但Excel进程始终保持到关闭应用程序为止。
一旦杀死Excel进程,就无法在同一线程中再次执行-不知道为什么。

1
excelBook.Close();
excelApp.Quit();

在代码结尾添加,就足够了。这对我的代码有效。

是的,但当我使用它时,保存对话框会出现,而我不希望它出现 :( - Javad Yousefi

1

关闭所有Excel进程的正确方法

var _excel = new Application();
foreach (Workbook _workbook in _excel.Workbooks) {
    _workbook.Close(0);
}

_excel.Quit();
_excel = null;

使用进程示例,这可能会关闭所有Excel进程。
var process = System.Diagnostics.Process.GetProcessesByName("Excel");
foreach (var p in process) {
    if (!string.IsNullOrEmpty(p.ProcessName)) {
        try {
            p.Kill();
        } catch { }
    }
}

这也会关闭在VS环境之外打开的任何合法Excel实例。例如,假设我在实际的Excel应用程序中打开了另一个Excel工作表,那么您的代码也会将其关闭。 - Mark

1
workbook.Close(0);
excelApp.Quit();

对我有用。


0
我们可以使用以下代码在将xls转换为xlsx时关闭Excel应用程序。当我们执行这种任务时,Excel应用程序在任务管理器中运行,我们应该关闭后台运行的Excel。Interop是一个Com组件,为了释放com组件,我们使用了Marshal.FinalReleaseComObject。
 private void button1_Click(object sender, EventArgs e)
    {

        Excel03to07("D:\\TestExls\\TestExcelApp.XLS");

    }
    private void Excel03to07(string fileName)
    {
        string svfileName = Path.ChangeExtension(fileName, ".xlsx");
        object oMissing = Type.Missing;
        var app = new Microsoft.Office.Interop.Excel.Application();
        var wb = app.Workbooks.Open(fileName, oMissing, oMissing,
                        oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing, oMissing);
        wb.SaveAs(svfileName, XlFileFormat.xlOpenXMLWorkbook, Type.Missing, Type.Missing, Type.Missing, Type.Missing, XlSaveAsAccessMode.xlNoChange, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);

        wb.Close(false, Type.Missing, Type.Missing);
        app.Quit();
        GC.Collect();
        Marshal.FinalReleaseComObject(wb);
        Marshal.FinalReleaseComObject(app);
   }

0

根据其他解决方案,我使用了这个:

IntPtr xAsIntPtr = new IntPtr(excelObj.Application.Hwnd);
excelObj.ActiveWorkbook.Close();

System.Diagnostics.Process[] process = System.Diagnostics.Process.GetProcessesByName("Excel");
                foreach (System.Diagnostics.Process p in process)
                {
                    if (p.MainWindowHandle == xAsIntPtr)
                    {
                        try
                        {
                            p.Kill();
                        }
                        catch { }
                    }
                }

使用“MainWindowHandle”来识别进程并关闭它。
excelObj:这是我的应用程序Interop Excel对象。

0
我想出了一个解决这个问题的好方法,适用于许多用例。
请查看我之前发布的帖子,获取代码和解释: https://dev59.com/cGQm5IYBdhLWcg3wwxLF#75414974 以下是代码:
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();
            }
        }
    }
}

你可以通过以下方式调用它: YourNameSpace.MicrosoftApplications.Excel xlApp = new YourNameSpace.MicrosoftApplications.Excel();

要退出,用户关闭窗口或者通过编程方式调用 xlApp.Kill();


我真的很希望有人能试用我的代码,以证明它的优秀表现。我之前只是在原帖中发布了一个链接,解决了这个问题,但却被投票否决了,因为有些人对此感到不满。我本以为Stack Exchange会希望将相同的答案放在一个主题下,但我想我错了。 - LancourWestbrook

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接