Generic Parser Design - c#

I have this function implemented for parsing employee details, similarly i will have to parse for sales, customer etc for that i need to create 2 more functions. The code will be repeated in all the functions only difference being
the return type of the function
instantiating appropriate object
cells to read
is there any way to move the repeating code to a class and configure it so that i an reuse it?
public List<Employee> ParseEmployee(string filePath)
{
Application _excelApp = null;
Workbooks workBooks = null;
Workbook workBook = null;
Sheets wSheets = null;
Worksheet wSheet = null;
Range xlRange = null;
Range xlRowRange = null;
Range xlcolRange = null;
List<Employee> empLst= new List<Employee>();
try
{
_excelApp = new Application();
workBooks = _excelApp.Workbooks;
workBook = workBooks.Open(filePath, 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);
wSheets = (Sheets)workBook.Sheets;
wSheet = (Worksheet)wSheets.get_Item(1);
xlRange = wSheet.UsedRange;
xlRowRange = xlRange.Rows;
xlcolRange = xlRange.Columns;
int rowCount = xlRowRange.Count;
int colCount = xlcolRange.Count;
for (int i = 2; i <= rowCount; i++)
{
Range cell1 = xlRange.Cells[i, 1] as Range;
Range cell2 = xlRange.Cells[i, 2] as Range;
Range cell3 = xlRange.Cells[i, 3] as Range;
object val1 = cell1.Value2;
object val2 = cell2.Value2;
object val3 = cell3.Value2;
Employee emp = new Employee();
emp.FirstName = val1.ToString();
emp.LastName = val2.ToString();
emp.EmpID = val3.ToString();
empLst.Add(emp);
Marshal.ReleaseComObject(cell1);
Marshal.ReleaseComObject(cell2);
Marshal.ReleaseComObject(cell3);
}
}
catch (Exception exp)
{
}
finally
{
GC.Collect();
GC.WaitForPendingFinalizers();
workBook.Close(false, Type.Missing, Type.Missing);
_excelApp.Quit();
Marshal.ReleaseComObject(xlRowRange);
Marshal.ReleaseComObject(xlRange);
Marshal.ReleaseComObject(xlcolRange);
Marshal.ReleaseComObject(wSheet);
Marshal.ReleaseComObject(wSheets);
Marshal.ReleaseComObject(workBook);
Marshal.ReleaseComObject(workBooks);
Marshal.ReleaseComObject(_excelApp);
}
return empLst;
}

I think the visitor pattern might be a good fit here. You modify the function you have above to include a parameter called visitor. Then you modify your for loop to pass relevant data to the visitor object:
for (int i = 2; i <= rowCount; i++)
{
visitor.VisitRow(xlRange.Cells, i);
}
The visitor.VisitRow() function will extract the data it needs and keeps internally a reference to the extracted objects. You will have different visitors, one for employers, one for sales, customers, etc.
In the end, you will write something like this:
Visitor employerVisitor = new EmployerVisitor();
Visitor salesVisitor = new SalesVisitor();
Parse("workbook-employers.xls", employerVisitor);
Parse("workbook-sales.xls", salesVisitor);
List<Employee> employers = employerVisitor.GetData();
List<Sale> sales = salesVisitor.GetData();

You could expose this from a generic class, along the lines of:
public class ObjectParser<T>
{
public List<T> ParseObject(string filePath, Func<Range, T> f)
{
Application _excelApp = null;
Workbooks workBooks = null;
Workbook workBook = null;
Sheets wSheets = null;
Worksheet wSheet = null;
Range xlRange = null;
Range xlRowRange = null;
Range xlcolRange = null;
List<T> lst= new List<T>();
try
{
_excelApp = new Application();
workBooks = _excelApp.Workbooks;
workBook = workBooks.Open(filePath, 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);
wSheets = (Sheets)workBook.Sheets;
wSheet = (Worksheet)wSheets.get_Item(1);
xlRange = wSheet.UsedRange;
xlRowRange = xlRange.Rows;
xlcolRange = xlRange.Columns;
int rowCount = xlRowRange.Count;
int colCount = xlcolRange.Count;
for (int i = 2; i <= rowCount; i++)
{
lst.Add(f(xlRange));
}
}
catch (Exception exp)
{
}
finally
{
GC.Collect();
GC.WaitForPendingFinalizers();
workBook.Close(false, Type.Missing, Type.Missing);
_excelApp.Quit();
Marshal.ReleaseComObject(xlRowRange);
Marshal.ReleaseComObject(xlRange);
Marshal.ReleaseComObject(xlcolRange);
Marshal.ReleaseComObject(wSheet);
Marshal.ReleaseComObject(wSheets);
Marshal.ReleaseComObject(workBook);
Marshal.ReleaseComObject(workBooks);
Marshal.ReleaseComObject(_excelApp);
}
return lst;
}
}
To use this:
ObjectParser<Employee> op = new ObjectParser<Employee>()
op.Parse(filepath, r => /* insert code to handle Employee here */)
My concern here is that some of the Marshall.ReleaseComObject() calls are pushed onto the lambda that is passed in, which makes that a little heavy-weight. Can you tell us more about the differences in what cells are used between Employee and the other types?

