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

时间:2013-07-21 22:20:35

标签: c# excel visual-studio-2012 excel-interop kill-process

我正在用C#编写一个应用程序,它打开一个用于读/写操作的Excel模板文件。我想当用户关闭应用程序时,excel应用程序进程已关闭,而不保存excel文件。多次运行应用程序后,请参阅我的任务管理器。

enter image description here

我使用此代码打开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中看到类似的问题,例如this questionthis,以及测试答案,但它不起作用。

19 个答案:

答案 0 :(得分:77)

试试这个:

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

关闭工作簿时,您有三个可选参数:

Workbook.close SaveChanges, filename, routeworkbook 

Workbook.Close(false)或者如果你正在进行后期绑定,有时候使用零更容易 Workbook.Close(0) 这就是我自动关闭工作簿时的方法。

我也去找了它的文档,在这里找到它: Excel Workbook Close

谢谢,

答案 1 :(得分:20)

xlBook.Save();
xlBook.Close(true);
xlApp.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp);
试试这个..它对我有用...... 你应该释放那个xl应用程序对象来停止进程。

答案 2 :(得分:10)

想一想,它会杀死这个过程:

System.Diagnostics.Process[] process=System.Diagnostics.Process.GetProcessesByName("Excel");
foreach (System.Diagnostics.Process p in process)
{
    if (!string.IsNullOrEmpty(p.ProcessName))
    {
        try
        {
            p.Kill();
        }
        catch { }
    }
}

另外,您是否尝试过正常关闭它?

myWorkbook.SaveAs(@"C:/pape.xltx", missing, missing, missing, missing, missing, Microsoft.Office.Interop.Excel.XlSaveAsAccessMode.xlNoChange, missing, missing, missing, missing, missing);
excelBook.Close(null, null, null);                 // close your workbook
excelApp.Quit();                                   // exit excel application
excel = null;                                      // set to NULL

答案 3 :(得分:10)

参考:https://stackoverflow.com/a/17367570/132599

  

避免使用双点调用表达式,例如:

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

...因为通过这种方式,您不仅可以为工作簿创建RCW对象,还可以为Workbooks创建RCW对象,并且也应该释放它(如果不维护对象的引用,则无法实现)。

这解决了我的问题。您的代码变为:

public Excel.Application excelApp = new Excel.Application();
public Excel.Workbooks workbooks;
public Excel.Workbook excelBook;
workbooks = excelApp.Workbooks;
excelBook = workbooks.Add(@"C:/pape.xltx");

...

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

然后释放所有这些对象:

System.Runtime.InteropServices.Marshal.ReleaseComObject(rng);
System.Runtime.InteropServices.Marshal.ReleaseComObject(excelSheet);
System.Runtime.InteropServices.Marshal.ReleaseComObject(sheets);
excelBook .Save();
excelBook .Close(true);
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlBook);
System.Runtime.InteropServices.Marshal.ReleaseComObject(workbooks);
excelApp.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp);

我将它包装在try {} finally {}中,以确保即使出现问题(可能出错的地方,一切都会被释放),例如。

public Excel.Application excelApp = null;
public Excel.Workbooks workbooks = null;
...
try
{
    excelApp = new Excel.Application();
    workbooks = excelApp.Workbooks;
    ...
}
finally
{
    ...
    if (workbooks != null) System.Runtime.InteropServices.Marshal.ReleaseComObject(workbooks);
    excelApp.Quit();
    System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp);
}

答案 4 :(得分:4)

杀死Excel并不总是那么容易;请参阅此文章:50 Ways to Kill Excel

本文从Microsoft(MS Knowlege Base Article)获得了有关如何让Excel快速退出的最佳建议,但是如果有必要,还可以通过终止该过程来确保它。我喜欢第二个降落伞。

确保关闭所有打开的工作簿,退出应用程序并释放xlApp对象。最后检查过程是否仍然存在,如果是,则将其杀死。

本文还确保我们不会终止所有Excel进程,但只会杀死已启动的确切进程。

另见Get Process from Window Handle

以下是我使用的代码:(每次都有效)

