I want to create an excel file for each day I run a Test, in D drive.
However, if the file already exists - as in I already ran a test for that day - I want it to append to the file.
This is what I got so far but I feel like it doesn't look right.
Any help would be great, Thanks a lot guys.
Excel.Application excelApp = new Excel.Application();
string date = DateTime.Now.ToString("MMM dd-yy");
string myPath = #"D:\" + date + ".xls";
int rowIndex = 1; int colIndex = 1;
Excel._Workbook oWB;
try
{
oWB = (Excel._Workbook)(excelApp.Workbooks.Open(myPath));
excelApp.Visible = false;
excelApp.Cells[rowIndex, colIndex] = "IN TRY METHOD";
oWB.Save();
oWB.Close();
}
catch (Exception e)
{
oWB = (Excel._Workbook)(excelApp.Workbooks.Add(System.Reflection.Missing.Value));
excelApp.Visible = false;
excelApp.Cells[rowIndex, colIndex] = "IN CATCH METHOD";
oWB.SaveAs(myPath);
oWB.Close();
}
Also I noticed that when I open the file it says:
The file you are trying to open is in a different format than
specified by file extension. Verify it's from a trusted source before
opening the file. Do you want to open now?
It works but it has this message before hand.
So please tell me what I'm doing wrong and also how to make this code a lot cleaner.
Check if the file is on disk and then take appropriate action instead of abusing try{}catch{}
if(File.Exists(myPath))
{
ModifyExcel();
}
else
{
CreateExcel();
}
private void ModifyExcel()
{
oWB = (Excel._Workbook)(excelApp.Workbooks.Open(myPath));
excelApp.Visible = false;
excelApp.Cells[rowIndex, colIndex] = "MODIFY";
oWB.Save();
oWB.Close();
}
private void CreateExcel()
{
oWB = (Excel._Workbook)(excelApp.Workbooks.Add(System.Reflection.Missing.Value));
excelApp.Visible = false;
excelApp.Cells[rowIndex, colIndex] = "CREATE";
oWB.SaveAs(myPath);
oWB.Close();
}
Instead of using a try .. catch this way you should first test to see if the file exists. If it does, then do your append code. If it doesn't then do the workbook creation code.
The reason you get that message is that you are using the newer Excel COM objects to create the file. Instead of emitting .xls as the extension try .xlsx
Related
I working with windows application and processing large excel file. I need to save 100k rows from datatable to excel file.
Currently my create excel function only support 65,500 rows only?
But I need to save excel file more than that. Is it possible?
If yes then, Kindly give the source?
Here is my code
public static void ExportDataSetToExcel(DataTable dt, int index, string strFilePathName)
{
Console.WriteLine("Creating Output Excel file");
string fileFomat = getExcelFileName(index) + (DateTime.Now.ToString("yyyyMMddTHHmmss"));
Microsoft.Office.Interop.Excel.Application objXL = null;
Microsoft.Office.Interop.Excel.Workbook objWB = null;
try
{
objXL = new Microsoft.Office.Interop.Excel.Application();
objWB = objXL.Workbooks.Add(1);
int sheetcount = 1;
Microsoft.Office.Interop.Excel.Worksheet objSHT = (Microsoft.Office.Interop.Excel.Worksheet)objWB.Sheets.Add();
Microsoft.Office.Interop.Excel.Range cells = objSHT.Cells;
cells.NumberFormat = "#";
//formatRange = objSHT.get_Range("b1",Type.Missing);
//formatRange.EntireRow.Font.Bold = true;
objSHT.Name = "RunOrderSheet";
for (int j = 0; j < dt.Rows.Count; j++)
{
for (int i = 0; i < dt.Columns.Count; i++)
{
//Condition to put column names in 1st row
//Excel work book indexes start from 1,1 and not 0,0
if (j == 0)
{
objSHT.Cells[1, i + 1] = dt.Columns[i].ColumnName.ToString();
}
//Writing down data
objSHT.Cells[j + 2, i + 1] = dt.Rows[j][i].ToString();
}
}
sheetcount++;
objWB.Saved = true;
objWB.SaveCopyAs(strFilePathName.Trim() + fileFomat.Trim() + ".xlsx");
objWB.Close();
objXL.Quit();
Console.WriteLine("Process done");
}
catch (Exception ex)
{
objWB.Saved = true;
objWB.Close();
objXL.Quit();
log.Error(ex.Message);
}
}
Excel and the COM interface with C# absolutely will support more than 65k lines. Just to prove that it's possible, run the following code:
public static void ExportDataSetToExcel()
{
Excel.Application objXL = null;
Excel.Workbook objWB = null;
objXL = new Microsoft.Office.Interop.Excel.Application();
objXL.Visible = true;
objWB = objXL.Workbooks.Add(1);
Excel.Worksheet objSHT = objWB.Sheets.Add();
for (int row = 1; row < 100000; row++)
{
objSHT.Cells[row, 1].Value2 = row;
}
objWB.SaveAs("c:\test\test.xlsx", Excel.XlFileFormat.xlOpenXMLWorkbook);
objWB.Close();
objXL.Quit();
}
Which leads me to several possibilities as to what your issue might be, in order of my suspicions (first being what I think is the most likely):
Since you have a try/catch block that is very nicely trapping errors and making sure that anything that happens within your loop while still saving the file, something is tripping the proverbial circuit breaker. While I don't see any glaring errors, it could be anything. Put a breakpoint within the catch block and see what ex contains. Also, make note of the specific record that is causing the error so you can step through just that one record and see where it occurs.
I noticed you are using SaveCopyAs rather than SaveAs. SaveCopyAs automatically saves in the exact same format as the original. I would think your Excel comes up with the default xlsx, but I certainly can't guarantee that. Using SaveAs with the explicit file format will guarantee it is saved in a format that supports more than 65k lines:
.
objWB.SaveAs("c:\test\test.xlsx", Excel.XlFileFormat.xlOpenXMLWorkbook);
Without knowing how large your datatable is, it might simply be a memory issue also that's tripping the try/catch.
I have created a C# program that takes a XML document as input, drag and drop on exe. The output is an xlsx sheet i create using the input.
When I give the exe to a coworker to run, it works as expected, and outputs the xlsx. But now after the coworker closes out of excel and opens any excel workbook any where on the computer, the xlsx that was created from my program always opens.
This doesn't happen on my computer. Not sure how it's even possible. Is there some setting I am missing in saving the files?
public static void WriteFile2(string filePath, InitialData initialData)
{
var excelApp = new Application {Visible = false};
var workbooks = excelApp.Workbooks;
_Workbook workbook = workbooks.Add(XlWBATemplate.xlWBATWorksheet);
var sheets = workbook.Worksheets;
var worksheet = (_Worksheet) sheets.Item[1];
worksheet.Name = "BGPM1000XX";
excelApp.StandardFont = "Arial";
excelApp.StandardFontSize = 8;
/*Skipping body of code, mostly writing to cells and formatting*/
//Insert Cover sheet tab
var newWorksheet = (Worksheet) excelApp.Worksheets.Add();
newWorksheet.Name = "COVER SHEET";
/*Skipping body of code, mostly writing to cells and formatting*/
try
{
//TODO: make png path generic
#if DEBUG
var fileName = #"..\..\..\ExternalReferences\logo.png";
var newname = Path.GetFullPath(fileName);
newWorksheet.Shapes.AddPicture(
newname,
MsoTriState.msoFalse, MsoTriState.msoCTrue, 280, 5, 55, 45);
#else
newWorksheet.Shapes.AddPicture(
#"logo.png",
MsoTriState.msoFalse, MsoTriState.msoCTrue, 280, 5, 55, 45);
#endif
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.StackTrace);
}
workbook.Saved = true;
var version = 1;
var inUse = true;
var extension = Path.GetExtension(filePath);
while (inUse)
{
inUse = IsFileInUse(filePath);
if (!inUse)
break;
filePath = filePath.Substring(0, filePath.Length - extension.Length) + "_V" + version + ".xlsx";
version++;
}
excelApp.DisplayAlerts = false;
//workbook.SaveAs(filePath);
workbook.SaveAs(filePath, XlFileFormat.xlWorkbookDefault, Type.Missing, Type.Missing, false, false, XlSaveAsAccessMode.xlNoChange, XlSaveConflictResolution.xlLocalSessionChanges, Type.Missing, Type.Missing);
excelApp.UserControl = false;
excelApp.Quit();
}
make sure you kill the process if there is an exception in your code. A good way to handle it is with a try..catch block. After you call the exception, call something like this:
public void KillProcess()
{
_workBook.Close();
_workBooks.Close();
_excelApp.Quit();
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
Marshal.FinalReleaseComObject(_workSheets);
Marshal.FinalReleaseComObject(_workBook);
Marshal.FinalReleaseComObject(_workBooks);
Marshal.FinalReleaseComObject(_excelApp);
}
Also, to clean up all of your previous EXCEL tasks, restart your computer.
Two things
Put in a call to Close the document before you call Quit.
Place the whole Excel code into a try catch and have this command as a finally to release the com object.
System.Runtime.InteropServices.Marshal.ReleaseComObject( excelApp );
I base this advice off of my blog article C# Open Word Documents using Visual Studio 2010 and .Net 4 which provides an example of working with the interops.
I have been struggling with this problem for a couple of days, I have made reasearch and applied all the suggestions I found on various forums but I'm still unable to solve it.
My problem is with excel using interop library, I have an excel file used as template, so I am opening it and saving in a new location with a new name. Everything works great except that the Excel process keeps runing after the file is created and closed.
This is my code
protected string CreateExcel(string strProjectID, string strFileMapPath)
{
string strCurrentDir = HttpContext.Current.Server.MapPath("~/Reports/Templates/");
string strFile = "Not_Created";
Application oXL;
Workbook oWB;
oXL = new Application();
oXL.Visible = false;
Workbooks wbks = oXL.Workbooks;
//opening template file
oWB = wbks.Open(strFileMapPath);
oXL.Visible = false;
oXL.UserControl = false;
strFile = strProjectID + "_" + DateTime.Now.Ticks.ToString() + ".xlsx";
//Saving file with new name
oWB.SaveAs(strCurrentDir + strFile, XlFileFormat.xlWorkbookDefault, null, null, false, false, XlSaveAsAccessMode.xlExclusive, false, false, null, null);
oWB.Close(false, strCurrentDir + strFile, Type.Missing);
wbks.Close();
oXL.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(oXL);
System.Runtime.InteropServices.Marshal.ReleaseComObject(wbks);
System.Runtime.InteropServices.Marshal.ReleaseComObject(oWB);
oWB = null;
oXL = null;
wbks = null;
GC.Collect();
return strFile;
}
As you can see I am closing and releasing all the objects but the application does not quit.
I'm testing in a Windows Server 2008(production) and Windows 7(development) both in 32bits with IIS7.
Try
Process excelProcess = Process.GetProcessesByName("EXCEL")[0];
if (!excelProcess.CloseMainWindow())
{
excelProcess.Kill();
}
This is how I got around this problem:
// Store the Excel processes before opening.
Process[] processesBefore = Process.GetProcessesByName("excel");
// Open the file in Excel.
Application excelApplication = new Application();
Workbook excelWorkbook = excelApplication.Workbooks.Open(Filename);
// Get Excel processes after opening the file.
Process[] processesAfter = Process.GetProcessesByName("excel");
// Now find the process id that was created, and store it.
int processID = 0;
foreach (Process process in processesAfter)
{
if (!processesBefore.Select(p => p.Id).Contains(process.Id))
{
processID = process.Id;
}
}
// Do the Excel stuff
// Now close the file with the COM object.
excelWorkbook.Close();
excelApplication.Workbooks.Close();
excelApplication.Quit();
// And now kill the process.
if (processID != 0)
{
Process process = Process.GetProcessById(processID);
process.Kill();
}
Have a look here: How can I get the ProcessID (PID) for a hidden Excel Application instance
You can track down your ProcessID via GetWindowThreadProcessId API and than kill the process that particularly matches your instance of Excel Application object.
[DllImport("user32.dll")]
static extern int GetWindowThreadProcessId(int hWnd, out int lpdwProcessId);
Process GetExcelProcess(Microsoft.Office.Interop.Excel.Application excelApp)
{
int id;
GetWindowThreadProcessId(excelApp.Hwnd, out id);
return Process.GetProcessById(id);
}
void TerminateExcelProcess(Microsoft.Office.Interop.Excel.Application excelApp)
{
var process = GetExcelProcess(excelApp);
if (process != null)
{
process.Kill();
}
}
I created this method for it, in my tests it works.
private void ClearMemory(Application excelApp) {
excelApp.DisplayAlerts = false;
excelApp.ActiveWorkbook.Close(0);
excelApp.Quit();
Marshal.ReleaseComObject(excelApp);
}
Try using Open XML SDK 2.0 for Microsoft Office library to create excel document instead of using interop assemblies. It runs a lot faster in my experience and it's easier to use.
Here is the VB version -- I have a large project that uses this and not the time to convert to a better system, so here is the same answer... in vb.NET
Use this to get the process ID (Prior to opening the excel sheet)
Dim excelProcess(0) As Process
excelProcess = Process.GetProcessesByName("excel")
After you're done with your sheet:
xlWorkBook.Close(SaveChanges:=False)
xlApp.Workbooks.Close()
xlApp.Quit()
'Kill the process
If Not excelProcess(0).CloseMainWindow() Then
excelProcess(0).Kill()
End If
Simple rule: avoid using double-dot-calling expressions, such as this:
var workbook = excel.Workbooks.Open(/*params*/)
(Reference)
oWB.Close(false, strCurrentDir + strFile, Type.Missing);
oWB.Dispose();
wbks.Close();
wbks.Dispose();
oXL.Quit();
oXL.Dispose();
System.Runtime.InteropServices.Marshal.ReleaseComObject(oXL);
System.Runtime.InteropServices.Marshal.ReleaseComObject(wbks);
System.Runtime.InteropServices.Marshal.ReleaseComObject(oWB);
I have an Excel file.
I need to open it, select specific sheets from it, and convert those sheets to a PDF format. I am able to convert the whole excel file, I just don't know how to convert only the specific sheets.
My idea is to copy specific sheets from an existing file to a new temporary file, and convert that whole new temporary file to PDF.
Maybe there's an easier way?
My code so far is =>
using Word = Microsoft.Office.Interop.Word;
using Excel = Microsoft.Office.Interop.Excel;
public static void ExportExcel(string infile, string outfile, int[] worksheets)
{
Excel.Application excelApp = null;
Excel.Application newExcelApp = null;
try
{
excelApp = new Excel.Application();
excelApp.Workbooks.Open(infile);
//((Microsoft.Office.Interop.Excel._Worksheet)excelApp.ActiveSheet).PageSetup.Orientation = Microsoft.Office.Interop.Excel.XlPageOrientation.xlLandscape;
excelApp.ActiveWorkbook.ExportAsFixedFormat(Excel.XlFixedFormatType.xlTypePDF, outfile);
}
finally
{
if (excelApp != null)
{
excelApp.DisplayAlerts = false;
excelApp.SaveWorkspace();
excelApp.Quit();
}
}
}
Maybe the ExportAsFixedFormat method can be set to consider only specific pages (sheets) while converting?
If not, how do I copy the sheets from one file to another?
Thanks!
You might be able to just print the sheets you want from the original file. I fired up the Macro Recorder, selected a couple of sheets, and Saved As to PDF. Here's the code:
Sheets(Array("Sheet1", "Sheet2")).Select
ActiveSheet.ExportAsFixedFormat Type:=xlTypePDF, Filename:= _
"C:\Users\doug\Documents\Book1.pdf", Quality:=xlQualityStandard, _
IncludeDocProperties:=True, IgnorePrintAreas:=False, OpenAfterPublish:=True
When I changed the selected sheets and ran again it worked as expected.
What's strange is that in the actual Save As dialog, you can go to Options and check "Selected Sheets." That's not available as a parameter to ExportAsFixedFormat, but it was automatically selected in the dialog, and maybe is the default also when called.
You could simply copy the file to the new destination, open the destination, remove the unwanted sheets and export. This is an example (tested) of my idea.
// infile is the excel file, outfile is the pdf to build, sheetToExport is the name of the sheet
public static void ExportExcel(string infile, string outfile, string sheetToExport)
{
Microsoft.Office.Interop.Excel.Application excelApp = new
Microsoft.Office.Interop.Excel.Application();
try
{
string tempFile = Path.ChangeExtension(outfile, "XLS");
File.Copy(infile, tempFile, true);
Microsoft.Office.Interop.Excel._Workbook excelWorkbook =
excelApp.Workbooks.Open(tempFile);
for(int x = excelApp.Sheets.Count; x > 0; x--)
{
_Worksheet sheet = (_Worksheet)excelApp.Sheets[x];
if(sheet != null && sheet.Name != sheetToExport)
sheet.Delete();
}
excelApp.ActiveWorkbook.ExportAsFixedFormat(XlFixedFormatType.xlTypePDF, outfile);
}
finally
{
if (excelApp != null)
{
excelApp.DisplayAlerts = false;
excelApp.SaveWorkspace();
excelApp.Quit();
}
}
}
I have an excel workbook opened via double-clicking it in windows explorer but cannot access it in code
Excel.Application xlApp = (Application)Marshal.GetActiveObject("Excel.Application");
Excel.Workbooks xlBooks = xlApp.Workbooks;
xlBooks.Count equals 0, why isn't it referencing my opened workbook?
EDIT
Here are the various scenarios and what is happening:
Scenario 1: If the file is not already open
Code opens workbook, I am happy.
Scenario 2: If the file is initially opened from code and I close and reopen the app
Code references file just fine xlBooks.Count equals 1, I am happy.
Scenario 3: If the file is initially opened not from code, and via double-clicking it in explorer
Code opens another instance of the file xlBooks.Count equals 0, I am in a rage!
Here is the entire code as it stands right now
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.Office.Interop.Excel;
public class ExcelService : IExcelService
{
const string _filePath = #"C:\Somewhere";
const string _fileName = #"TestFile.xlsb";
string _fileNameAndPath = Path.Combine(_filePath, _fileName);
Application xlApp;
Workbooks xlBooks;
Workbook xlBook;
Worksheet xlSheet;
public ExcelService()
{
try
{
xlApp = (Application)Marshal.GetActiveObject("Excel.Application");
xlBooks = xlApp.Workbooks;
var numBooks = xlBooks.Count;
Log.Info("Number of workbooks: {0}".FormatWith(numBooks));
if (numBooks > 0)
{
xlBook = xlBooks[1];
Log.Info("Using already opened workbook");
}
else
{
xlBook = xlBooks.Open(_fileNameAndPath);
Log.Info("Opening workbook: {0}".FormatWith(_fileNameAndPath));
}
xlSheet = (Worksheet)xlBook.Worksheets[1];
// test reading a named range
string value = xlSheet.Range["TEST"].Value.ToString();
Log.Info(#"TEST: {0}".FormatWith(value));
xlApp.Visible = true;
}
catch (Exception e)
{
Log.Error(e.Message);
}
}
~ExcelService()
{
GC.Collect();
GC.WaitForPendingFinalizers();
try
{
Marshal.FinalReleaseComObject(xlSheet);
}
catch { }
try
{
Marshal.FinalReleaseComObject(xlBook);
}
catch { }
try
{
Marshal.FinalReleaseComObject(xlBooks);
}
catch { }
try
{
Marshal.FinalReleaseComObject(xlApp);
}
catch { }
}
}
I know this thread is a little old, but I found another way of doing this. When you create a new Excel.Applicationobject you have to create the WorkBooks object. When you access an already opened Excel file, the WorkBooks object is already created, so you just need to add a new WorkBook to the existing one. #Tipx 's solution works great if you have access to the WorkBook name, but in my case the current WorkBook name is always random. Here's the solution I came up with to get around this:
Excel.Application excelApp = null;
Excel.Workbooks wkbks = null;
Excel.Workbook wkbk = null;
bool wasFoundRunning = false;
Excel.Application tApp = null;
//Checks to see if excel is opened
try
{
tApp = (Excel.Application)System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application");
wasFoundRunning = true;
}
catch (Exception)//Excel not open
{
wasFoundRunning = false;
}
finally
{
if (true == wasFoundRunning)
{
excelApp = tApp;
wkbk = excelApp.Workbooks.Add(Type.Missing);
}
else
{
excelApp = new Excel.Application();
wkbks = excelApp.Workbooks;
wkbk = wkbks.Add(Type.Missing);
}
//Release the temp if in use
if (null != tApp) { Marshal.FinalReleaseComObject(tApp); }
tApp = null;
}
//Initialize the sheets in the new workbook
Might not be the best solution but it worked for my needs. Hope this helps someone. :)
If all your workbooks are opened in the same Excel instance (you can check this by checking if you can switch from one to the other using Alt-tab). You can simply refer to the other using Workbooks("[FileName]"). So, for example :
Dim wb as Workbook //for C#, type Excel.Workbook wb = null;
Set wb = Workbooks("MyDuperWorkbook.xlsx") //for C#, type wb = Excel.Workbooks["MyDuperWorkbook.xlsx"];
wb.Sheets(1).Cells(1,1).Value = "Wahou!"