I have re-factored my code to something like this
class ExcelParser : IDisposable
{
bool disposed = false;
Application _excelApp = null;
Workbooks workBooks = null;
Workbook workBook = null;
Sheets wSheets = null;
Worksheet wSheet = null;
Range xlRange = null;
Range xlRowRange = null;
Range xlcolRange = null;
public bool Load(string filePath)
{
bool bFlag = true;
try
{
_excelApp = new Application();
workBooks = _excelApp.Workbooks;
workBook = workBooks.Open(filePath, 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);
wSheets = (Sheets)workBook.Sheets;
wSheet = (Worksheet)wSheets.get_Item(1);
xlRange = wSheet.UsedRange;
xlRowRange = xlRange.Rows;
xlcolRange = xlRange.Columns;
}
catch (Exception exp)
{
throw;
}
return bFlag;
}
public int GetRowCount()
{
int rowCount = 0;
if(xlRowRange != null)
rowCount = xlRowRange.Count;
return rowCount;
}
public string GetValue(int rowIndex, int colIndex)
{
string value = "";
Range cell = null;
try
{
cell = xlRange.Cells[rowIndex, colIndex] as Range;
object val = cell.Value2;
value = val.ToString();
}
catch (Exception exp)
{
}
finally
{
Marshal.ReleaseComObject(cell);
}
return value;
}
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{ // don't dispose more than once
if (disposing)
{
// disposing==true means you're not in the finalizer, so
// you can reference other objects here
GC.Collect();
GC.WaitForPendingFinalizers();
if (workBook != null)
workBook.Close(false, Type.Missing, Type.Missing);
if (_excelApp != null)
_excelApp.Quit();
if (xlRowRange != null)
Marshal.ReleaseComObject(xlRowRange);
if (xlRange != null)
Marshal.ReleaseComObject(xlRange);
if (xlcolRange != null)
Marshal.ReleaseComObject(xlcolRange);
if (wSheet != null)
Marshal.ReleaseComObject(wSheet);
if (wSheets != null)
Marshal.ReleaseComObject(wSheets);
if (workBook != null)
Marshal.ReleaseComObject(workBook);
if (workBooks != null)
Marshal.ReleaseComObject(workBooks);
if (_excelApp != null)
Marshal.ReleaseComObject(_excelApp);
}
}
this.disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~ExcelParser()
{
Dispose(false);
}
}
and the calling code looks like this
public List<Employee> Handle(string filePath)
{
List<Employee> empLst = new List<Employee>();
ExcelParser exlParser = new ExcelParser();
try
{
if (exlParser.Load(filePath))
{
int rowCount = exlParser.GetRowCount();
for (int i = 2; i <= rowCount; i++)
{
Employee emp = new Employee();
emp.FirstName = exlParser.GetValue(i, 1);
emp.LastName = exlParser.GetValue(i, 2);
emp.EmpID = exlParser.GetValue(i, 3);
empLst.Add(emp);
}
}
}
catch (Exception exp)
{
}
finally
{
exlParser.Dispose();
}
return empLst;
}
so now i can reuse the parser in whatever places i wish to use. please comment whether this is correct

Related

Loop through Excel files and copy correct range in a separate file with C#

