【问题标题】:Excel process doesn't get closedExcel 进程未关闭
【发布时间】:2011-10-21 02:25:42
【问题描述】:

我的 EXCEL (32) 进程使用完毕后无法关闭它。

正如您在下面的代码中看到的,一旦ProcessRFAFData 函数执行完毕,EXCEL 进程就不会关闭(我仍然可以在任务管理器中看到EXCEL.EXE*32)。

因此,当SaveErrors 开始执行时,我得到以下异常:

System.Runtime.InteropServices.COMException (0x800A03EC):   
Microsoft Office Excel cannot open or save any more documents because there is not enough available memory or disk space.   
• To make more memory available, close workbooks or programs you no longer need.   
• To free disk space, delete files you no longer need from the disk you are saving to.  
at Microsoft.Office.Interop.Excel.Workbooks.Add(Object Template)   
at NextG.RFAFImport.Layouts.NextG.RFAFImport.RFAFDataImporter.<>c__DisplayClass9.b__6()   
at Microsoft.SharePoint.SPSecurity.<>c__DisplayClass4.b__2()   
at Microsoft.SharePoint.Utilities.SecurityContext.RunAsProcess(CodeToRunElevated secureCode)   
at Microsoft.SharePoint.SPSecurity.RunWithElevatedPrivileges(WaitCallback secureCode, Object param)   
at Microsoft.SharePoint.SPSecurity.RunWithElevatedPrivileges(CodeToRunElevated secureCode)  
at NextG.RFAFImport.Layouts.NextG.RFAFImport.RFAFDataImporter.SaveErrors()

这是执行 Excel 进程的代码:

try {
    ProcessRFAFData(FileName);
} catch (Exception ex) {
    Status = "ERROR: " + ex.ToString();
}

if (Errors.Count() > 0) {
    SaveErrors();
}

以下是与 Excel 交互的所有函数:

private void ReleaseObject(object obj) {
    try {
        System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
        obj = null;
    } catch (Exception) { } finally {
        GC.Collect();
    }
}

private void ProcessRFAFData(string FileName) {
    Microsoft.Office.Interop.Excel.Application XLA = null;
    Microsoft.Office.Interop.Excel.Workbook XLW = null;
    Microsoft.Office.Interop.Excel.Worksheet XLS = null;

    bool error = false;

    try {
        SPSecurity.RunWithElevatedPrivileges(delegate() {
            XLA = new Microsoft.Office.Interop.Excel.Application();
            XLW = XLA.Workbooks.Open(FileName, 0, true,
                Type.Missing, null, null, true, Microsoft.Office.Interop.Excel.XlPlatform.xlWindows,
                Type.Missing, false, false, Type.Missing, false, Type.Missing, Type.Missing);

            int index = RFAFTabExists(ref XLW);
            if (index == 0) return;

            XLS = (Microsoft.Office.Interop.Excel.Worksheet)XLW.Worksheets.get_Item(index);

            if (!ValidProjectID(ref XLS)) return;

            ParseData(ref XLS);

            XLW.Close(true, Type.Missing, Type.Missing);
            XLA.Quit();

            ReleaseObject(XLS);
            ReleaseObject(XLW);
            ReleaseObject(XLA);
        });
    } catch (SP.ServerException ex) {
        // output error
    } catch (Exception ex) {
        // output error
    }
}

private int RFAFTabExists(ref Microsoft.Office.Interop.Excel.Workbook XLW) {
    int index = 0;
    foreach (Microsoft.Office.Interop.Excel.Worksheet w in XLW.Worksheets) {
        if (w.Name.Equals(settings.Collection["RFAFTabName"])) index++;
    }

    return index;
}

private bool ValidProjectID(ref Microsoft.Office.Interop.Excel.Worksheet XLS) {
    using (SP.ClientContext CTX = new SP.ClientContext(SiteURL)) {
        var projectId = XLS.Cells.get_Range(settings.Collection["ProjectIDCell"], Type.Missing).Text.ToString();

        var project = // getting list of projects from SharePoint

        if (project.Count() > 0) {
            ProjectID = XLS.Cells.get_Range(settings.Collection["ProjectIDCell"], Type.Missing).Text.ToString();
            return true;
        }
    }

    return false;
}

