简体   繁体   English

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

[英]Closing Excel Application Process in C# after Data Access

I'm writing an application in C# that opens an Excel template file for read/write operations.我正在 C# 中编写一个应用程序,该应用程序打开一个 Excel 模板文件以进行读/写操作。 I want to when user closes the application, excel application process has been closed, without saving excel file.我想当用户关闭应用程序时,excel 应用程序进程已经关闭,而不保存 excel 文件。 See my Task Manager after multiple runs of the app.多次运行应用程序后查看我的任务管理器。

在此处输入图像描述

I use this code to open the excel file:我使用此代码打开 excel 文件:

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

and for data access I use this code:对于数据访问,我使用以下代码:

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

I see similar questions in stackoverflow such as this question and this , and test answers, but it doesn't works.我在 stackoverflow 中看到类似的问题,例如this questionthis ,并测试答案,但它不起作用。

Try this:试试这个:

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

When closing the work-book, you have three optional parameters:关闭工作簿时,您有三个可选参数:

Workbook.close SaveChanges, filename, routeworkbook 

Workbook.Close(false) or if you are doing late binding, it sometimes is easier to use zero Workbook.Close(0) That is how I've done it when automating closing of workbooks. Workbook.Close(false)或者如果您正在进行后期绑定,有时使用零Workbook.Close(0)会更容易,这就是我在自动关闭工作簿时所做的。

Also I went and looked up the documentation for it, and found it here: Excel Workbook Close我也去查找了它的文档,并在这里找到了它: Excel Workbook Close

Thanks,谢谢,

xlBook.Save();
xlBook.Close(true);
xlApp.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp);

try this.. it worked for me... you should release that xl application object to stop the process.试试这个……它对我有用……你应该释放那个 xl 应用程序对象来停止这个过程。

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

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). ...因为通过这种方式,您不仅可以为工作簿创建 RCW 对象,还可以为工作簿创建 RCW 对象,并且您也应该释放它(如果不维护对对象的引用,这是不可能的)。

This resolved the issue for me.这为我解决了这个问题。 Your code becomes:您的代码变为:

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;

And then release all those objects:然后释放所有这些对象:

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);

I wrap this in a try {} finally {} to ensure everything gets released even if something goes wrong (what could possibly go wrong?) eg我将其包装在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);
}

Think of this, it kills the process:想到这一点,它杀死了进程:

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 { }
    }
}

Also, did you try just close it normally?另外,您是否尝试过正常关闭它?

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

Killing Excel is not always easy;杀死 Excel 并不总是那么容易。 see this article: 50 Ways to Kill Excel请参阅这篇文章:杀死 Excel 的 50 种方法

This article takes the best advice from Microsoft ( MS Knowlege Base Article ) on how to get Excel to quit nicely, but then also makes sure about it by killing the process if necessary.本文采纳了 Microsoft( MS Knowledge Base 文章)关于如何让 Excel 很好地退出的最佳建议,但如果有必要,还会通过终止进程来确保它。 I like having a second parachute.我喜欢有第二个降落伞。

Make sure to Close any open workbooks, Quit the application and Release the xlApp object.确保关闭所有打开的工作簿,退出应用程序并释放 xlApp 对象。 Finally check to see if the process is still alive and if so then kill it.最后检查进程是否还活着,如果是,则杀死它。

This article also makes sure that we don't kill all Excel processes but only kills the exact process that was started.本文还确保我们不会终止所有 Excel 进程,而只会终止已启动的确切进程。

See also Get Process from Window Handle另请参阅从窗口句柄获取进程

Here is the code I use: (works every time)这是我使用的代码:(每次都有效)

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

I met the same problems and tried many methods to solve it but doesn't work.我遇到了同样的问题并尝试了很多方法来解决它,但没有用。 Finally , I found the by my way.最后,我找到了我的方式。 Some reference enter link description here一些参考在这里输入链接描述

Hope my code can help someone future.希望我的代码可以帮助未来的人。 I have been spent more than two days to solve it.我已经花了两天多的时间来解决它。 Below is my Code:下面是我的代码:

//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();
                    }        
                }
            }

excelBook.Close(); excelBook.Close(); excelApp.Quit(); excelApp.退出(); add end of the code, it could be enough.添加代码的末尾,就足够了。 it is working on my code它正在处理我的代码

The right way to close all excel process关闭所有excel进程的正确方法

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

