Extra worksheets in C# Excel app - c#

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!");
}

Related

How to make cell formatting faster for large worksheets in EPPlus

I have a C# data processing application which uses EPPlus to write the final results into an excel sheet. The background color of the rows are changed based on what the data on that row signifies. Time was never an issue as I only dealt with files that were below <100MB before. However, as my requirements have changed and the files get larger, I have noticed that.. just coloring makes my application 60% slower. Removing coloring makes the application significantly faster. The snippet below is an example of the code which I use to color the data to make it visually distinguishing. I'm no expert at EPPlus but is there a way, this can be optimized to make my application faster? Or are there any better ways for me to make the rows visually distinct for the people who will end up looking at the data? Any help will be appreciated!
if (data[4] == "3")
{
// color the type 3 messages here
var fill1 = cell1.Style.Fill;
fill1.PatternType = ExcelFillStyle.Solid;
fill1.BackgroundColor.SetColor(Color.LightGray);
}
if (data[4] == "4")
{
var fill1 = cell1.Style.Fill;
fill1.PatternType = ExcelFillStyle.Solid;
fill1.BackgroundColor.SetColor(Color.BlanchedAlmond);
}
EDIT:
This is the code I use to copy the template and write the excel data into the new worksheet. p is an Excel Package which I convert to a byte Array before writing to the excel file.
Byte[] bin = p.GetAsByteArray();
File.Copy("C:\\Users\\mpas\\Desktop\\template.xlsx", "C:\\Users\\mpas\\Desktop\\result.xlsx");
using (FileStream fs = File.OpenWrite("C:\\Users\\mpas\\Desktop\\result.xlsx")) {
fs.Write(bin, 0, bin.Length);
}
Styling is much faster in EPPlus, and most Excel APIs, if you use named styles. Assign and use the style to cell in EPPlus like this ...
internal static string YourStyleName = "MyStyle";
ExcelNamedStyleXml yourStyle = excel.Workbook.Styles.CreateNamedStyle(YourStyleName);
yourStyle.Style.Font.Color.SetColor(Color.DarkRed);
yourStyle.Style.Fill.PatternType = ExcelFillStyle.Solid;
yourStyle.Style.Fill.BackgroundColor.SetColor(Color.LemonChiffon);
// ...
sheet.Cells[sourceRange].StyleName = YourStyleStyleName
Here is code to open an existing file.
FileInfo AddressList = new FileInfo("c:\test\test.xlsx");
// Open and read the XlSX file.
try
{
using (ExcelPackage package = new ExcelPackage(AddressList))
{
// Get the work book in the file
ExcelWorkbook workBook = package.Workbook;
if (workBook != null)
{
if (workBook.Worksheets.Count > 0)
{
// Get the first worksheet
//ExcelWorksheet Worksheet = workBook.Worksheets.First();
var worksheet = package.Workbook.Worksheets[1];

Updating existing excel file while it is open

I read a lot about how to communicate from C# to Excel and saw some good references.
The thing is I'm looking for an easy way to update existing excel file while it is still open, using
the most advanced way (linq for example) and not OLEDB.
This should be few lines of code describing how can I read current cell, update his value and take into consideration the file might not be exist, but if it does exist and open, it will just update the file without giving the notification the file is already exist. If the file doesn't exist it will create a new one.
SO:
1. connect to an excel file, check if it exist, if not create one
2. read from cell
3. update cell
4. do this while the excel sheet can be still open wild.
I already visited the following places:
http://social.msdn.microsoft.com/Forums/vstudio/en-US/ef11a193-54f3-407b-9374-9f5770fd9fd7/writing-to-excel-using-c
Updating an excel document programmatically
Update specific cell of excel file using oledb
I used the following code:
if (File.Exists(#"C:\\temp\\test.xls"))
{
Microsoft.Office.Interop.Excel.Application excelApp = new Microsoft.Office.Interop.Excel.Application();
Microsoft.Office.Interop.Excel.Workbooks workBooks = excelApp.Workbooks;
Microsoft.Office.Interop.Excel.Workbook workBook = workBooks.Open(#"C:\\temp\\test.xls");
Microsoft.Office.Interop.Excel.Worksheet workSheet = workBook.Worksheets.get_Item(1);
int nColumns = workSheet.UsedRange.Columns.Count;
int nRows = workSheet.UsedRange.Rows.Count;
for (int i = 2; i < nRows; i++)
{
workSheet.Columns["1","A"] = "test";
}
workBook.Save();
workBook.Close();
}
So I use VSTO Contrib to help out with COM Interop and memory management and that's why you see .WithComCleanup().
To open up a spreadsheet:
try
{
using (var xlApp = new Microsoft.Office.Interop.Excel.Application().WithComCleanup())
using (var wrkbooks = xlApp.Resource.Workbooks.WithComCleanup())
using (var wrkbook = wrkbooks.Resource.Open(filePath, false, true).WithComCleanup())
{
If the excel file is already open, then to get around the Read-Only follow this tip:
wrkbooks.Resource.Open(filePath, false, FALSE).WithComCleanup())
Here's how I iterate though the sheets (note that some Excel sheets are ChartSheets):
foreach (object possibleSheet in xlApp.Resource.Sheets)
{
Microsoft.Office.Interop.Excel.Worksheet aSheet = possibleSheet as Microsoft.Office.Interop.Excel.Worksheet;
if (aSheet == null)
continue;
Here is a quick way to get a reference to the sheet you're interested in:
activeSheet = wrkbook.Resource.Sheets[sheetToImport];
You read and write to cells just as you've identified:
for (int i = 2; i < nRows; i++)
{
activeSheet.Columns["1","A"] = "test";
}
Here is how I close Excel:
MathematicaAPI.XlHelper.CloseExcel((Worksheet)activeSheet, (Workbook)wrkbook.Resource , (Workbooks)wrkbooks.Resource);
public static void CloseExcel(Worksheet activeSheet, Workbook wrkbook, Workbooks wrkbooks)
{
//http://support.microsoft.com/kb/317109 -> excel just wont close for some reason
if (activeSheet != null)
{
Marshal.FinalReleaseComObject(activeSheet);
activeSheet = null;
}
if (wrkbook != null)
{
wrkbook.Saved = true;
wrkbook.Close(Microsoft.Office.Interop.Excel.XlSaveAction.xlDoNotSaveChanges);
}
if (wrkbooks != null)
{
wrkbooks.Close();
}
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
}
Sometimes Excel just wont close and you have to kill it (after trying to close it properly of course) - I dont recommend this, but if you cant track down the un-disposed memory and all else fails then...
if (xlApp != null)
{
ExcelDataSourceHelper.GetWindowThreadProcessId(new IntPtr(xlApp.Resource.Hwnd), ref excelProcessId);
}
if (excelProcessId > 0)
{
XlHelper.KillProcess(excelProcessId);
}
public static void KillProcess(int excelProcessId)
{
if (excelProcessId > 0)
{
System.Diagnostics.Process ExcelProc = null;
try
{
ExcelProc = System.Diagnostics.Process.GetProcessById(excelProcessId);
if (ExcelProc != null)
{
ExcelProc.Kill();
}
}
catch
{ }
}
}
Note: I reduce the chances of needing to kill Excel by using VSTO Contrib with Using's.
OK thank you all for trying to solve the issue
The solution was using Excel 2011/2013 Add-In which can communicate excel as a plugin
create an application-level add-in for Microsoft Office Excel. The features that you create in this kind of solution are available to the application itself, regardless of which workbooks are open.
You can visit MSDN

When Excel file is opened externally during C# application, an exception occurs

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;

C# Excel 2010 programing

I am writing a program in C# for analysing a structure details in an earth quake.I have a lot of data and want to put them in a excel file.
I mean i need a function like this :
public void excel(int sheet_no,int row,int column,string value)
Is there any way to parse the text file and Put this data in Excel sheet ?
use this :
Microsoft.Office.Interop.Excel.Application app = new Microsoft.Office.Interop.Excel.Application();
Workbooks workbooks;
_Workbook workbook;
_Workbook workbook2;
Sheets sheets;
_Worksheet worksheet;
app.Visible = false;
workbooks = app.Workbooks;
workbook = workbooks.Add(XlWBATemplate.xlWBATWorksheet);
sheets = workbook.Worksheets;
worksheet = (_Worksheet)sheets.get_Item(1);
worksheet.Cells[row, column] = value;
workbook.Saved = true;
workbook.SaveAs(output_file);
app.UserControl = false;
app.Quit();
Take a look at ClosedXml
Code sample:
var workbook = new XLWorkbook();
var worksheet = workbook.Worksheets.Add("Sample Sheet");
worksheet.Cell("A1").Value = "Hello World!";
workbook.SaveAs("HelloWorld.xlsx");
But if you are dealing with a lot of data, consider using a database like sqlserver.
It provides a lot of analysis tools.
You can connect it as a datasource to excel too.
For now, I am providing links for you to read # Excel Tasks and code samples # Excel controls in ASP.net. If you're still confused, I may be able to find code samples from my projects or the Internet, which are not hard to find.
Look at link Workbook Open (create) method to create Workbooks, and link Workbook Open method to open Workbooks.

Calculating User Defined Functions in Excel using C# Excel Interop

I am trying to an Add-in directly from C# so that when I open a workbook, and do a Workbook.Calculate() the UDF's (User Defined Functions) that are defined in an external addin correctly calculate in the worksheet. Currently, I am looping through each adding and simple setting:
AddIn.Installed = true
This does not work. C# does not load add-in at all, and I want to avoid using VBA. I want to open a workbook an excel workbook with the specific add in loaded, do a full calculated, and should have all values of the worksheet updated, including cells with UDF's.
Thanks for any help....
Some code:
Excel.Workbook wkbk = ExcelApp.ActiveWorkbook;
Excel.XlFixedFormatType paramExportFormat = Excel.XlFixedFormatType.xlTypePDF;
Excel.XlFixedFormatQuality paramExportQuality = Excel.XlFixedFormatQuality.xlQualityStandard;
bool paramOpenAfterPublish = false;
bool paramIncludeDocProps = true;
bool paramIgnorePrintAreas = true;
object paramFromPage = Type.Missing;
object paramToPage = Type.Missing;
ExcelApp.Visible = true;
//foreach (Excel.AddIn aiTemp in ExcelApp.AddIns)
//{
// if (aiTemp.Name.Contains(""))
// {
// aiTemp.Installed = false;
// aiTemp.Installed = true;
// }
//}
while (ExcelApp.CalculationState == Microsoft.Office.Interop.Excel.XlCalculationState.xlCalculating)
{
Thread.Sleep(50);
}
ExcelApp.CalculateFull();
var wksht = wkbk.ActiveSheet;
Excel.Range rng = ((Excel.Worksheet)wksht).get_Range("B1", "B1");
rng.Calculate();
//EnsureCalcFinished();
ExcelApp.Visible = false;
wkbk.ExportAsFixedFormat(Microsoft.Office.Interop.Excel.XlFixedFormatType.xlTypePDF, PathToDocument.Replace(".xlsx", ".pdf"), paramExportQuality, true, false, Type.Missing, Type.Missing, true,Type.Missing);
UPDATE:
I found a link with the method I use to register UDFs.
Ref: http://msdn.microsoft.com/en-us/library/ms173189(v=vs.80).aspx
In Excel, you need to go to Options -> Add-Ins => Excel Add-in (Go..) => automation = select the library and hit OK. Once you do this once, it'll be auto-loaded each time you open excel.
Here is the algorithm to load excel AddIns so that when you open excel workbook, a calculation will include UDF's defined in Add-in:
1)Initiliaze Excel Application
ExcelApp = new Excel.Application();
2)Load AddIns *
foreach (Excel.AddIn ai in ExcelApp.AddIns)
{
ai.Installed = false;
ai.Installed = true;
ExcelApp.Wait(50);
}
**The Key is to load add-ins before you open Excel Workbook.
3)Open Excel Workbook, which will trigger calculations
4)Set Calculation Mode to manual so that any changes in Interop do not trigger lengthy recalc
ExcelApp.Calculation = Microsoft.Office.Interop.Excel.XlCalculation.xlCalculationManual;
5)Perform any manipulations, and perform calc
ExcelApp.CalculateFull();
6)Dispose of Excel Objects appropriately
Hope this helps someone with a similar issue.. Ended up being a simple fix for a simple problem. Just remember to load add-ins before opening the workbook. Otherwise, opening an excel workbook with UDF's dependent on AddIn will fail.

Categories

Resources