private void ParseData(ref Microsoft.Office.Interop.Excel.Worksheet XLS) {
    ListData.Add("HID", GetHID(XLS.Cells.get_Range(settings.Collection["HIDCell"],
        Type.Missing).Text.ToString()));

    if (ListData["HID"].Equals("0")) Errors.Add(new ImportError {
        Reason = "Hub ID does not exist in this project workspace.",
        Reference = string.Format("Hub ID: {0}", XLS.Cells.get_Range(settings.Collection["HIDCell"],
        Type.Missing).Text.ToString())
    });

    int row = Int32.Parse(settings.Collection["StartRow"]);
    while (!NoMoreData(ref XLS, row)) {
        string PRSIN = XLS.Cells.get_Range(string.Format("{0}{1}",
            settings.Collection["PRSIN"], row), Type.Missing).Text.ToString();

        string NOC = ValidateNumber(XLS.Cells.get_Range(string.Format("{0}{1}",
            settings.Collection["NOC"], row), Type.Missing).Text.ToString());

        string UEIRP = ValidateNumber(XLS.Cells.get_Range(string.Format("{0}{1}",
            settings.Collection["UEIRP"], row), Type.Missing).Text.ToString());

        string LAT = ValidateLatLon(XLS.Cells.get_Range(string.Format("{0}{1}",
            settings.Collection["LAT"], row), Type.Missing).Text.ToString());

        string LON = ValidateLatLon(XLS.Cells.get_Range(string.Format("{0}{1}",
            settings.Collection["LON"], row), Type.Missing).Text.ToString());

        string PJ = GetPJ(XLS.Cells.get_Range(string.Format("{0}{1}",
            settings.Collection["JurisdictionCol"], row), Type.Missing).Text.ToString(),
            XLS.Cells.get_Range(string.Format("{0}{1}", settings.Collection["StateCol"], row),
            Type.Missing).Text.ToString());

        string ST = GetState(XLS.Cells.get_Range(string.Format("{0}{1}",
            settings.Collection["JurisdictionCol"], row), Type.Missing).Text.ToString(),
            XLS.Cells.get_Range(string.Format("{0}{1}", settings.Collection["StateCol"], row),
            Type.Missing).Text.ToString());

        ListItemData.Add(new ListItem {
            ProposedRemoteSiteItemNumber = PRSIN,
            NumberOfCarriers = NOC,
            UsableEIRP = UEIRP,
            Latitude = LAT,
            Longitude = LON,
            PrimaryJurisdiction = PJ,
            State = ST
        });

        row++;
    }
}

private bool NoMoreData(ref Microsoft.Office.Interop.Excel.Worksheet XLS, int row) {
    return string.IsNullOrEmpty(XLS.Cells.get_Range(string.Format("{0}{1}",
        settings.Collection["ProposedRemoteSiteItemNumberCol"], row), Type.Missing).Text.ToString());
}

private void SaveErrors() {
    Microsoft.Office.Interop.Excel.Application XLA = null;
    Microsoft.Office.Interop.Excel.Workbook XLW = null;
    Microsoft.Office.Interop.Excel.Worksheet XLS = null;

    object MissingValue = System.Reflection.Missing.Value;

    try {
        try {
            SPSecurity.RunWithElevatedPrivileges(delegate() {
                XLA = new Microsoft.Office.Interop.Excel.Application();
                XLW = XLA.Workbooks.Add(MissingValue);
                XLS = (Microsoft.Office.Interop.Excel.Worksheet)XLW.Worksheets.get_Item(1);

                XLS.Cells[1, 1] = "Reason for error";
                XLS.Cells[1, 2] = "Reference";

                XLS.get_Range("A1").Font.Bold = true;
                XLS.get_Range("B1").Font.Bold = true;

                int row = 2;
                foreach (ImportError e in Errors) {
                    XLS.Cells[row, 1] = e.Reason;
                    XLS.Cells[row, 2] = e.Reference;

                    row++;
                }

                XLW.SaveAs(ErrorLogFileName, Microsoft.Office.Interop.Excel.XlFileFormat.xlWorkbookNormal,
                    MissingValue, MissingValue, MissingValue, MissingValue,
                    Microsoft.Office.Interop.Excel.XlSaveAsAccessMode.xlExclusive, MissingValue,
                    MissingValue, MissingValue, MissingValue, MissingValue);

                XLW.Close(true, MissingValue, MissingValue);
                XLA.Quit();

                ReleaseObject(XLS);
                ReleaseObject(XLW);
                ReleaseObject(XLA);
            });
        } catch (Exception ex) {
            Status = "ERROR: " + ex.ToString();
        }

        // Uploading excel file to SharePoint document library

    } catch (Exception) { }
}