Intro:
Today I have decided to make an Excel automatization task with C#. This is probably the first time I am doing something like this, thus the problems are plenty.
The task:
Pretty much, the idea is the following - I have 4 excel files in folder strPath. I have to loop through all of them and make a file called Report.xlsx in the same folder, with the information from those files.
The information, that I need is anything, below row 9. Thus, the first row to copy is row number 10. That is why, the first file I loop for is saved as Report, and the bMakeOnce value is changed. After the first file is looped and saved As, I start entering into the else condition. There I locate the last used row of the XL files and I try to copy the range into the sheetReport.
The questions:
First of all - any ideas for code improvement;
Whenever I am looping through the files I get the following picture telling me that each of the looping file is opened already.
Any good idea how to do the range copy better? Currently, I simply try to put the copied range on every 200+n line, to avoid some confusion for me.
Any idea why I do not get anything in the sheetReport, except for the first file?
The code I am using (initially, for the current goto Github below):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Reflection;
using Excel = Microsoft.Office.Interop.Excel;
using Word = Microsoft.Office.Interop.Word;
class MainClass
{
static void Main()
{
string strPath = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), #"..\..\..\"));
string[] strFiles = Directory.GetFiles(strPath);
Excel.Application excel = null;
bool bMakeOnce = true;
int intFirstLine = 10;
int intLastColumn = 50;
int lastRow;
int lastRowReport;
Excel.Workbook wkbReport = null;
string strWkbReportPath;
int n = 0;
foreach (string strFile in strFiles)
{
Console.WriteLine(strFile);
Excel.Workbook wkb = null;
Excel.Worksheet sheet = null;
Excel.Worksheet sheetReport = null;
Excel.Range rngLast = null;
Excel.Range rngLastReport = null;
Excel.Range rngToCopy = null;
Excel.Range rngDestination = null;
excel = new Excel.Application();
excel.Visible = true;
wkb = OpenBook(excel, strFile);
if (bMakeOnce)
{
bMakeOnce = false;
strWkbReportPath = wkb.Path + "\\" + "Report.xlsx";
wkb.SaveAs(strWkbReportPath);
wkbReport = OpenBook(excel, strWkbReportPath);
}
else
{
wkb = OpenBook(excel, strFile);
sheetReport = wkbReport.Worksheets[1];
sheet = wkb.Worksheets[1];
n++;
rngLastReport = sheetReport.Cells.SpecialCells(Excel.XlCellType.xlCellTypeLastCell, Type.Missing);
rngLast = sheet.Cells.SpecialCells(Excel.XlCellType.xlCellTypeLastCell, Type.Missing);
rngToCopy = sheet.Range[sheet.Cells[intFirstLine, 1], sheet.Cells[rngLast.Row, intLastColumn]];
int size = rngToCopy.Rows.Count;
Console.WriteLine(size);
rngDestination = sheetReport.Range[sheetReport.Cells[200 * n, 1], sheetReport.Cells[200 * n + size, intLastColumn]];
rngToCopy.Copy(rngDestination);
//rngDestination.PasteSpecial(Excel.XlPasteType.xlPasteAll);
}
}
wkbReport.Close(false);
excel.Quit();
}
public static Excel.Workbook OpenBook(Excel.Application excelInstance, string fileName, bool readOnly = false, bool editable = true, bool updateLinks = true)
{
Excel.Workbook book = excelInstance.Workbooks.Open(
fileName, updateLinks, readOnly,
Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, editable, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing);
return book;
}
}
Now it works somehow, producing what I want:
using System;
using System.IO;
using Excel = Microsoft.Office.Interop.Excel;
class MainClass
{
static void Main()
{
string strPath = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), #"..\..\..\"));
string[] strFiles = Directory.GetFiles(strPath);
Excel.Application excel = null;
bool bMakeOnce = true;
string strReportName = "Report.xlsx";
int intFirstLine = 10;
int intLastColumn = 50;
int lastRow;
int lastRowReport;
int intTotalRows;
Excel.Workbook wkbReport = null;
string strWkbReportPath;
int n = 0;
excel = new Excel.Application();
excel.Visible = true;
foreach (string strFile in strFiles)
{
if (strFile.Contains(strReportName))
{
Console.WriteLine(strReportName + " is deleted.");
File.Delete(strFile);
}
}
foreach (string strFile in strFiles)
{
if (strFile.Contains(strReportName))
{
continue;
}
Console.WriteLine(strFile);
Excel.Workbook wkb = null;
Excel.Worksheet sheet = null;
Excel.Worksheet sheetReport = null;
Excel.Range rngLastReport = null;
Excel.Range rngToCopy = null;
wkb = Open(excel, strFile);
if (bMakeOnce)
{
bMakeOnce = false;
strWkbReportPath = wkb.Path + "\\" + strReportName;
wkb.SaveAs(strWkbReportPath);
wkb.Close();
wkbReport = Open(excel, strWkbReportPath);
}
else
{
sheetReport = wkbReport.Worksheets[1];
sheet = wkb.Worksheets[1];
//lastRow = sheet.Cells[1, 3].get_End(Excel.XlDirection.xlUp).Row;
intTotalRows = sheet.Rows.Count;
lastRow = sheet.Cells[intTotalRows, 1].End(Excel.XlDirection.xlUp).Row;
lastRowReport = sheetReport.Cells[intTotalRows, 1].End(Excel.XlDirection.xlUp).Row;
//lastRowReport = sheetReport.Cells[intTotalRows, 1].get_End(Excel.XlDirection.xlUp).Row;
//lastRowReport = sheetReport.Cells[intTotalRows, intTotalRows.End[Excel.XlDirection.xlUp]].Row;
n++;
rngToCopy = sheet.Range[sheet.Cells[intFirstLine,1],sheet.Cells[lastRow, intLastColumn]];
int size = rngToCopy.Rows.Count;
rngLastReport = sheetReport.Range[sheetReport.Cells[lastRowReport+1, 1], sheetReport.Cells[lastRowReport + 1+size, intLastColumn]];
rngToCopy.Copy(rngLastReport);
wkb.Close(false);
}
}
wkbReport.Close(true);
excel.Quit();
Console.WriteLine("Finished!");
}
public static Excel.Workbook Open(Excel.Application excelInstance, string fileName, bool readOnly = false, bool editable = true, bool updateLinks = true)
{
Excel.Workbook book = excelInstance.Workbooks.Open(
fileName, updateLinks, readOnly,
Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, editable, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing);
return book;
}
//public static Excel.Workbook OpenBook(Excel.Application excelInstance, string fileName, bool readOnly = false, bool editable = true, bool updateLinks = true)
//{
// Excel.Workbook book = excelInstance.Workbooks.Open(
// fileName, updateLinks, readOnly,
// Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing,
// Type.Missing, editable, Type.Missing, Type.Missing, Type.Missing,
// Type.Missing, Type.Missing);
// return book;
//}
}
Thus, I have put it codeReview here: https://codereview.stackexchange.com/questions/153054/loop-through-excel-files-and-copy-correct-range-in-a-separate-file

