我通过组合一系列不同Excel工作簿的第一张表,在c#中构建一个新的Excel工作簿;随后我将新的工作簿导出为PDF。我做了这个工作,但是在方法结束时总是有一个Excel实例运行。
我讨论了here同样的问题,设置更简单,我可以解决更少的Excel对象使用GC.Collect命令。现在,这些都不起作用。
public void CombineWorkBooks()
{
Microsoft.Office.Interop.Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();
xlApp.DisplayAlerts = false;
xlApp.Visible = false;
Workbooks newBooks = null;
Workbook newBook = null;
Sheets newBookWorksheets = null;
Worksheet defaultWorksheet = null;
// Create a new workbook, comes with an empty default worksheet");
newBooks = xlApp.Workbooks;
newBook = newBooks.Add(XlWBATemplate.xlWBATWorksheet);
newBookWorksheets = newBook.Worksheets;
// get the reference for the empty default worksheet
if (newBookWorksheets.Count > 0)
{
defaultWorksheet = newBookWorksheets[1] as Worksheet;
}
// loop through every line in Gridview and get the path' to each Workbook
foreach (GridViewRow row in CertificadosPresion.Rows)
{
string path = row.Cells[0].Text;
string CertName = CertificadosPresion.DataKeys[row.RowIndex].Value.ToString();
Workbook childBook = null;
Sheets childSheets = null;
// Excel of each line in Gridview
childBook = newBooks.Open(path,Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);
childSheets = childBook.Worksheets;
if (childSheets != null)
{
// Build a new Worksheet
Worksheet sheetToCopy = null;
// Only first Worksheet of the Workbook belonging to that line
sheetToCopy = childSheets[1] as Worksheet;
if (sheetToCopy != null)
{
// Assign the Certificate Name to the new Worksheet
sheetToCopy.Name = CertName;
// set PageSetup for the new Worksheet to be copied
sheetToCopy.PageSetup.Zoom = false;
sheetToCopy.PageSetup.FitToPagesWide = 1;
sheetToCopy.PageSetup.FitToPagesTall = 1;
sheetToCopy.PageSetup.PaperSize = Microsoft.Office.Interop.Excel.XlPaperSize.xlPaperA4;
// Copy that new Worksheet to the defaultWorksheet
sheetToCopy.Copy(defaultWorksheet, Type.Missing);
}
System.Runtime.InteropServices.Marshal.ReleaseComObject(sheetToCopy);
childBook.Close(false, Type.Missing, Type.Missing);
}
System.Runtime.InteropServices.Marshal.ReleaseComObject(childSheets);
System.Runtime.InteropServices.Marshal.ReleaseComObject(childBook);
}
//Delete the empty default worksheet
if (defaultWorksheet != null) defaultWorksheet.Delete();
//Export to PDF
newBook.ExportAsFixedFormat(Microsoft.Office.Interop.Excel.XlFixedFormatType.xlTypePDF, @"C:\pdf\" + SALESID.Text + "_CertPres.pdf", 0, false, true);
newBook.Close();
newBooks.Close();
xlApp.DisplayAlerts = true;
DownloadFile(SALESID.Text);
System.Runtime.InteropServices.Marshal.ReleaseComObject(defaultWorksheet);
System.Runtime.InteropServices.Marshal.ReleaseComObject(newBookWorksheets);
System.Runtime.InteropServices.Marshal.ReleaseComObject(newBook);
System.Runtime.InteropServices.Marshal.ReleaseComObject(newBooks);
xlApp.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp);
GC.Collect();
GC.WaitForPendingFinalizers();
}
protected void DownloadFile(string Salesid)
{
string path = @"c:\\pdf\" + Salesid + "_CertPres.pdf";
byte[] bts = System.IO.File.ReadAllBytes(path);
Response.Clear();
Response.ClearHeaders();
Response.AddHeader("Content-Type", "Application/octet-stream");
Response.AddHeader("Content-Length", bts.Length.ToString());
Response.AddHeader("Content-Disposition", "attachment; filename=" + Salesid + "_CertPres.pdf");
Response.BinaryWrite(bts);
Response.Flush();
Response.End();
}
问题必定与DownloadFile方法的调用有关。我取消了该调用,并且Excel进程已正确关闭。其中一些操作必须保持对其中一个COM对象的引用,以便它们无法关闭。通过调用" DownloadFile"在GarbageCollect之后的最后问题解决了。 (我不太清楚为什么)
答案 0 :(得分:0)
我发现有时唯一有帮助的是“大锤方法”。杀死所有正在运行的Excel实例:
foreach (Process p in Process.GetProcessesByName("EXCEL"))
{
try
{
p.Kill();
p.WaitForExit();
}
catch
{
//Handle exception here
}
}
答案 1 :(得分:0)
在我看来你没有清理过参考文献。可能类似于“双点规则”'问题 - 在我看来这是一个愚蠢的规则,因为你不能编写任何体面的代码,因为它难以跟踪。
您可以尝试使用COM引用的Marshal.ReleaseComObject,但仍然会遇到麻烦......
我的建议是尝试使用VSTO自动化Excel。这将代表您正确清除您的参考文献。
答案 2 :(得分:0)
在您的方法DownloadFile中,您可以调用
Response.End()
HttpResponse.End抛出异常(强调我的):
为了模仿ASP中End方法的行为,此方法尝试引发ThreadAbortException异常。如果此尝试成功,调用线程将被中止,[...]
此异常会中止您的线程。因此,所有ReleaseComObject,Excel.Quit,GC.Collect东西都不会被执行。
解决方案:不要拨打Response.End
。你可能不需要它。如果您需要,可能需要考虑替代mentioned in the documentation:
此方法仅用于与ASP兼容,即与ASP.NET之前的基于COM的Web编程技术兼容。如果您想跳到EndRequest事件并向客户端发送响应,通常最好调用CompleteRequest。
[...]
CompleteRequest方法不会引发异常,并且可能会执行对CompleteRequest方法的调用后的代码
PS:使用Web应用程序中的Excel自动化not officially supported by Microsoft。对于将来的开发,您可能需要考虑使用第三方Excel库。