【问题标题】:How to properly close/dispose Excel COMs in C# ?(Excel process not closing) [duplicate]如何在 C# 中正确关闭/处理 Excel COM?(Excel 进程未关闭)[重复]
【发布时间】:2015-01-11 14:31:37
【问题描述】:

是的,我知道,它遍布整个互联网,可能也在这里,我遵循了很多例子,但它仍然没有关闭,我真的厌倦了这个问题,拜托,如果有人能找到除此之外的解决方案杀掉excel进程,帮忙分享一下!

我的代码:

            #region ////////// Declarations and initialisations //////////

            string[] sFilesPath = System.IO.Directory.GetFiles(Path); // Get all files from current Path

            MSExcel.Application xlApp = null;
            MSExcel.Workbook xlWorkBook = null;
            MSExcel.Workbooks xlWorkBooks = null;
            MSExcel.Sheets xlSheets = null;
            MSExcel.Shapes xlShapes = null;
            MSExcel.Worksheet xlWorkSheet = null;
            MSExcel.Worksheets xlWorkSheets = null;

            xlApp = new MSExcel.Application(); xlApp.Visible = false; xlApp.DisplayAlerts = false;
            xlWorkBooks = xlApp.Workbooks;
            xlWorkBook = xlWorkBooks.Open(Path + _xlNamesList[i] + ".xlsx");
            xlSheets = xlWorkBook.Sheets;

            #endregion \\\\\\\\\\ Declarations and initialisations \\\\\\\\\\

            #region ////////// Clear all previous WorkSheets //////////

            foreach (MSExcel.Worksheet Worksheet in xlSheets)
            {
                Worksheet.Cells.Clear();
                foreach (MSExcel.Shape sh in Worksheet.Shapes)
                {
                    sh.Delete();
                    Marshal.ReleaseComObject(sh);
                }
            }
            if (xlSheets != null) Marshal.ReleaseComObject(xlSheets);

            #endregion \\\\\\\\\\ Clear all previous WorkSheets \\\\\\\\\\

            #region ////////// Insert each screenshot at it's respective location //////////

            foreach (string File in sFilesPath)
            {
                string sFileExtension = System.IO.Path.GetExtension(File);
                if (sFileExtension == ".jpg") // Insert each jpg file in it's coresponding place
                {
                    //--->  Declarations and initialisations
                    string sFileNameWitouthExtension = System.IO.Path.GetFileNameWithoutExtension(File),
                           sShiftName = sFileNameWitouthExtension.Substring(sFileNameWitouthExtension.Length - 7),
                           sLineName = sFileNameWitouthExtension.Substring(0, sFileNameWitouthExtension.Length - 8);

                    xlWorkSheet = xlWorkBook.Worksheets[sLineName]; // Get the coresponding worksheet to edit based on LineName

                    //--->  Place the screenshot in the Excel file based on sShiftName
                    if (sShiftName == "Shift 1")
                    {
                        xlWorkSheet.Cells[1, 4] = sFileNameWitouthExtension;
                        xlWorkSheet.Cells[1, 4].Font.Size = 30;
                        xlWorkSheet.Cells[1, 4].Rows.AutoFit();
                        xlWorkSheet.Cells[1, 4].Columns.AutoFit();

                        xlWorkSheet.Shapes.AddPicture(File, Microsoft.Office.Core.MsoTriState.msoFalse, Microsoft.Office.Core.MsoTriState.msoCTrue, 50, 40, 800, 500);
                    }
                    else
                        if (sShiftName == "Shift 2")
                        {
                            xlWorkSheet.Cells[40, 4] = sFileNameWitouthExtension;
                            xlWorkSheet.Cells[40, 4].Font.Size = 30;
                            xlWorkSheet.Cells[40, 4].Rows.AutoFit();
                            xlWorkSheet.Cells[40, 4].Columns.AutoFit();

                            xlWorkSheet.Shapes.AddPicture(File, Microsoft.Office.Core.MsoTriState.msoFalse, Microsoft.Office.Core.MsoTriState.msoCTrue, 50, 650, 800, 500);
                        }
                        else
                            if (sShiftName == "Shift 3")
                            {
                                xlWorkSheet.Cells[78, 4] = sFileNameWitouthExtension;
                                xlWorkSheet.Cells[78, 4].Font.Size = 30;
                                xlWorkSheet.Cells[78, 4].Rows.AutoFit();
                                xlWorkSheet.Cells[78, 4].Columns.AutoFit();

                                xlWorkSheet.Shapes.AddPicture(File, Microsoft.Office.Core.MsoTriState.msoFalse, Microsoft.Office.Core.MsoTriState.msoCTrue, 50, 1245, 800, 500);
                            }
                }
            }
            #endregion \\\\\\\\\\ Insert each screenshot at it's respective location \\\\\\\\\\

            #region ////////// Save EXCEL file and release/close objects //////////

            xlWorkBook.SaveAs(Path + _xlNamesList[i++] + ".xlsx", MSExcel.XlFileFormat.xlWorkbookDefault, Type.Missing, Type.Missing, false, false, MSExcel.XlSaveAsAccessMode.xlNoChange, MSExcel.XlSaveConflictResolution.xlLocalSessionChanges, Type.Missing, Type.Missing);
            xlWorkBook.Close();
            xlApp.Quit();

            if (xlShapes != null) { Marshal.ReleaseComObject(xlShapes); xlShapes = null; }
            if (xlWorkSheet != null) { Marshal.ReleaseComObject(xlWorkSheet); xlWorkSheet = null; }
            if (xlWorkSheets != null) { Marshal.ReleaseComObject(xlWorkSheets); xlWorkSheets = null; }
            if (xlWorkBook != null) { Marshal.ReleaseComObject(xlWorkBook); xlWorkBook = null; }
            if (xlWorkBooks != null) { Marshal.ReleaseComObject(xlWorkBooks); xlWorkBooks = null; }
            if (xlApp != null) { Marshal.ReleaseComObject(xlApp); xlApp = null; }

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

            #endregion \\\\\\\\\\ Save EXCEL file and release/close objects \\\\\\\\\\

            #region ////////// Kill EXCEL proccess ---> DON'T WANT THIS!!! //////////

            foreach (Process Proc in Process.GetProcesses())
                if (Proc.ProcessName.Equals("EXCEL"))
                    Proc.Kill();

            #endregion \\\\\\\\\\ Kill EXCEL proccess \\\\\\\\\\

