【问题标题】:Interop Excel not closing process互操作 Excel 未关闭进程
【发布时间】:2013-02-21 16:47:19
【问题描述】:

我已经为这个问题苦苦挣扎了几天,我已经进行了研究并应用了我在各个论坛上找到的所有建议,但我仍然无法解决它。

我的问题是使用互操作库的 excel,我有一个 excel 文件用作模板,所以我打开它并用新名称保存在新位置。一切都很好,只是 Excel 进程在文件创建和关闭后继续运行。

这是我的代码

protected string CreateExcel(string strProjectID, string strFileMapPath)
{
    string strCurrentDir = HttpContext.Current.Server.MapPath("~/Reports/Templates/");
    string strFile = "Not_Created";

    Application oXL;
    Workbook oWB;        

    oXL = new Application();
    oXL.Visible = false;

    Workbooks wbks = oXL.Workbooks;
    //opening template file
    oWB = wbks.Open(strFileMapPath);        

    oXL.Visible = false;
    oXL.UserControl = false;
    strFile = strProjectID + "_" + DateTime.Now.Ticks.ToString() + ".xlsx";
    //Saving file with new name
   oWB.SaveAs(strCurrentDir + strFile, XlFileFormat.xlWorkbookDefault, null, null,    false, false, XlSaveAsAccessMode.xlExclusive, false, false, null, null);

    oWB.Close(false, strCurrentDir + strFile, Type.Missing);

    wbks.Close();

    oXL.Quit();


    System.Runtime.InteropServices.Marshal.ReleaseComObject(oXL);
    System.Runtime.InteropServices.Marshal.ReleaseComObject(wbks);
    System.Runtime.InteropServices.Marshal.ReleaseComObject(oWB);


    oWB = null;
    oXL = null;
    wbks = null;
    GC.Collect();

    return strFile;
}

如您所见,我正在关闭并释放所有对象,但应用程序并未退出。

我正在使用 IIS7 的 32 位 Windows Server 2008(生产)和 Windows 7(开发)中进行测试。

【问题讨论】:

  • 您不能使用来自 ASP.NET 或其他服务器技术的 Office Interop。见Considerations for server-side Automation of Office
  • @JohnSaunders:你可以,这只是一个非常非常糟糕的主意 ;-)
  • @StingyJack:不,那不是 asp.net 造成的。
  • @roma8716:正如 John 指出的那样,使用 ASP.Net 的互操作(根本)不是一个好主意。我建议您要么使用适合此任务的组件(例如epplus.codeplex.com),要么编写一个接受排队请求以处理 Excel 文件的 Windows 服务。

标签: c# asp.net visual-studio-2010 excel office-interop


【解决方案1】:

简单规则:避免使用双点调用表达式,例如:

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

(Reference)