Linking of Specific excel columns via Open Xml

using Microsoft.Office.Interop.Excel.dll i am able to get specific row and specific columns data from excel sheet in to a list by using below code
Excel.Workbook MyWorkBook = Excel_App.Workbooks.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);
Excel.Worksheet MyWorksheet = null;
MyWorksheet = (Microsoft.Office.Interop.Excel.Worksheet)MyWorkBook.Sheets[(1)];
Excel.Range Excel_Range;
Excel_Range = MyWorksheet.UsedRange;
SheetCount = MyWorkBook.Sheets.Count;
Lastrow = MyWorksheet.Cells.Find("*", System.Reflection.Missing.Value, System.Reflection.Missing.Value, System.Reflection.Missing.Value, Excel.XlSearchOrder.xlByRows, Excel.XlSearchDirection.xlPrevious, false, System.Reflection.Missing.Value, System.Reflection.Missing.Value).Row;
LastColumn = MyWorksheet.Cells.Find("*", System.Reflection.Missing.Value, System.Reflection.Missing.Value, System.Reflection.Missing.Value, Excel.XlSearchOrder.xlByColumns, Excel.XlSearchDirection.xlPrevious, false, System.Reflection.Missing.Value, System.Reflection.Missing.Value).Column;
for (int i = 8; i <= Lastrow; i++)
{
List_MAPPING_FILE_A429_PATHS.Add((string)(Excel_Range.Cells[i, 4] as Excel.Range).Value2.ToString());
List_MAPPING_FILE_ASCB_PATHS.Add((string)(Excel_Range.Cells[i, 5] as Excel.Range).Value2.ToString());
}
now i want to get same data stored in list by using OpenXml.dll i tried below code but got stuck how to proceed further
public void AddtoLogFile( )
{
string temp =#"C:\Ported\DATA\EJETE2_A429RX_TIF_temp.xml";
using (SpreadsheetDocument myDoc = SpreadsheetDocument.Open(temp, true))
{
WorkbookPart workbookPart = myDoc.WorkbookPart;
WorksheetPart worksheetPart = workbookPart.WorksheetParts.First();
SheetData sheetData =
worksheetPart.Worksheet.Elements<SheetData>().First();
foreach (Row r in sheetData.Elements<Row>())
{
foreach (Cell c in r.Elements<Cell>())
{
string text = c.CellValue.Text;
}
}
}
}
can someone help me in this.
The Cells location is stored in Cell.CellReference,
The cells reference for the first cell would be "A1"
Use this method to extract the Column:
private static readonly Regex ColumnNameRegex = new Regex("[A-Za-z]+");
private static string GetColumnName(string cellReference)
{
if (ColumnNameRegex.IsMatch(cellReference))
return ColumnNameRegex.Match(cellReference).Value;
throw new ArgumentOutOfRangeException(cellReference);
}
Not sure what your trying to get from the Spreadsheet, I guess you only want the information from cells in the certain row:
foreach (Row r in sheetData.Elements<Row>())
{
foreach (Cell c in r.Elements<Cell>())
{
if (GetColumnName(c.CellReference) == "A")
{
string text = c.CellValue.InnerText;
}
}
}
The data given from CellVaule.InnerText will be a reference to the SharedStringTabl which holds all the strings for a worksheet. You will need to get the data from the SharedStringTable which is stored as
WorkBookPart.SharedStringTablePart.SharedStringTable;
For this I use a method that takes the Cell and SharedStringTable to return the value:
public static string GetCellV (Cell cell, SharedStringTable ss)
{
string cellV = null;
try
{
cellV = cell.CellValue.InnerText;
if (cell.DataType != null
&& cell.DataType.Value == CellValues.SharedString)
{
cellV = ss.ElementAt(Int32.Parse(cellV)).InnerText;
}
else
{
cellV = cell.CellValue.InnerText;
}
}
catch (Exception)
{
cellV = " ";
}
return cellV;
}