【问题讨论】:

  • 一般来说,您永远不应该在服务器环境中使用 Office Interop。如果您使用的是普通的 ASP.NET,我会说您 1005 可能会失败。既然是 SharePoint,我只能说有 80% 的可能性。
  • @John Saunders:我同意。但有时你必须做一些讨厌的事情!
  • 你读过这个 (stackoverflow.com/questions/158706/…) 对吗?
  • @Mark:不,我没有。精彩的。按照帖子中的建议尝试。非常感谢:)

标签: c# .net excel sharepoint pia


【解决方案1】:

看看这个链接

Kill Process Excel C#

感谢 KD7 的解决方案。

一个小的修改是,如果您的 excelapp 不可见,您的主窗口标题将为空白。例如。 ""

private void KillSpecificExcelFileProcess(string excelFileName)
    {
        var processes = from p in Process.GetProcessesByName("EXCEL")
                        select p;

        foreach (var process in processes)
        {
            MessageBox.Show(process.MainWindowTitle);
            if (process.MainWindowTitle == excelFileName)
            {

            process.Kill();
            }
        }
    }

并杀死所有僵尸excel后台进程'

KillSpecificExcelFileProcess("");

这给我解决问题带来了无穷无尽的麻烦,所以我将把它发布在每个僵尸 Excel 进程线程上。