【讨论】:

    【解决方案2】:

    看看这里:How can I get the ProcessID (PID) for a hidden Excel Application instance

    您可以通过GetWindowThreadProcessId API 追踪您的 ProcessID,然后终止与您的 Excel 应用程序对象实例特别匹配的进程。

    [DllImport("user32.dll")]
    static extern int GetWindowThreadProcessId(int hWnd, out int lpdwProcessId);
    
    Process GetExcelProcess(Microsoft.Office.Interop.Excel.Application excelApp)
    {
         int id;
         GetWindowThreadProcessId(excelApp.Hwnd, out id);
         return Process.GetProcessById(id);
    }
    
    void TerminateExcelProcess(Microsoft.Office.Interop.Excel.Application excelApp)
    {
         var process = GetExcelProcess(excelApp);
         if (process != null)
         {
              process.Kill();
         }
    }
    

    【讨论】:

    • 我不想导入 dll。有没有其他方法可以做到这一点。
    • @UmerFarooq 您可以遍历所有Process 实例并检查Process.MainWindowHandleexcelApp.Hwnd。所有Process 实例都可以通过Process.GetProcesses 获得
    【解决方案3】:

    我为它创建了这个方法,在我的测试中它有效。

    private void ClearMemory(Application excelApp) {
        excelApp.DisplayAlerts = false;
        excelApp.ActiveWorkbook.Close(0);
        excelApp.Quit();
        Marshal.ReleaseComObject(excelApp);
    }
    

    【讨论】:

      【解决方案4】:

      这是 VB 版本——我有一个使用它的大型项目,而不是转换到更好系统的时间,所以这里是相同的答案......在 vb.NET 中

      使用它来获取进程 ID(在打开 excel 表之前)

      Dim excelProcess(0) As Process
      excelProcess = Process.GetProcessesByName("excel")
      

      完成工作表后:

      xlWorkBook.Close(SaveChanges:=False)
      xlApp.Workbooks.Close()
      xlApp.Quit()
      'Kill the process
      If Not excelProcess(0).CloseMainWindow() Then
          excelProcess(0).Kill()
      End If
      

      【讨论】:

      • 这不适合 ASP.NET(服务器)方案。您将杀死在您开始处理您的请求之后出现并且当前正在处理其他请求的实例。
      • 谢谢@ZverevEugene - 你能告诉我更好的编码方式吗?
      【解决方案5】:

      这就是我解决这个问题的方法:

      // Store the Excel processes before opening.
      Process[] processesBefore = Process.GetProcessesByName("excel");
      
      // Open the file in Excel.
      Application excelApplication = new Application();
      Workbook excelWorkbook = excelApplication.Workbooks.Open(Filename);
      
      // Get Excel processes after opening the file.
      Process[] processesAfter = Process.GetProcessesByName("excel");
      
      // Now find the process id that was created, and store it.
      int processID = 0;
      foreach (Process process in processesAfter)
      {
          if (!processesBefore.Select(p => p.Id).Contains(process.Id))
          {
              processID = process.Id;
          }
      }
      
      // Do the Excel stuff
      
      // Now close the file with the COM object.
      excelWorkbook.Close();
      excelApplication.Workbooks.Close();
      excelApplication.Quit();
      
      // And now kill the process.
      if (processID != 0)
      {
          Process process = Process.GetProcessById(processID);
          process.Kill();
      }
      

      【讨论】:

      • 这是最好的,因为它首先检查现有实例。
      • 这不适合 ASP.NET(服务器)方案。您将杀死在您开始处理您的请求之后出现并且当前正在处理其他请求的实例。
      【解决方案6】:

      试试

      Process excelProcess = Process.GetProcessesByName("EXCEL")[0];
      if (!excelProcess.CloseMainWindow())
      {
       excelProcess.Kill();
      }
      

      【讨论】:

      • 翻译成VB.Net Dim excelProcess(0) As Process excelProcess = Process.GetProcessesByName("excel")
      • 这不适合 ASP.NET(服务器)方案。您将杀死在您开始处理您的请求之后出现并且当前正在处理其他请求的实例。
      • 这应该可以,但是如果您/(您的客户)打开了另一个可以关闭它而不是这个应用程序的 Excel 工作表
      【解决方案7】:

      尝试使用Open XML SDK 2.0 for Microsoft Office library 创建 excel 文档,而不是使用互操作程序集。根据我的经验,它的运行速度要快得多,而且更容易使用。

      【讨论】:

        【解决方案8】:
        oWB.Close(false, strCurrentDir + strFile, Type.Missing);
        oWB.Dispose();
        wbks.Close();
        wbks.Dispose();
        oXL.Quit();
        oXL.Dispose();
        
        
        System.Runtime.InteropServices.Marshal.ReleaseComObject(oXL);
        System.Runtime.InteropServices.Marshal.ReleaseComObject(wbks);
        System.Runtime.InteropServices.Marshal.ReleaseComObject(oWB);
        

        【讨论】:

        • 如上面问题 cmets 中所述。 ASP.NET 不建议这样做。
        猜你喜欢
        • 2014-12-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-10-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多