How to read the entire worksheet from excel

I am reading data from numerous excel worksheets, but the performance is slow since I am fetching each col directly. Is there a way I can read the entire UsedRange into memory with one call? Then process the rows/cols locally?
The code I have is basically this:
xlWorkSheet = (Worksheet)_xlWorkBook.Worksheets.get_Item(1);
var range = xlWorkSheet.UsedRange;
for (var rCnt = 2; rCnt <= range.Rows.Count; rCnt++)
{
// Process column entries
}
I had the same problem while handling very large excel
I managed to read it as range and then transformed it to List> using AsParallel() on each row
It made it to run much faster
Here is the code:
private List<List<string>> ReadExcelFile(string fileName)
{
Excel.Application xlApp = null;
Workbook xlWorkbook = null;
Sheets xlSheets = null;
Worksheet xlSheet = null;
var results = new List<List<string>>();
try
{
xlApp = new Microsoft.Office.Interop.Excel.Application();
xlWorkbook = xlApp.Workbooks.Open(fileName, Type.Missing, true, Type.Missing, Type.Missing, Type.Missing, true, XlPlatform.xlWindows, Type.Missing,false, false, Type.Missing, false, Type.Missing, Type.Missing);
xlSheets = xlWorkbook.Sheets as Sheets;
xlSheet = xlSheets[1];
// Let's say your range is from A1 to DG5200
var cells = xlSheet.get_Range("A1", "DG5200");
results = ExcelRangeToListsParallel(cells);
}
catch (Exception)
{
results = null;
}
finally
{
xlWorkbook.Close(false);
xlApp.Quit();
if (xlSheet != null)
Marshal.ReleaseComObject(xlSheet);
if (xlSheets != null)
Marshal.ReleaseComObject(xlSheets);
if (xlWorkbook != null)
Marshal.ReleaseComObject(xlWorkbook);
if (xlApp != null)
Marshal.ReleaseComObject(xlApp);
xlApp = null;
}
return results;
}
private List<List<String>> ExcelRangeToListsParallel(Excel.Range cells)
{
return cells.Rows.Cast<Excel.Range>().AsParallel().Select(row =>
{
return row.Cells.Cast<Excel.Range>().Select(cell =>
{
var cellContent = cell.Value2;
return (cellContent == null) ? String.Empty : cellContent.ToString();
}).Cast<string>().ToList();
}).ToList();
}

Modify Interop Excel for Opening Existing File