【讨论】:

    【解决方案2】:

    除了上述解决方案之外,还有我的LOGICAL解决方案。 我在想为什么我有这么多 EXCEL.EXE 应用程序并以这种方式处理。 在这里,我在创建新 Excel 应用程序时检查条件。 如果(_masterApp == null) _masterApp = new EXCEL.Application(); 如果要创建的变量为 Null,则仅创建 Excel 应用程序的新实例,否则重新分配现有变量。

    并使用本博客中提到的解决方案关闭 _masterApp。

    好处:这样你只会关闭你通过解决方案打开的EXCEL应用程序,而不会关闭手动打开的Excel应用程序。

    【讨论】:

      【解决方案3】:

      就我而言,Excel 加载项位于我之前创建的单独 AppDomain 中,但卸载加载项时,我从未调用过:

      childDomain.Unload();
      

      一旦调用,Excel Zombie 进程就不再存在...

      【讨论】:

        【解决方案4】:

        第一个建议:http://code.google.com/p/excellibrary 这是一个很棒的库,我使用过并取得了很大的成功。

        第二个建议:如果你绝对必须关闭 Excel

        /// <summary>
        /// Gets all currently running instances of Excel, so we don't kill active windows.
        /// </summary>
        private void GetInstancesToSave()
        {
            if (_instancesToSaveList != null)
            {
                _instancesToSaveList.Clear();
            }
            _instancesToSaveList = Process.GetProcesses().ToList<Process>();
            _instancesToSaveList = _instancesToSaveList.FindAll(proc => proc.ProcessName == "EXCEL");
        }
        
        /// <summary>
        /// Kills any instances of Excel that were created by the program.
        /// </summary>
        /// <param name="zInstancesToSave">Instances that were not </param>
        private static void KillExcel(List<Process> zInstancesToSave)
        {
            List<Process> xProcesslist = Process.GetProcesses().ToList<Process>();
            xProcesslist = xProcesslist.FindAll(proc => proc.ProcessName == "EXCEL");
            foreach (Process xTheprocess in xProcesslist)//read through all running programs
            {
                bool killit = true;
                foreach (Process proc in zInstancesToSave)//read through all running programs
                {
                    if (xTheprocess.Id == proc.Id)
                    {
                        killit = false;
                    }
                }
                if (killit)
                {
                    xTheprocess.Kill();
                }
            }
        }
        

        您可以使用这两种方法来跟踪启动时正在运行的 Excel 实例,然后找到您的应用打开的任何 Excel 实例,然后将其终止。这当然不是一个很好的解决方案,但有时您只能硬着头皮。

        如果您不关心之前的实例,您也可以这样做:

        /// <summary>
        /// Kills any instances of Excel that were created by the program.
        /// </summary>
        /// <param name="zInstancesToSave">Instances that were not </param>
        private static void KillExcel(List<Process> zInstancesToSave)
        {
            List<Process> xProcesslist = Process.GetProcesses().ToList<Process>();
            xProcesslist = xProcesslist.FindAll(proc => proc.ProcessName == "EXCEL");
            foreach(Proc process in xProcesslist)
            {
                process.Kill();
            }
        }
        

        【讨论】:

          【解决方案5】:

          我会考虑使用 Code Plex 中的EPPlus
          这个example 展示了如何读取数据。在走这条路线的服务器上,您总是更好 - 唯一的问题是如果您需要调用公式 - 那么这种方法将不起作用。
          您的大部分代码将与您当前的代码非常相似,因此我估计需要几个小时才能迁移到此库。

          【讨论】:

            【解决方案6】:

            在 finally 块中释放您的 Excel 对象,以防出现异常。

            try
            {
            ...
            }
            catch
            {
            ...
            }
            finally
            {
              ReleaseObject(XLS);
              ReleaseObject(XLW);
              ReleaseObject(XLA);
            }
            

            【讨论】:

              【解决方案7】:

              你可能不得不去可笑explicit

              excelWorkbook.Close (false, System.Reflection.Missing.Value,System.Reflection.Missing.Value) ;   
              excelWorkbooks.Close();  
              excelApp.Quit();  
              Marshal.ReleaseComObject(excelWorksheet);  
              Marshal.ReleaseComObject(excelSheets);  
              Marshal.ReleaseComObject(excelWorkbooks);  
              Marshal.ReleaseComObject(excelWorkbook);  
              Marshal.ReleaseComObject(excelApp);  
              excelWorksheet = null;  
              excelSheets = null;  
              excelWorkbooks = null;  
              excelWorkbook = null;  
              excelApp = null;  
              GC.GetTotalMemory(false);  
              GC.Collect();  
              GC.WaitForPendingFinalizers();  
              GC.Collect();  
              GC.GetTotalMemory(true);  
              

              我遇到过即使那样做也没有的情况。我求助于追查 Excel 进程并在其上调用了 Kill()。

              【讨论】:

              • 这是释放Excel并允许Excel关闭的正确方法。这里唯一缺少的是,如果您通过主互操作在工作簿中编辑单元格,您还需要在您访问的每个单元格上执行 Marshal.ReleaseComObject,然后将其清空。对于您创建的任何变量都是如此,然后将一个 excel 对象分配给您的变量。包括每个 R 作为 ListObject.Rows 中的范围。 R 必须通过 Marshaling 释放,然后在循环到下一个元素之前清空。基本上,如果您没有发布您接触的所有内容,那么 Excel 将保持打开状态。
              • 请注意我所指的循环,对于 ListObject.Rows 中的每个 R 作为 Range 只是一个示例。这一切都非常具有挑战性。我在使用 Primary Interop 构建一个实用程序时遇到了一些相同的问题,该实用程序允许某人在工作簿中创建和应用样式,然后将这些样式与其他工作簿同步。粗...
              猜你喜欢
              • 1970-01-01
              • 2013-02-21
              • 1970-01-01
              • 2011-10-24
              • 2014-04-10
              • 1970-01-01
              • 2015-01-11
              • 1970-01-01
              相关资源
              最近更新 更多