Sub UsingExcel()

    'declare process; will be used later to attach the Excel process
    Dim XLProc As Process

    'call the sub that will do some work with Excel
    'calling Excel in a separate routine will ensure that it is 
    'out of scope when calling GC.Collect
    'this works better especially in debug mode
    DoOfficeWork(XLProc)

    'Do garbage collection to release the COM pointers
    'http://support.microsoft.com/kb/317109
    GC.Collect()
    GC.WaitForPendingFinalizers()

    'I prefer to have two parachutes when dealing with the Excel process
    'this is the last answer if garbage collection were to fail
    If Not XLProc Is Nothing AndAlso Not XLProc.HasExited Then
        XLProc.Kill()
    End If

End Sub

'http://msdn.microsoft.com/en-us/library/ms633522%28v=vs.85%29.aspx
<System.Runtime.InteropServices.DllImport("user32.dll", SetLastError:=True)> _
    Private Shared Function GetWindowThreadProcessId(ByVal hWnd As IntPtr, _
    ByRef lpdwProcessId As Integer) As Integer
End Function

Private Sub ExcelWork(ByRef XLProc As Process)

    'start the application using late binding
    Dim xlApp As Object = CreateObject("Excel.Application")

    'or use early binding
    'Dim xlApp As Microsoft.Office.Interop.Excel

    'get the window handle
    Dim xlHWND As Integer = xlApp.hwnd

    'this will have the process ID after call to GetWindowThreadProcessId
    Dim ProcIdXL As Integer = 0

    'get the process ID
    GetWindowThreadProcessId(xlHWND, ProcIdXL)

    'get the process
    XLProc = Process.GetProcessById(ProcIdXL)


    'do some work with Excel here using xlApp

    'be sure to save and close all workbooks when done

    'release all objects used (except xlApp) using NAR(x)


    'Quit Excel 
    xlApp.quit()

    'Release
    NAR(xlApp)

End Sub

Private Sub NAR(ByVal o As Object)
    'http://support.microsoft.com/kb/317109
    Try
        While (System.Runtime.InteropServices.Marshal.ReleaseComObject(o) > 0)
        End While
    Catch
    Finally
        o = Nothing
    End Try
End Sub

答案 5 :(得分:1)

excelBook.Close(); excelApp.Quit(); 添加代码的结尾,就足够了。它正在处理我的代码

答案 6 :(得分:1)

我发现在Marshal.ReleaseComObject循环中加入While很重要,并且 完成垃圾收集

static void Main(string[] args)
{
    Excel.Application xApp = new Excel.Application();
    Excel.Workbooks xWbs = xApp.Workbooks;
    Excel.Workbook xWb = xWbs.Open("file.xlsx");

    Console.WriteLine(xWb.Sheets.Count);

    xWb.Close();
    xApp.Quit();

    while (Marshal.ReleaseComObject(xWb) != 0);
    while (Marshal.ReleaseComObject(xWbs) != 0);
    while (Marshal.ReleaseComObject(xApp) != 0);

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

答案 7 :(得分:1)

您可以使用自己的COM对象excel pid

来终止进程

在dll导入代码下面添加一些内容

[DllImport("user32.dll", SetLastError = true)]
private static extern int GetWindowThreadProcessId(IntPtr hwnd, ref int lpdwProcessId);

并使用

 if (excelApp != null)
            {
                int excelProcessId = -1;
                GetWindowThreadProcessId(new IntPtr(excelApp.Hwnd), ref excelProcessId);

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

答案 8 :(得分:1)

我遇到了同样的问题并尝试了很多方法来解决它,但是没有用。 最后,我找到了我的方式。一些参考enter link description here

希望我的代码可以帮助未来的人。我花了两天多的时间来解决它。 以下是我的代码:

//get current in useing excel
            Process[] excelProcsOld = Process.GetProcessesByName("EXCEL");
            Excel.Application myExcelApp = null;
            Excel.Workbooks excelWorkbookTemplate = null;
            Excel.Workbook excelWorkbook = null;
try{
    //DO sth using myExcelApp , excelWorkbookTemplate, excelWorkbook
}
catch (Exception ex ){
}
finally
            {
                //Compare the EXCEL ID and Kill it 
                Process[] excelProcsNew = Process.GetProcessesByName("EXCEL");
                foreach (Process procNew in excelProcsNew)
                {
                    int exist = 0;
                    foreach (Process procOld in excelProcsOld)
                    {
                        if (procNew.Id == procOld.Id)
                        {
                            exist++;
                        }
                    }
                    if (exist == 0)
                    {
                        procNew.Kill();
                    }        
                }
            }

答案 9 :(得分:0)

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

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

_excel.Quit();
_excel = null;
var process = System.Diagnostics.Process.GetProcessesByName("Excel");
foreach (var p in process) {
    if (!string.IsNullOrEmpty(p.ProcessName)) {
        try {
            p.Kill();
        } catch { }
    }
}

答案 10 :(得分: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:这是我的Application Interop excel objecto

答案 11 :(得分:0)

为每个Excel对象使用一个变量,并且必须循环Marshal.ReleaseComObject >0。没有循环,Excel进程仍然保持活动状态。

public class test{
        private dynamic ExcelObject;
        protected dynamic ExcelBook;
        protected dynamic ExcelBooks;
        protected dynamic ExcelSheet;

public void LoadExcel(string FileName)
        {
            Type t = Type.GetTypeFromProgID("Excel.Application");
            if (t == null) throw new Exception("Excel non installato");
            ExcelObject = System.Activator.CreateInstance(t);
            ExcelObject.Visible = false;
            ExcelObject.DisplayAlerts = false;
            ExcelObject.AskToUpdateLinks = false;
            ExcelBooks = ExcelObject.Workbooks;
            ExcelBook = ExcelBooks.Open(FileName,0,true);
            System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application");
            ExcelSheet = ExcelBook.Sheets[1];
        }
 private void ReleaseObj(object obj)
        {
            try
            {
                int i = 0;
             while(   System.Runtime.InteropServices.Marshal.ReleaseComObject(obj) > 0)
                {
                    i++;
                    if (i > 1000) break;
                }
                obj = null;
            }
            catch 
            {
                obj = null;
            }
            finally
            {
                GC.Collect();
            }
        }
        public void ChiudiExcel() {
            System.Threading.Thread.CurrentThread.CurrentCulture = ci;

            ReleaseObj(ExcelSheet);
            try { ExcelBook.Close(); } catch { }
            try { ExcelBooks.Close(); } catch { }
            ReleaseObj(ExcelBooks);
            try { ExcelObject.Quit(); } catch { }
            ReleaseObj(ExcelObject);
        }
}

答案 12 :(得分: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);
   }

答案 13 :(得分:0)

大多数方法都有效,但是excel进程始终存在,直到关闭应用程序为止。

一旦杀死excel进程,就无法在同一线程中再次执行-不知道为什么。

答案 14 :(得分:0)

RelativeSource Binding

关闭名称为“Excel”的最后10秒进程

答案 15 :(得分:0)

(None, 64)

为我工作。

答案 16 :(得分:0)

此问题的另一个解决方案是保存您正在使用的 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 进程,如任务管理器中所示

答案 17 :(得分:-1)

        GetWindowThreadProcessId((IntPtr)app.Hwnd, out iProcessId);
        wb.Close(true,Missing.Value,Missing.Value);
        app.Quit();
        System.Diagnostics.Process[] process = System.Diagnostics.Process.GetProcessesByName("Excel");
        foreach (System.Diagnostics.Process p in process)
        {
            if (p.Id == iProcessId)
            {
                try
                {
                    p.Kill();
                }
                catch { }
            }
        }
}
[DllImport("user32.dll")]

private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

uint iProcessId = 0;

此GetWindowThreadProcessId找到正确的进程ID o excell .... 杀了之后.... 享受它!!!

答案 18 :(得分:-1)

process()
{
    rem=$(( $$ % 2 ))
...
}

export -f process
for i in {1..100}; do
   bash -c "process" $i &
...