【问题标题】:Closing Excel Application Process in C# after Data Access数据访问后在 C# 中关闭 Excel 应用程序进程
【发布时间】:2013-07-21 22:20:35
【问题描述】:

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

【问题讨论】:

  • Excel 和 Word 的速度非常很慢,并且带有大量的怪癖,就像你偶然发现的那样。这些文件是 zip 文件,其中包含一些 XML 和其他内容。还有可以打开文档的 Open XML SDK(或者可能是更新的东西)。这样的代码也可以在没有本地安装 Office 的情况下工作。考虑不使用 Excel

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


【解决方案1】:

试试这个:

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

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

Workbook.close SaveChanges, filename, routeworkbook 

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

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

【讨论】:

  • 谢谢亲爱的迈克尔,它工作正常:excelBook.Close(0)
  • 嗨,伙计们,当您打开 Excel 文件进行阅读时,这不起作用。以下是代码 var excelApp = new Application(); var workBooks = excelApp.Workbooks.Open@"c:\temp\myexcel.xlsx"); workBooks.Close(0); excelApp.Quit();
  • 我使用了applicationClass...并且我使用上面的代码来处理对象,但它不会工作...
  • 在多次尝试所有这些答案都失败后,我使用this solution 获取我打开的进程ID并直接将其杀死。
  • @Singaravelan:你检查过大卫克拉克的答案吗?
【解决方案2】:
xlBook.Save();
xlBook.Close(true);
xlApp.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp);

试试这个……它对我有用…… 您应该释放该 xl 应用程序对象以停止该进程。

【讨论】:

  • 我必须释放我的工作簿和工作表才能停止该过程。我正在使用 powershell,所以它看起来像 [System.Runtime.InteropServices.Marshal]::ReleaseComObject($xlBook) 。该过程在大约 4 秒后停止。
【解决方案3】:

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

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

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

...因为通过这种方式,您不仅可以为工作簿创建 RCW 对象,还可以为工作簿创建 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);
}

【讨论】:

  • 这对我有用,将工作簿分配给一个变量以便可以清除它。还有一个观察结果,我有完全相同的代码来清除 Web 应用程序和控制台应用程序中的 excel 应用程序。删除双点更新之前的代码在控制台应用程序中运行良好,但在 Web 应用程序中运行时,它没有清除 EXCEL.EXE。我不知道为什么他们的行为不同,但摆脱双点引用在网络应用程序中修复了它。
  • 为我工作。我认为要释放comobject,我们必须释放所有相关的对象。我对solidworks程序有同样的问题。
  • 关闭工作簿,退出 Excel 应用程序,避免双点调用,并释放创建的每个 COM 对象。这对我有用。救命的答案。谢谢。
【解决方案4】:

想想这个,它杀死了进程:

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

【讨论】:

  • 感谢您的回答。您的第一个解决方案将关闭所有打开的 Excel 文件。当我使用“excelBook.Close”时,会出现 excel 保存对话框,我不想要它:(
  • 我对其进行了编辑并添加了一行以在代码中执行 saveAs,这将用代码替换对话框,希望对您有所帮助。:)
  • 我只是将它用于对打开的 Excel 进程的初始检查,并将 ID 保存在列表中。然后创建了一个新的 Excel 进程,进行了我必须进行的编辑并关闭了 ID 列表中未包含的所有 Excel 进程。
  • 从任务管理器中释放excel不是正确的方法。您应该释放所有创建的对象,包括那些在幕后创建的对象,例如 WorkBOOKs。
  • 这样,你会杀死每一个正在运行的 Excel 进程。问题是,可能有其他用户或应用程序同时使用 Excel。请记住,如果您愿意这样做。
【解决方案5】:

杀死 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

【讨论】:

  • 这是唯一对我有用的解决方案。谢谢。
【解决方案6】:

我遇到了同样的问题并尝试了很多方法来解决它,但都不起作用。 最后,我顺便找到了。一些参考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();
                    }        
                }
            }

【讨论】:

    【解决方案7】:

    我发现在 While 循环中包含 Marshal.ReleaseComObject 很重要 以垃圾收集结束。

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

    【讨论】:

    • 您也可以调用一次FinalReleaseComObject(),而不是在while循环中调用ReleaseComObject()。这会将引用计数器重置为 0 而无需循环。请确保在您确定没有其他对 COM 对象的引用时才这样做。
    【解决方案8】:

    您可以使用自己的COM object 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();
                    }
                }
    

    【讨论】:

      【解决方案9】:

      关闭所有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 工作表,那么你的代码也可以杀死它。
      【解决方案10】:
      excelBook.Close();
      excelApp.Quit();
      

      添加代码末尾,就足够了。它正在处理我的代码

      【讨论】:

      • 是的,但是当我使用它时,会出现保存对话框,我不希望它出现:(
      【解决方案11】:
               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 { }
                   }
               }
      

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

      【讨论】:

        【解决方案12】:

        基于另一种解决方案。我用过这个:

        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

        【讨论】:

          【解决方案13】:

          为每个 Excel 对象使用一个变量,并且必须循环 Marshal.ReleaseComObject &gt;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);
                  }
          }
          

          【讨论】:

            【解决方案14】:

            我们可以使用以下代码在将 xls 转换为 xlsx 时关闭 Excel 应用程序。 当我们执行此类任务时,Excel应用程序正在任务管理器中运行,我们应该关闭这个在后台运行的excel。 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);
               }
            

            【讨论】:

              【解决方案15】:

              大多数方法都有效,但 excel 进程始终停留到关闭应用程序。

              一旦终止 excel 进程,它就不能在同一个线程中再次执行 - 不知道为什么。

              【讨论】:

                【解决方案16】:
                workbook.Close(0);
                excelApp.Quit();
                

                为我工作。

                【讨论】:

                  【解决方案17】:

                  此问题的另一个解决方案是保存您正在使用的 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 进程

                  【讨论】:

                    【解决方案18】:
                            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 excel .... 杀了之后…… 尽情享受吧!!!

                    【讨论】:

                      【解决方案19】:
                      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();
                          }
                      }
                      

                      【讨论】:

                      • 虽然此代码可能会解决问题,但 including an explanation 关于如何以及为什么解决问题将真正有助于提高您的帖子质量,并可能导致更多的赞成票。请记住,您正在为将来的读者回答问题,而不仅仅是现在提出问题的人。请edit您的答案添加解释,并说明适用的限制和假设。
                      猜你喜欢
                      • 1970-01-01
                      • 2012-09-13
                      • 2016-06-28
                      • 2014-06-06
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多