_excel.Quit();
_excel = null;

Using process example, this may close all the excel process regardless.使用流程示例,这可能会关闭所有 excel 流程。

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

You may kill process with your own COM object excel pid您可以使用自己的COM对象 excel pid 终止进程

add somewhere below dll import code在 dll 导入代码下方添加某处

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

and use并使用

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

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

I have found that it is important to have Marshal.ReleaseComObject within a While loop AND finish with Garbage Collection .我发现在While循环中使用Marshal.ReleaseComObject并以Garbage Collection结束很重要。

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();
}
         wb.Close();
         app.Quit();

         System.Diagnostics.Process[] process = System.Diagnostics.Process.GetProcessesByName("Excel");
         foreach (System.Diagnostics.Process p in process)
         {
             if (!string.IsNullOrEmpty(p.ProcessName) && p.StartTime.AddSeconds(+10) > DateTime.Now)
             {
                 try
                 {
                     p.Kill();
                 }
                 catch { }
             }
         }

It Closes last 10 sec process with name "Excel"它关闭最后 10 秒的进程,名称为“Excel”

Based on another solutions.基于另一个解决方案。 I have use this:我用过这个:

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 { }
                    }
                }

Using the "MainWindowHandle" to identify the process and close him.使用“MainWindowHandle”来识别进程并关闭他。

excelObj: This is my Application Interop excel objecto excelObj:这是我的应用程序互操作 excel objecto

Use a variable for each Excel object and must loop Marshal.ReleaseComObject >0 .为每个 Excel 对象使用一个变量,并且必须循环Marshal.ReleaseComObject >0 Without the loop, Excel process still remain active.没有循环,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);
        }
}

We can close the Excel Application while converting xls to xlsx by using following code.我们可以在使用以下代码将 xls 转换为 xlsx 的同时关闭 Excel 应用程序。 When we perform this kind of task then Excel application is running in task manager, we Should close this excel which is running in background.当我们执行此类任务时,Excel 应用程序正在任务管理器中运行,我们应该关闭这个在后台运行的 excel。 Interop is a Com component ,to release the com component we used Marshal.FinalReleaseComObject. Interop是一个Com组件,我们使用Marshal.FinalReleaseComObject来释放com组件。

 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);
   }

Most of the methods works, but the excel process always stay until close the appliation.大多数方法都有效,但 excel 过程始终保持到关闭应用程序。

When kill excel process once it can't be executed once again in the same thread - don't know why.当杀死 excel 进程时,它无法在同一线程中再次执行 - 不知道为什么。

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

Worked for me.为我工作。

Another solution to this problem is to save the ProcessID of the Excel program in which you are working with.此问题的另一个解决方案是保存您正在使用的 Excel 程序的 ProcessID。 Then when you are done with the program, you can specifially kill that Excel process without targeting other excel processes.然后,当您完成该程序时,您可以专门终止该 Excel 进程,而无需针对其他 excel 进程。

I got the solution from this answer.我从这个答案中得到了解决方案。 Thought I would share it here以为我会在这里分享

So first, you add these line of code outside of a class method首先,在类方法之外添加这些代码行

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

After that,之后,

Now in a method of your choosing, add these lines.现在以您选择的方法添加这些行。 These lines discard the specific excel process that you are working with这些行丢弃了您正在使用的特定 excel 流程

Modify them to your needs, but the logic is thesame根据您的需要修改它们,但逻辑是相同的

        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();
            }
        }

Hence in total your program should look like this因此,您的程序总共应该是这样的

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();
            }
        }

    }
}

I have tested this and it removes my Excel processes as shown in Task Manager我已经对此进行了测试,它删除了我的 Excel 进程,如任务管理器中所示

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();
}

Call GC collect to force a garbage collection after closing the application and workbook.关闭应用程序和工作簿后调用 GC collect 强制进行垃圾回收。

        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;

this GetWindowThreadProcessId finds the correct Process Id o excell .... After kills it.... Enjoy It!!!这个 GetWindowThreadProcessId 找到了正确的进程 Id o excel.... 杀死它后......享受它!

private void releaseObject(object obj)
{
    try
    {
        System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
        obj = null;
    }
    catch (Exception ex)
    {
        obj = null;
        MessageBox.Show("Unable to release the Object " + ex.ToString());
    }
    finally
    {
        GC.Collect();
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM