var tempFileName = file.Replace(".xlsx", "_Temp.xlsx");
Application excelApp = new Application();
Workbooks books = excelApp.Workbooks;
Workbook excelFile = books.Open(file);
Sheets sheets = excelFile.Worksheets;
var app = excelApp.Application;
DeleteRows(sheets, 3);
excelFile.SaveAs(tempFileName);
excelFile.Close();
books.Close();
app.Quit();
excelApp.Quit();
Marshal.FinalReleaseComObject(sheets);
Marshal.ReleaseComObject(excelFile);
Marshal.ReleaseComObject(books);
Marshal.FinalReleaseComObject(excelApp);
sheets = null;
excelFile = null;
excelApp = null;
books = null;
I am trying to dispose of my excel objects, however even the above code still leaves me with hanging EXCEL.EXE processes after the program has finished running. I have seen this question on here a number of times, but most of the askers had not tried the ReleaseComObject() method, however that did not fix anything for me.
EDIT: This is the code i use to read the table after deleting the header
System.Data.DataTable schemaTable = excelConnection.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, new object[] { null, null, null, "TABLE" });
DataRow schemaRow = schemaTable.Rows[0];
string sheet = schemaRow["TABLE_NAME"].ToString();
if (!sheet.EndsWith("_"))
{
string query = "SELECT * FROM [Daily Payment$]";
OleDbDataAdapter daexcel = new OleDbDataAdapter(query, excelConnection);
dtexcel.Locale = CultureInfo.CurrentCulture;
daexcel.Fill(dtexcel);
var reader = dtexcel.CreateDataReader();
and here is the code for DeleteRows:
void DeleteRows(Sheets sheets, int n)
{
foreach (Worksheet workSheet in sheets)
{
Range range = workSheet.get_Range("A1", "A" + n);
Range row = range.EntireRow;
row.Delete(XlDirection.xlUp);
Marshal.ReleaseComObject(workSheet);
}
}
FINAL EDIT: I have reposted the new code that I have at the top of the post. I am still getting one lingering object (even though i run the process multiple times). Calling Gc.Collect() works, but as a relative neophyte, i figured it was best to avoid something so powerful.
For freeing Office COM interop objects, you can go by the 'one release per . rule'. That is, when you have a chain of member accesses, you need to free all the intermediate objects.
This often means splitting up your code into additional lines.
Following that rule, you're missing one. You need to keep a handle to the Workbooks object you've implicitly created.
Microsoft.Office.Interop.Excel.Application excelApp = new Microsoft.Office.Interop.Excel.Application();
Workbooks books = excelApp.Workbooks;
Workbook excelFile = books.Open(file);
DeleteRows(excelFile, 3);
Then you need to call:
Marshal.ReleaseComObject(books);
at the end.
Failing this, you can also try calling:
books.Close()
before you quit Excel. And to go overkill, you can change all your ReleaseComObject calls to FinalReleaseComObject.
EDIT:
In your newly posted update, you are not releasing your Row or Range objects. Call FinalReleaseComObject on these, and I'd expect the problem to be solved.
Related
Simple code, and I thought it should kill Excel. But the task manager says this leaves an instance of Excel running. What am I missing? Thank you.
using Microsoft.Office.Interop.Excel;
//Stuff...
// Launch dialog picker that return path to Excel file, in string
// called excelTemplate
Microsoft.Office.Interop.Excel.Application xlTemp = new Microsoft.Office.Interop.Excel.Application();
Workbook workbook = xlTemp.Workbooks.Open(excelTemplate);
xlTemp.DisplayAlerts = false;
// Poke through individual sheets, get some info from them
workbook.Close();
xlTemp.Quit();
Application xlTemp = new Application();
Workbooks workbooks = xlTemp.Workbooks;
Workbook workbook = workbooks.Open(excelTemplate);
// Do stuff.
workbook.Close();
Marshal.ReleaseComObject(workbook);
Marshal.ReleaseComObject(workbooks);
xlTemp.Quit();
Marshal.ReleaseComObject(xlTemp);
Aside from needing to release each COM object, note also that by doing xlTemp.Workbooks.Open, you're leaking a COM reference by not assigning xlTemp.Workbooks to a variable that can be released later.
You can used #using to automatically dispose the application.
using(Microsoft.Office.Interop.Excel.Application xlTemp = new
Microsoft.Office.Interop.Excel.Application())
{
Workbook workbook = xlTemp.Workbooks.Open(excelTemplate);
xlTemp.DisplayAlerts = false;
// Poke through individual sheets, get some info from them
workbook.Close();
}
I have a C# program that opens an Excel file, reads a cell, closes the file, and exits Excel. Unfortunately, the Windows Task Manager still shows an Excel.exe process running. I've read just about every article concerning this issue and tried almost all of the solutions . . . and still have the same problem. I believe one of COM objects is not being released and thus hanging the process. However, I also believe that I've been very careful about instantiating the Excel objects (no double ".") and releasing them.
If I remove the "a = xlCells[1,1].Value" line, every thing releases and Excel dies cleanly after the FinalReleaseComObject of the Application instance. Why would this assignment create COM objects or interfere with them?
Excel.Application xlApp = null;
Excel.Workbooks xlWorkbooks = null;
Excel.Workbook xlWorkbook = null;
Excel.Sheets xlSheets = null;
Excel.Worksheet xlWorksheet = null;
Excel.Range xlCells = null;
string inputFile = #"C:\Temp\test.xlsx";
string a;
xlApp = new Excel.Application();
xlApp.Visible = false;
xlApp.DisplayAlerts = false;
xlWorkbooks = xlApp.Workbooks;
xlWorkbook = xlWorkbooks.Open(inputFile);
xlSheets = xlWorkbook.Sheets;
xlWorksheet = xlSheets[1];
xlCells = xlWorksheet.Cells;
a = xlCells[1,1].Value;
Marshal.FinalReleaseComObject(xlCells);
xlCells = null;
Marshal.FinalReleaseComObject(xlWorksheet);
xlWorksheet = null;
Marshal.FinalReleaseComObject(xlSheets);
xlSheets = null;
xlWorkbook.Close(false, Type.Missing, Type.Missing);
Marshal.FinalReleaseComObject(xlWorkbook);
xlWorkbook = null;
xlWorkbooks.Close();
Marshal.FinalReleaseComObject(xlWorkbooks);
xlWorkbooks = null;
xlApp.Quit();
Marshal.FinalReleaseComObject(xlApp);
xlApp = null;
I would make two changes. First, since a Sheets item can be either a worksheet or a graph sheet, it's best to cast with As and check for null. Second, if you just want to get a range with an alphanumeric address, the get_Range() method works well. Otherwise, if you want to go by row and column indexes, then follow #Daneau's comment.
xlWorksheet = xlSheets[1] as Excel.Worksheet;
if(xlWorksheet != null)
{
xlCells = xlWorksheet.get_Range("A1");
a = xlCells[1,1].Value;
Marshal.FinalReleaseComObject(xlCells);
xlCells = null;
Marshal.FinalReleaseComObject(xlWorksheet);
xlWorksheet = null;
}
I changed up the code and added the dummy Range object.
Range tmpCell = xlCell[1,1];
a = tmpCell.Value;
Marshal.ReleaseComObject(tmpCell);
My problems went away. Much thanks, Daneau!
The real routine has several loops with the cell being evaluated. I thought it would work fine using the tmpCell for each new cell assignment, then release tmpCell at the end. That failed. Prior to each new tmpCell assignment to a xlCell[x,y], I had to release tmpCell. This worked, but is certainly cumbersome. It's difficult to believe that there's not a better way to manage this or keep track of the various COM objects.
I am writing an Excel app in C# which does some data visualization. The first stage of the analysis is to copy the data from a single sheet in an input workbook to a new sheet in an output workbook where the analysis is performed.
This runs correctly on my laptop but when the client I am producing the code for runs the same application, they get some extra worksheets created which cause the code to crash.
It's pretty difficult to debug the application on the client's PC so I am having difficultly understanding how it's possible the results are different on two different machines.
Setup code is below, any help at all would be incredibly useful. Using Microsoft.Office.Interop.Excel 14.
Excel.Workbooks wbs = excelApp.Workbooks;
Excel.Workbook dataSource = wbs.Open(inFile);
//get the data sheet from the source file
Excel.Sheets dataSourceSheets = dataSource.Worksheets;
Excel.Worksheet dataSourceSheet = (Excel.Worksheet)dataSourceSheets.get_Item(1);
//create a new workbook
dataOutput = wbs.Add();
outputSheets = dataOutput.Worksheets;
//ensure there are only 2 sheets in the output
int osc = outputSheets.Count;
for (int i = 2; i < osc; ++i)
{
outputSheets.get_Item(i).Delete();
}
//setup summary and contents pages
summaryPage = outputSheets.get_Item(1);
summaryPage.Name = "Summary";
contentsPage = outputSheets.Add(Type.Missing, summaryPage);
contentsPage.Name = "Contents";
dataSourceSheet.Copy(Type.Missing, contentsPage); //copy the sheet AFTER contents page
dataSheet = (Excel.Worksheet)outputSheets.get_Item(outputSheets.Count);
dataSheet.Name = "Raw Data";
//fails with outputSheets.Count == 5
if (outputSheets.Count != 3)
{
throw new Exception("Error, there are too many sheets here!");
}
I have an excel workbook with many, many sheets. I want to delete all the sheets except for three of them.
Specifically, i would like to know if there is a way to remove the sheets using sheet name instead of ordinals (sheet number).
I am using excel interop and C# to work with Excel.
Microsoft.Office.Interop.Excel.Application xlApp = null;
Excel.Workbook xlWorkbook = null;
Excel.Sheets xlSheets = null;
Excel.Worksheet xlNewSheet = null;
xlApp.DisplayAlerts = false;
for (int i = xlApp.ActiveWorkbook.Worksheets.Count; i > 0 ; i--)
{
Worksheet wkSheet = (Worksheet)xlApp.ActiveWorkbook.Worksheets[i];
if (wkSheet.Name == "NameOfSheetToDelete")
{
wkSheet.Delete();
}
}
xlApp.DisplayAlerts = true;
I know this is old but I just use the fallowing
workBook.Sheets["Sheet1"].Delete();
I know this thread is really old but for future visitors, if you know the names of the sheets you want to delete, a better way is to loop over the names of the sheets like this:
Workbook book = excelApp.Workbooks.Open("filePathHere");
string[] sheetsToDelete = {"s1", "s2"};
excelApp.DisplayAlerts = false;
foreach(string sheetToDelete in sheetsToDelete )
{
book.Worksheets[sheetToDelete].Delete();
}
excelApp.DisplayAlerts = true;
It's always good practice to avoid deleting items in a collection while iterating through it.
I wrote a C# application that queries a DB and puts the results in an excel file. The program itself works fine. However if I open a second completely unrelated excel file while the application is running, an exception occurs and the process stops.
Now, in the program itself I've set the visibility to false, and after opening the second unrelated Excel file, the one that was being generated is suddenly open and visible, and then I get the exception.
Anyone know what is causing the problem or how to resolve it?
The relevant parts of the code is as follows, the exception occurs at the line worksheet.get_Range(currCol.GetString() + excelRow, Missing.Value).Formula = item.ToString();
The exception itself is : "Exception from HRESULT: 0x800AC472"
Application exc = new Application();
//Makes the Excel file not visible
exc.Visible = false;
exc.UserControl = false;
exc.DisplayAlerts = false;
Workbooks workbooks = exc.Workbooks;
Workbook workbook = workbooks.Add(XlWBATemplate.xlWBATWorksheet);
Sheets sheets = workbook.Worksheets;
Worksheet worksheet = (Worksheet)sheets.get_Item(1);
int excelRow = 1;
ExcelChar currCol = new ExcelChar('A');
System.Data.DataTable testTable = dbConnection.searchQuery("Select * from testTable").Copy();
if (worksheet == null)
{
Console.WriteLine("ERROR: worksheet == null");
}
foreach (System.Data.DataRow row in testTable.Rows)
{
foreach (var item in row.ItemArray)
{
worksheet.get_Range(currCol.GetString() + excelRow, Missing.Value).Formula = item.ToString();
currCol.Add(1);
}
excelRow++;
currCol = new ExcelChar('A');
}
Take a look at this thread.
It looks like your error is VBA_E_IGNORE, in which case you need to register an IMessageFilter implementation so you can implement retry logic.
I've seen this issue in the past, when using the same instance of Excel with interop and interactively - for example when instatiating an Excel Application object using:
Marshal.GetActiveObject("Excel.Application")
In your case, you're creating a new instance of Excel using:
exc = new Application();
What you should try to do is to make sure you close this instance as quickly as possible. This is not always easy, because of the problem described in this KB article. Otherwise you might consider something other than COM Interop to write to Excel (e.g. OLEDB or a third party library such as Aspose or EPPlus).
When Excel is busy - e.g. has a modal dialog displayed, or is busy loading a workbook, it will not respond to incoming COM messages, so it returns an error which is translated into this exception. An IMessageFilter implementation (specifically: RetryRejectedCall) will typically retry a few times, then either fail or prompt the user to retry ("Server busy").
When using the Office InterOp services, you must close the objects you've created in reverse order.
private static void Excel_FromDataTable(DataTable dt)
{
// Global missing variable.
object missing = System.Reflection.Missing.Value;
// Creates an excel object,
Excel.Application excel = new Microsoft.Office.Interop.Excel.Application();
// Then a workbooks object,
Excel.Workbooks workbooks = excel.Workbooks;
// Then adds a workbook object,
Excel.Workbook workbook = workbooks.Add(true);
// Then adds a worksheet object,
Excel.Worksheet activeSheet = workbook.ActiveSheet;
// Then names the worksheet to what we need.
activeSheet.Name = "scbyext";
// Add column headings,
int iCol = 0;
// for each row of data,
int iRow = 0;
foreach (DataRow r in dt.Rows)
{
iRow++;
// Then add each row's cell data.
iCol = 0;
foreach (DataColumn c in dt.Columns)
{
iCol++;
excel.Cells[iRow, iCol] = r[c.ColumnName];
}
}
// Disable Excel prompts.
excel.DisplayAlerts = false;
// Save the workbook to the correct folder.
workbook.SaveAs("C:\\Escaped\\Path",
Excel.XlFileFormat.xlExcel8, missing, missing,
false, false, Excel.XlSaveAsAccessMode.xlNoChange,
missing, missing, missing, missing, missing);
// Release the objects we made, in reverse order, to allow Excel to quit correctly.
ReleaseObj(activeSheet);
ReleaseObj(workbook);
ReleaseObj(workbooks);
excel.Quit();
ReleaseObj(excel);
}
If you don't, the process will stay stuck open. I'm not sure what it's doing when it stays stuck, but by the end of the day the CPU Time used by it can get quite high.
I had a similar problem. I was creating an excel file using C# with a lot of charts that took a long time to create. If a user opened an existing excel file while my C# file was still being written it caused an exception to be thrown and my application would crash.
I fixed it with the following:
xlApp = new Application();
xlApp.IgnoreRemoteRequests = true;