I'm using Excel Interop for C# to export a datagridview to Excel and print it. I'm using this code:
try
{
Microsoft.Office.Interop.Excel.Application excel = new Microsoft.Office.Interop.Excel.Application();
excel.Application.Workbooks.Add(true);
int ColumnIndex = 0;
int rowIndex = -1;
foreach (DataGridViewRow row in dataGridView1.Rows)
{
rowIndex++;
ColumnIndex = 0;
foreach (DataGridViewColumn col in dataGridView1.Columns)
{
ColumnIndex++;
excel.Cells[rowIndex + 1, ColumnIndex] = row.Cells[col.Name].Value;
}
}
excel.Visible = true;
excel.DisplayAlerts = false;
Worksheet worksheet = (Worksheet)excel.ActiveSheet;
worksheet.Activate();
worksheet.Cells.Style.HorizontalAlignment = Microsoft.Office.Interop.Excel.XlHAlign.xlHAlignCenter;
worksheet.Range[worksheet.Cells[1, 1], worksheet.Cells[1, 7]].Merge();
worksheet.Range[worksheet.Cells[2, 1], worksheet.Cells[2, 7]].Merge();
worksheet.Range[worksheet.Cells[3, 1], worksheet.Cells[3, 7]].Merge();
worksheet.Range[worksheet.Cells[4, 1], worksheet.Cells[4, 4]].Merge();
worksheet.Cells[1, 1].Font.Bold = true;
worksheet.Range["A1"].Cells.Font.Size = 15;
worksheet.Range["A4"].Cells.Font.Size = 15;
worksheet.Range["B7"].Cells.Font.Size = 15;
worksheet.Range["B8"].Cells.Font.Size = 15;
worksheet.Range["B9"].Cells.Font.Size = 15;
worksheet.Range["A11"].Cells.Font.Size = 15;
worksheet.Range["B13"].Cells.Font.Size = 15;
worksheet.PrintOut(Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);
GC.Collect();
GC.WaitForPendingFinalizers();
excel.Quit();
}
catch (Exception ex)
{
MessageBox.Show("Error de exportación.");
}
It works fine, but I need to open an existing file to do this same thing to avoid reconfiguration of the printing margins. I checked other similar questions and tried to apply the "workBook = oXL.Workbooks.Open("path", ...);" but it can make it work. Any thoughts?
To open a file use the open method.
Microsoft.Office.Interop.Excel.Workbooks wkbks = null;
Microsoft.Office.Interop.Excel.Workbook wkbk = null;
wkbks = excel.Workbooks;
wkbk = wkbks.Open(xlsFileName);
At the end of your method you should do a cleanup on all your interop variables:
if (wkbk != null)
{
try
{
wkbk.Close(false);
}
catch
{
}
Marshal.FinalReleaseComObject(wkbk);
wkbk = null;
}

CSV export with interop

I'm using this code to generate an Excel file. It should be simple to change the export to CSV file, just by modifying a parameter in the saveAs call, but it doesn't work. Any ideea why ?
Microsoft.Office.Interop.Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();
xlApp.Visible = false;
if (xlApp == null)
{
MessageBox.Show("EXCEL could not be started. Check that your office installation and project references are correct.");
return false;
}
Workbook wb = xlApp.Workbooks.Add(XlWBATemplate.xlWBATWorksheet);
try
{
if (details != false)
{
//Workbook wb = xlApp.Workbooks.Add(XlWBATemplate.xlWBATWorksheet);
wb.Worksheets.Add();
Worksheet detailsWs = (Worksheet)wb.Worksheets[2];
for (int i = 0; i < dt.Columns.Count; i++)
{
detailsWs.Cells[1, i + 1] = dt.Columns[i].Caption;
}
}
Worksheet ws = (Worksheet)wb.Worksheets[1];
if (ws == null)
{
MessageBox.Show("Worksheet could not be created. Check that your office installation and project references are correct.");
}
for (int i = 0; i < dt.Columns.Count; i++)
{
ws.Cells[1, i + 1] = dt.Columns[i].Caption;
}
for (int i = 0; i < dt.Rows.Count; i++)
{
for (int j = 0; j < dt.Columns.Count; j++)
{
ws.Cells[i + 2, j + 1] = dt.Rows[i].ItemArray[j];
}
worker.ReportProgress((i * 100) / dt.Rows.Count);
}
wb.SaveAs(filename, XlFileFormat.xlCSV, Type.Missing, Type.Missing, Type.Missing, Type.Missing, XlSaveAsAccessMode.xlShared, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);
wb.Close(true, filename);
return true;
}
catch (Exception ex)
{
throw ex;
}
finally
{
// Cleanup
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
if (wb != null)
{
wb.Close(Type.Missing, Type.Missing, Type.Missing);
Marshal.FinalReleaseComObject(wb);
}
if (xlApp != null)
{
xlApp.Quit();
Marshal.FinalReleaseComObject(xlApp);
}
}
i used the old way. lines of strings, with commas
XlSaveAsAccessMode.xlShared is the reason. i don't know why but i get that error too.
I try changing XlSaveAsAccessMode.xlNoChange and XlSaveAsAccessMode.xlExclusive,
it worked!

Categories

Resources