Hello so I was given a Project were I needed to read a xlsm file and then Project the result into a DataGripView, this is what I have so far.
var filename = "Filename|.xlsm"
XLWorkbook workbook = null;
try
{
workbook = new XLWorkbook(filename);
}
catch (Exception)
{
labelResult.Text = "error message";
return;
}
IXLWorksheet ws = null;
bool success = workbook.Worksheets.TryGetWorksheet("Cell", out ws);
if(!success)
{
labelResult.Text = "error message.";
return;
}
List<MyList> listnum = new List<MyList>();
foreach (var row in ws.RowsUsed())
{
MyList ML = new MyList { };
ML.statecod = row.Cell(1).GetString();
ML.state = row.Cell(2).GetString();
dataGridView1.DataSource = listnum;
}
So how do I populate my datagridview with the data from a xlsm file without having excel installed, therefore no excel libraries and no use of oledb?
So I found the answer. Just needed to add: listnum.add(ML);
I forgot to add my list, silly me...
I have a function to read an Excel, then workbook and sheet.
But the Excel thread never finish. I tried every solution i found around here but didn't work.
Excel thread stack on task manager, and at moment, my application crash because of Excel stop working.
public static object[,] ReadFile(string filepath, string sheetname)
{
Application xlApp = null;
Workbooks wks = null;
Workbook wb = null;
object[,] values = null;
try
{
xlApp = new ApplicationClass();
wks = xlApp.Workbooks;
wb = wks.Open(filepath, missing, missing, missing, missing, missing, missing, missing, missing, missing, missing, missing, missing, missing, missing);
Worksheet sh = (Worksheet)wb.Worksheets.get_Item(sheetname);
values = sh.UsedRange.Value2 as object[,];
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(sh);
sh = null;
}
catch (Exception ex)
{
throw new Exception(string.Format("Sheet \"{0}\" does not exist in the Excel file", sheetname));
}
finally
{
if (wb != null)
{
wb.Close(false);
Marshal.FinalReleaseComObject(wb);
wb = null;
}
if (wks != null)
{
wks.Close();
Marshal.FinalReleaseComObject(wks);
wks = null;
}
if (xlApp != null)
{
// Close Excel.
xlApp.Quit();
Marshal.FinalReleaseComObject(xlApp);
xlApp = null;
}
}
return values;
}
Maybe i don't do things in the right order, or maybe i understood wrong the COM object problem.
I had same issue I used below code to specifically kill the process
[DllImport("user32.dll")]
static extern int GetWindowThreadProcessId(int hWnd, out int lpdwProcessId);
Process GetExcelProcess(Excel.Application excelApp)
{
int id;
GetWindowThreadProcessId(excelApp.Hwnd, out id);
return Process.GetProcessById(id);
}
This method GetExcelProcesswill give you the exact process which you can kill manually. It will remove Excel.exe from task manager.
In my understanding it should not be necessary to explicitly release the com objects if you use a .NET wrapper object like excel interop. If excel still exists then I would look for errors in handling as something might be left over.
In the sample below the excel process stops already when it comes to the first console.ReadLine. In case it does not immediately stop try to hit enter to start a GC.
The following works for me and excel is stopped.
I used:
.NET Framework 4.5.2
Microsoft.Office.Interop.Excel 1.7.0.0
Excel 2010
Windows 7
using System;
using Microsoft.Office.Interop.Excel;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Go");
var t = ReadFile(#"<filename>", "<sheetname>");
Console.WriteLine("1");
Console.ReadLine();
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
Console.ReadLine();
}
public static object[,] ReadFile(string filepath, string sheetname)
{
Application xlApp = null;
Workbooks wks = null;
Workbook wb = null;
object[,] values = null;
try
{
xlApp = new Application();
wks = xlApp.Workbooks;
wb = wks.Open(filepath);
Worksheet sh = (Worksheet)wb.Worksheets.get_Item(sheetname);
values = sh.UsedRange.Value2 as object[,];
//System.Runtime.InteropServices.Marshal.FinalReleaseComObject(sh);
//sh = null;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(string.Format("Sheet \"{0}\" does not exist in the Excel file", sheetname));
}
finally
{
if (wb != null)
{
wb.Close(false);
//Marshal.FinalReleaseComObject(wb);
wb = null;
}
if (wks != null)
{
wks.Close();
//Marshal.FinalReleaseComObject(wks);
wks = null;
}
if (xlApp != null)
{
// Close Excel.
xlApp.Quit();
//Marshal.FinalReleaseComObject(xlApp);
xlApp = null;
}
}
return values;
}
}
}
As a comment to the linked stackoverflow question on not using two dot commands. This sample worked on my machine also with two dot commands (opening a workbook). It did not make a difference. Excel was released and the process was closed. The excel sheet used for testing was a standard sheet with 3 fields filled in with numbers.
Here is some code that I am using so far:
Microsoft.Office.Interop.Excel.Application excelApp = new Microsoft.Office.Interop.Excel.Application();
excelApp.Workbooks.Open(path);
// Make the object visible.
excelApp.Visible = true;
I know the workbook opens.
I am using Visual Studio 2013.
The Workbooks.Open() function returns a workbook object which you need to store and it contains more objects with the workbook data. You should be able to get all this information from intellisense or you can find information on the Microsoft.Office.Interop.Excel namespace on MSDN.
Here is an example script which will open a message box with the names of each of the named ranges in the workbook at path.
using Microsoft.Office.Interop.Excel;
Application excelApp = new Application();
Workbook myWorkbook = excelApp.Workbooks.Open(path);
Names wbNames = myWorkbook.Names;
foreach (Name n in wbNames)
{
System.Windows.Forms.MessageBox.Show(n.Name);
}
You can also get the location of the nametag (called value):
Microsoft.Office.Interop.Excel.Names Names = wb.Names;
foreach(Microsoft.Office.Interop.Excel.Name item in Names)
{
if(item.Name.Contains("cellexportcondition"))
{
MessageBox.Show(item.Name.ToString() + " = " + item.Value.ToString());
}
}
OR you can also get the cell values (if you want to do this in the workbook.beforeclose event you will need to reïnit the app and worksheet)
Microsoft.Office.Interop.Excel.Application tempexcellApp= wb.Application;
Microsoft.Office.Interop.Excel.Worksheet tws = (Microsoft.Office.Interop.Excel.Worksheet)tempexcellApp.Worksheets[1];
Microsoft.Office.Interop.Excel.Names Names = wb.Names;
foreach(Microsoft.Office.Interop.Excel.Name item in Names)
{
if(item.Name.Contains("cellexportcondition"))
{
string a = "";
if(tws.Range[item.Name].Value2 != null)
{
a = item.Name + " " + tws.Range[item.Name].Value2.ToString();
}else a = item.Name + " empty cell ";
MessageBox.Show(a);
}
}
I am developing one WPF application (using MVVM). I am also able to export my List to Excel file.But my problem is I am not able to make it readonly, so no one can able to make changes afterwards.
document.WorkbookPart.Workbook.WorkbookProtection=new WorkbookProtection
{
LockStructure=true
};
I only found this, but it's only make workbook readonly
Here is my code for creating excel file
public static bool CreateExcelDocument(DataSet ds, string excelFilename)
{
try
{
using (SpreadsheetDocument document = SpreadsheetDocument.Create(excelFilename, SpreadsheetDocumentType.Workbook))
{
document.AddWorkbookPart();
document.WorkbookPart.Workbook = new DocumentFormat.OpenXml.Spreadsheet.Workbook();
document.WorkbookPart.Workbook.WorkbookProtection=new WorkbookProtection
{
LockStructure = true,
};
// My thanks to James Miera for the following line of code (which prevents crashes in Excel 2010)
document.WorkbookPart.Workbook.Append(new BookViews(new WorkbookView()));
// If we don't add a "WorkbookStylesPart", OLEDB will refuse to connect to this .xlsx file !
WorkbookStylesPart workbookStylesPart = document.WorkbookPart.AddNewPart<WorkbookStylesPart>("rIdStyles");
Stylesheet stylesheet = new Stylesheet();
workbookStylesPart.Stylesheet = stylesheet;
CreateParts(ds, document);
}
Trace.WriteLine("Successfully created: " + excelFilename);
return true;
}
catch (Exception ex)
{
Trace.WriteLine("Failed, exception thrown: " + ex.Message);
return false;
}
}
private static void CreateParts(DataSet ds, SpreadsheetDocument spreadsheet)
{
// Loop through each of the DataTables in our DataSet, and create a new Excel Worksheet for each.
uint worksheetNumber = 1;
foreach (DataTable dt in ds.Tables)
{
// For each worksheet you want to create
string workSheetID = "rId" + worksheetNumber.ToString();
string worksheetName = dt.TableName;
WorksheetPart newWorksheetPart = spreadsheet.WorkbookPart.AddNewPart<WorksheetPart>();
newWorksheetPart.Worksheet = new DocumentFormat.OpenXml.Spreadsheet.Worksheet();
// create sheet data
newWorksheetPart.Worksheet.AppendChild(new DocumentFormat.OpenXml.Spreadsheet.SheetData());
// save worksheet
WriteDataTableToExcelWorksheet(dt, newWorksheetPart);
newWorksheetPart.Worksheet.Save();
// create the worksheet to workbook relation
if (worksheetNumber == 1)
spreadsheet.WorkbookPart.Workbook.AppendChild(new DocumentFormat.OpenXml.Spreadsheet.Sheets());
spreadsheet.WorkbookPart.Workbook.GetFirstChild<DocumentFormat.OpenXml.Spreadsheet.Sheets>().AppendChild(new DocumentFormat.OpenXml.Spreadsheet.Sheet()
{
Id = spreadsheet.WorkbookPart.GetIdOfPart(newWorksheetPart),
SheetId = (uint)worksheetNumber,
Name = dt.TableName
});
worksheetNumber++;
}
spreadsheet.WorkbookPart.Workbook.Save();
}
Before you open Excel, you can Lock your file with:
FileInfo cInfo = new FileInfo(Path);
cInfo.IsReadonly = true;
Than it will be readonly.
Is there any simple way to convert .xls file to .csv file ? (Excel)
in C# code ?
I mean to take an existing .xls file and convert them to .csv file
Here's a C# method to do this. Remember to add your own error handling - this mostly assumes that things work for the sake of brevity. It's 4.0+ framework only, but that's mostly because of the optional worksheetNumber parameter. You can overload the method if you need to support earlier versions.
static void ConvertExcelToCsv(string excelFilePath, string csvOutputFile, int worksheetNumber = 1) {
if (!File.Exists(excelFilePath)) throw new FileNotFoundException(excelFilePath);
if (File.Exists(csvOutputFile)) throw new ArgumentException("File exists: " + csvOutputFile);
// connection string
var cnnStr = String.Format("Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};Extended Properties=\"Excel 8.0;IMEX=1;HDR=NO\"", excelFilePath);
var cnn = new OleDbConnection(cnnStr);
// get schema, then data
var dt = new DataTable();
try {
cnn.Open();
var schemaTable = cnn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
if (schemaTable.Rows.Count < worksheetNumber) throw new ArgumentException("The worksheet number provided cannot be found in the spreadsheet");
string worksheet = schemaTable.Rows[worksheetNumber - 1]["table_name"].ToString().Replace("'", "");
string sql = String.Format("select * from [{0}]", worksheet);
var da = new OleDbDataAdapter(sql, cnn);
da.Fill(dt);
}
catch (Exception e) {
// ???
throw e;
}
finally {
// free resources
cnn.Close();
}
// write out CSV data
using (var wtr = new StreamWriter(csvOutputFile)) {
foreach (DataRow row in dt.Rows) {
bool firstLine = true;
foreach (DataColumn col in dt.Columns) {
if (!firstLine) { wtr.Write(","); } else { firstLine = false; }
var data = row[col.ColumnName].ToString().Replace("\"", "\"\"");
wtr.Write(String.Format("\"{0}\"", data));
}
wtr.WriteLine();
}
}
}
Checkout the .SaveAs() method in Excel object.
wbWorkbook.SaveAs("c:\yourdesiredFilename.csv", Microsoft.Office.Interop.Excel.XlFileFormat.xlCSV)
Or following:
public static void SaveAs()
{
Microsoft.Office.Interop.Excel.Application app = new Microsoft.Office.Interop.Excel.ApplicationClass();
Microsoft.Office.Interop.Excel.Workbook wbWorkbook = app.Workbooks.Add(Type.Missing);
Microsoft.Office.Interop.Excel.Sheets wsSheet = wbWorkbook.Worksheets;
Microsoft.Office.Interop.Excel.Worksheet CurSheet = (Microsoft.Office.Interop.Excel.Worksheet)wsSheet[1];
Microsoft.Office.Interop.Excel.Range thisCell = (Microsoft.Office.Interop.Excel.Range)CurSheet.Cells[1, 1];
thisCell.Value2 = "This is a test.";
wbWorkbook.SaveAs(#"c:\one.xls", Microsoft.Office.Interop.Excel.XlFileFormat.xlWorkbookNormal, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Microsoft.Office.Interop.Excel.XlSaveAsAccessMode.xlShared, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);
wbWorkbook.SaveAs(#"c:\two.csv", Microsoft.Office.Interop.Excel.XlFileFormat.xlCSVWindows, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Microsoft.Office.Interop.Excel.XlSaveAsAccessMode.xlShared, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);
wbWorkbook.Close(false, "", true);
}
Install these 2 packages
<packages>
<package id="ExcelDataReader" version="3.3.0" targetFramework="net451" />
<package id="ExcelDataReader.DataSet" version="3.3.0" targetFramework="net451" />
</packages>
Helper function
using ExcelDataReader;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ExcelToCsv
{
public class ExcelFileHelper
{
public static bool SaveAsCsv(string excelFilePath, string destinationCsvFilePath)
{
using (var stream = new FileStream(excelFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
IExcelDataReader reader = null;
if (excelFilePath.EndsWith(".xls"))
{
reader = ExcelReaderFactory.CreateBinaryReader(stream);
}
else if (excelFilePath.EndsWith(".xlsx"))
{
reader = ExcelReaderFactory.CreateOpenXmlReader(stream);
}
if (reader == null)
return false;
var ds = reader.AsDataSet(new ExcelDataSetConfiguration()
{
ConfigureDataTable = (tableReader) => new ExcelDataTableConfiguration()
{
UseHeaderRow = false
}
});
var csvContent = string.Empty;
int row_no = 0;
while (row_no < ds.Tables[0].Rows.Count)
{
var arr = new List<string>();
for (int i = 0; i < ds.Tables[0].Columns.Count; i++)
{
arr.Add(ds.Tables[0].Rows[row_no][i].ToString());
}
row_no++;
csvContent += string.Join(",", arr) + "\n";
}
StreamWriter csv = new StreamWriter(destinationCsvFilePath, false);
csv.Write(csvContent);
csv.Close();
return true;
}
}
}
}
Usage :
var excelFilePath = Console.ReadLine();
string output = Path.ChangeExtension(excelFilePath, ".csv");
ExcelFileHelper.SaveAsCsv(excelFilePath, output);
I had to come up with a hybrid solution after updating to Visual Studio 2022 and testing the most relevant answers for this to work.
First, we need to install the following Nuget packages: ExcelDataReader, ExcelDataReader.DataSet and System.Text.Encoding.CodePages
Then, for the sake of clean architecture, proceed to create a separate class within your corresponding namespace:
using ExcelDataReader;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace YourProjectNameSpace
{
public class ExcelFileHelper
{
/// <summary>
/// Converts a given XLS into CSV file format.
/// </summary>
public static bool SaveAsCsv(string excelFilePath, string destinationCsvFilePath)
{
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
using (var stream = new FileStream(excelFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
IExcelDataReader reader = null;
if (excelFilePath.EndsWith(".xls"))
{
reader = ExcelReaderFactory.CreateBinaryReader(stream);
}
else if (excelFilePath.EndsWith(".xlsx"))
{
reader = ExcelReaderFactory.CreateOpenXmlReader(stream);
}
if (reader == null)
return false;
var ds = reader.AsDataSet(new ExcelDataSetConfiguration()
{
ConfigureDataTable = (tableReader) => new ExcelDataTableConfiguration()
{
UseHeaderRow = false
}
});
var csvContent = string.Empty;
int row_no = 0;
while (row_no < ds.Tables[0].Rows.Count)
{
var arr = new List<string>();
for (int i = 0; i < ds.Tables[0].Columns.Count; i++)
{
arr.Add(ds.Tables[0].Rows[row_no][i].ToString());
}
row_no++;
csvContent += string.Join(",", arr) + "\n";
}
StreamWriter csv = new StreamWriter(destinationCsvFilePath, false);
csv.Write(csvContent);
csv.Close();
return true;
}
}
}
}
Notice I had to include this line at the beginning of the function:
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
If the line from above is omitted, you could end up getting the following error:
NotSupportedException: No data is available for encoding 1252
So, make sure of using it for better compatibility.
Finally, use example:
var execPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase)?.Replace("file:\\", "");
string FileNameXLS = "\\file.xls";
string FileNameCSV = "\\file.csv";
Console.WriteLine("Exporting file to CSV...." + "\n");
ExcelFileHelper.SaveAsCsv(execPath + FileNameXLS, execPath + FileNameCSV);
Console.WriteLine("File exported to CSV!" + "\n");
I need to do the same thing. I ended up with something similar to Kman
static void ExcelToCSVCoversion(string sourceFile, string targetFile)
{
Application rawData = new Application();
try
{
Workbook workbook = rawData.Workbooks.Open(sourceFile);
Worksheet ws = (Worksheet) workbook.Sheets[1];
ws.SaveAs(targetFile, XlFileFormat.xlCSV);
Marshal.ReleaseComObject(ws);
}
finally
{
rawData.DisplayAlerts = false;
rawData.Quit();
Marshal.ReleaseComObject(rawData);
}
Console.WriteLine();
Console.WriteLine($"The excel file {sourceFile} has been converted into {targetFile} (CSV format).");
Console.WriteLine();
}
If there are multiple sheets this is lost in the conversion but you could loop over the number of sheets and save each one as csv.
This is a modification of nate_weldon's answer with a few improvements:
More robust releasing of Excel objects
Set application.DisplayAlerts = false; before attempting to save to hide prompts
Also note that the application.Workbooks.Open and ws.SaveAs methods expect sourceFilePath and targetFilePath to be full paths (ie. directory path + filename)
private static void SaveAs(string sourceFilePath, string targetFilePath)
{
Application application = null;
Workbook wb = null;
Worksheet ws = null;
try
{
application = new Application();
application.DisplayAlerts = false;
wb = application.Workbooks.Open(sourceFilePath);
ws = (Worksheet)wb.Sheets[1];
ws.SaveAs(targetFilePath, XlFileFormat.xlCSV);
}
catch (Exception e)
{
// Handle exception
}
finally
{
if (application != null) application.Quit();
if (ws != null) Marshal.ReleaseComObject(ws);
if (wb != null) Marshal.ReleaseComObject(wb);
if (application != null) Marshal.ReleaseComObject(application);
}
}
I integrate the #mattmc3 aswer. If you want to convert a xlsx file you should use this connection string (the string provided by matt works for xls formats, not xlsx):
var cnnStr = String.Format("Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};Extended Properties=\"Excel 12.0;IMEX=1;HDR=NO\"", excelFilePath);
I had a similar task issue. Convert an inbound transactions like xlsx to tab delimited for automated processing by an existing system. Needs to run unattended. After reviewing many solutions on several different sites, and trying two of them, using MS Office Excel for C#, like above, and running into issues with different versions of MS Office, and older versions possibly on the PC, and having no control over that. I ended up going with... Aspose.Cells through NuGet. Solution is four lines of code.
string soureFilePath = "my/source/path/file.xlsx";
string targetFilePath = "my/output/path/file.txt";
var book = new Workbook(soureFilePath);
book.Save(targetFilePath, SaveFormat.Tsv);
It only converts sheet 1, and ignored sheets 2 and 3, but that is Ok for my use. I'm guessing it does have functionality to convert all of them if needed, I just did not need that, so did not look into it.
Their web site, incase folks want to view their information or license agreement (no cost use).
Manipulate Spreadsheets Easily | Aspose.Cells Universal Library
https://products.aspose.com/cells
Note: I do not work for Aspose, I am not affiliated with Aspose, and I am not profiting in any way from this posting.
I maintain some libraries that make the Excel to CSV conversion about as simple as it can be: Sylvan.Data.Excel and Sylvan.Data.Csv. Sylvan.Data.Excel can be used to read .xlsx, .xlsb, and .xls files. However, it can only write .xlsx files.
Here is a minimal example of converting an Excel spreadsheet to CSV:
using Sylvan.Data.Csv;
using Sylvan.Data.Excel;
using var reader = ExcelDataReader.Create("MyData.xlsx");
using var csvWriter = CsvDataWriter.Create("MyData.csv");
csvWriter.Write(reader);
These libraries have no external dependencies, other than the .NET runtime libraries; they don't require Excel be installed. They target the latest supported .NET versions and can run cross-platform. They are also the fastest libraries in the .NET ecosystem. They are MIT licensed, so can be freely used.