在我通过 GC 之后,Excel 进程仍然存在:/,它已经让我发疯了。请帮忙!!!

// 编辑:感谢 Hans 将其标记为重复,另一篇帖子最终回答了我的问题,所有的 tumb 规则和点和对象释放都是废话,你只需要将 GC 放在一个不在内部的方法之外...

【问题讨论】:

  • 您可以调用ReleaseComObject-方法,只要对象上存在引用即可。为确保不再有引用,只需在循环中调用它,直到它返回 0。请删除 GC 调用。不管你说什么 GC,它都会发挥作用......
  • 我在这个例子之后工作过,add-in-express.com/creating-addins-blog/2013/11/05/…,我会尝试按照你说的做,在一个循环中,但不知道我传递给的 com 应该是什么类型函数,例如 public int releaseComs(what_argument?) {return 0;}
  • 虽然它是 != null 时对象上仍然存在很多引用,并且在我的帖子代码中,您可以看到我总是在 ReleaseComObject 之后将它们设置为 null。所以即使我把它放在一个循环中,它也总是会运行 1 次,因为我总是将它们设置为 null(在你取消引用它之后表明这样做)
  • 将引用设置为 null 仅用于帮助自动垃圾收集,无论您的 excel 进程是否保留(除非您将它们设置为 null before 告诉Marshal.ReleaseComObject() 释放对象,在这种情况下你会告诉它释放 NULL)
  • 您是否在此代码中省略了任何 try/catch 代码?

标签: c# multithreading excel visual-studio com


【解决方案1】:

问题是您必须获取每个(中间)对象并在其上调用ReleaseComObject。否则你有一些悬而未决的东西不会被释放并且excel不会关闭。

以下是导致这些问题的代码的一些不良示例:

 xlWorkSheet = xlWorkBook.Worksheets[sLineName];

这样您就有了对Worksheets 的悬空引用。最好这样写:

 xlWorkSheets = xlWorkBook.Worksheets;
 xlWorkSheet = xlWorkSheets[sLineName];

还有一个:

xlWorkSheet.Cells[40, 4].Rows.AutoFit();

这样应该会更好:

var cells = xlWorkSheet.Cells[40,4];
var rows = cells.Rows;
rows.AutoFit();

所有这些中间对象都将被放入HashSet<object>。最后,我将遍历这个列表并调用它们的 release 方法。

经验法则是:如果您的命令中有多个点,就会出现问题。

为了找到这样的问题,我只创建和关闭了一个 excel 实例来启动我的包装器。然后我测试了excel打开并立即关闭。然后我开始慢慢访问一些对象并对其进行测试。在我添加的每新两行之间,我测试了当我调用 quit 方法时 excel 是否仍然关闭。到目前为止,这是一种非常困难且令人沮丧的方式,但也是唯一可靠地清理所有对象的方式。

【讨论】:

  • 大声笑,是的,在你给出的示例中,这就是我真正打算做的,但我忘记了,我尽我所能尊重拇指规则,但我在某个时候迷路了,我会重新查看代码并尽可能多地尝试应用它,但我似乎仍然看不到如何将它应用到,比如说,我将图片添加为形状的部分。
  • @VenomXLR:注释xlApp = new MSExcel.Application()xlWorkBook.Close();之间的所有代码。然后测试excel是否打开和关闭(它真的应该!)。然后开始慢慢取消注释您的代码并测试excel是否仍然关闭。这是我发现可靠地找到有关互操作的这个 f*** 错误的唯一方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-10-21
  • 2021-05-24
  • 2011-10-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多