Saveas excel not working in C# - c#

I'm working on c# windows application with SQL server.
I'm getting an error like the following when tried to do Save-as in excel in c#. But inside workbook.open statement and workbook.saveas am changing readonly attribute as false.I need to save the excel with the same name only.Can anybody please help me.
Cannot access readonly document filename.xlsx
private static Microsoft.Office.Interop.Excel.Workbook mWorkBook;
private static Microsoft.Office.Interop.Excel.Sheets mWorkSheets;
private static Microsoft.Office.Interop.Excel.Worksheet mWSheet1;
private static Microsoft.Office.Interop.Excel.Application oXL;
string path = #"D:\Test\test.xlsx";
oXL = new Microsoft.Office.Interop.Excel.Application();
oXL.Visible = true;
oXL.DisplayAlerts = false;
mWorkBook = oXL.Workbooks.Open(path, 0, false, 5, "", "", false, Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "", true, false, 0, true, false, false);
//Get all the sheets in the workbook
mWorkSheets = mWorkBook.Worksheets;
//Get the allready exists sheet
mWSheet1 = Microsoft.Office.Interop.Excel.Worksheet)mWorkSheets.get_Item("Sheet1");
mWSheet1.Cells[i+2, 5] = DBDaysWorked;
mWorkBook.SaveAs(path, System.Reflection.Missing.Value,
System.Reflection.Missing.Value,
System.Reflection.Missing.Value,
false,false, Microsoft.Office.Interop.Excel.XlSaveAsAccessMode.xlShared,
false,
false,
System.Reflection.Missing.Value,
System.Reflection.Missing.Value,
System.Reflection.Missing.Value);
mWorkBook.Close(Missing.Value, Missing.Value, Missing.Value);
mWSheet1 = null;
mWorkBook = null;
oXL.Quit();
GC.WaitForPendingFinalizers();
GC.Collect();

The problem is with the flag XlSaveAsAccessMode.xlShared when saving the document, it throws the error:
This workbook cannot be shared because privacy has been enabled for this workbook. To share this workbook, click the File tab, and then click Excel Options. In the Excel Options dialog box, click Trust Center, and then click the Trust Center Settings button. In the Privacy Options category, clear the check box next to the option Remove personal information from file properties on save.
Removing this option does indeed fix the error. Alternatively setting the flag to XlSaveAsAccessMode.xlNoChange works if you're not bothered about sharing the workbook.
I'm not sure if you can automate removing this property from a workbook to prevent the error.
For the record, I found this by simply wrapping your code in a try/catch and throwing an error...
Garbage Collection
Before the try block that you should have now added hint hint ;-)
Initialise everything to null so that it will be accessible in the finally block.
Microsoft.Office.Interop.Excel.Workbook mWorkBook = null;
Microsoft.Office.Interop.Excel.Sheets mWorkSheets = null;
Microsoft.Office.Interop.Excel.Worksheet mWSheet1 = null;
Microsoft.Office.Interop.Excel.Application oXL = null;
You normal code is fine and can go in the try block. Catch only errors you can handle and recover from. Then in the finally block:
if (mWorkBook != null)
{
mWorkBook.Close(Missing.Value, Missing.Value, Missing.Value);
}
if (oXL != null)
{
oXL.Quit();
if (mWSheet1 != null)
{
Marshal.FinalReleaseComObject(mWSheet1);
mWSheet1 = null;
}
if (mWorkSheets != null)
{
Marshal.FinalReleaseComObject(mWorkSheets);
mWorkSheets = null;
}
if (mWorkBook != null)
{
Marshal.FinalReleaseComObject(mWorkBook);
mWorkBook = null;
}
Marshal.FinalReleaseComObject(oXL);
oXL = null;
}
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();

Related

Get Excel Active Worksheet with C#

I am wondering why this code gets the open worksheet when I run it in debug, and catch an exception when I run it with no debug..
Excel.Application xlApp = null;
Excel.Workbook xlWBook = null;
Excel.Worksheet xlWSheet = null;
Excel.Range xlRange = null;
try {
xlApp = (Excel.Application) Marshal.GetActiveObject("Excel.Application");
xlApp.Visible = true;
xlWBook = xlApp.ActiveWorkbook;
xlWSheet = xlWBook.ActiveSheet;
xlRange = xlWSheet.UsedRange;
logResult = logResult + "Agganciato " + xlWBook.Name + " \r\n";
} catch {
logResult = logResult + "Nessun file aperto rilevato. \r\n";
}
Any suggestion?
The following shows how one can use Excel Interop to either interact with an open Excel workbook or create a new workbook if one isn't open.
Try the following:
Add Reference (Option 1)
Download / install NuGet package: Microsoft.Office.Interop.Excel
Add Reference (Option 2)
In VS menu, click Project
Select Add Reference...
Click COM
Check Microsoft Excel xx.x Object Library (ex: Microsoft Excel 16.0 Object Library)
Add the following using directives:
using Excel = Microsoft.Office.Interop.Excel;
using System.Runtime.InteropServices;
using System.IO;
using System.Diagnostics;
CreateExcelWorkbook
public void CreateExcelWorkbook(string filename)
{
bool isExcelAlreadyRunning = false;
string logResult = string.Empty;
Excel.Application xlApp = null;
Excel.Workbook xlWBook = null;
Excel.Worksheet xlWSheet = null;
Excel.Range xlRange = null;
try
{
try
{
//if Excel isn't open, this will throw a COMException
xlApp = (Excel.Application)Marshal.GetActiveObject("Excel.Application");
//set value
isExcelAlreadyRunning = true;
}
catch (System.Runtime.InteropServices.COMException ex)
{
//create new instance
xlApp = new Excel.Application();
}
//whether or not to make Excel visible
xlApp.Visible = true;
//prevent prompting to overwrite existing file
xlApp.DisplayAlerts = false;
//disable user control while modifying the Excel Workbook
//to prevent user interference
//only necessary if Excel application Visibility property = true
//need to re-enable before exitin this method
//xlApp.UserControl = false;
if (xlApp.Workbooks.Count > 0)
xlWBook = xlApp.ActiveWorkbook;
else
xlWBook = xlApp.Workbooks.Add();
if (xlWBook.Worksheets.Count > 0)
xlWSheet = xlWBook.ActiveSheet;
else
xlWSheet = xlWBook.Sheets.Add();
xlRange = xlWSheet.UsedRange;
//set value
xlWSheet.Cells[1, 1] = $"Test {DateTime.Now.ToString("HH:mm:ss.fff")}";
//save Workbook - if file exists, overwrite it
// xlWBook.SaveAs(filename, Excel.XlFileFormat.xlWorkbookDefault, System.Reflection.Missing.Value, System.Reflection.Missing.Value, true, false, Excel.XlSaveAsAccessMode.xlNoChange, Excel.XlSaveConflictResolution.xlLocalSessionChanges, System.Reflection.Missing.Value, System.Reflection.Missing.Value, System.Reflection.Missing.Value, System.Reflection.Missing.Value);
xlWBook.SaveAs(filename, System.Reflection.Missing.Value, System.Reflection.Missing.Value, System.Reflection.Missing.Value, System.Reflection.Missing.Value, System.Reflection.Missing.Value, Excel.XlSaveAsAccessMode.xlNoChange, System.Reflection.Missing.Value, System.Reflection.Missing.Value, System.Reflection.Missing.Value, System.Reflection.Missing.Value, System.Reflection.Missing.Value);
logResult = logResult + "Agganciato " + xlWBook.Name + " \r\n";
}
catch
{
logResult = logResult + "Nessun file aperto rilevato. \r\n";
}
finally
{
if (xlWBook != null)
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlRange);
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlRange);
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlWSheet);
xlWSheet = null;
xlRange = null;
if (!isExcelAlreadyRunning)
{
//close workbook
xlWBook.Close(false);
}
//release all resources
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlWBook);
xlWBook = null;
}
System.Threading.Thread.Sleep(150);
if (xlApp != null)
{
if (!isExcelAlreadyRunning)
{
xlApp.Quit();
}
//release all resources
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp);
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlApp);
xlApp = null;
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
System.Threading.Thread.Sleep(175);
}
}
}
Usage:
CreateExcelWorkbook(filename);
//the following is necessary otherwise the Excel process seems to persist in Task Manager
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
Resources:
Microsoft.Office.Interop.Excel Namespace
Excel is still running though I quit and released the object
Cannot close Excel.exe after Interop process

Save/Close Excel Workbook and quit excel with no popups c#

I have the below code which works to delete the first row of a specified excel workbook. After this is done I would like to save (Overwrite changes) and exit the excel applications.
I gathered this may be achieved by Workbook.Close(True) but the popups still occur and the Object workbook is not referenced.
Any help would be much appreciated.
public void DeleteRows(string workbookPath)
{
// New Excel Application
Microsoft.Office.Interop.Excel.Application excelApp = new Microsoft.Office.Interop.Excel.Application();
//Open WorkBook
Microsoft.Office.Interop.Excel.Workbook excelWorkbook = excelApp.Workbooks.Open(workbookPath,
0, false, 5, "", "", false, Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "",
true, false, 0, true, false, false);
Microsoft.Office.Interop.Excel.Sheets excelSheets = excelWorkbook.Worksheets;
string currentSheet = "Sheet1";
Microsoft.Office.Interop.Excel.Worksheet excelWorksheet = (Microsoft.Office.Interop.Excel.Worksheet)excelSheets.get_Item(currentSheet);
Microsoft.Office.Interop.Excel.Range excelCell = (Microsoft.Office.Interop.Excel.Range)excelWorksheet.get_Range("A1", "A1");
Microsoft.Office.Interop.Excel.Range row = excelCell.EntireRow;
row.Delete(Microsoft.Office.Interop.Excel.XlDirection.xlUp);
}
Honestly, all I think that's missing from your code is a save and close. Once you save, the warning should not be issued.
using Excelx = Microsoft.Office.Interop.Excel;
public void DeleteRows(string workbookPath)
{
// New Excel Application
Excelx.Application excelApp = new Excelx.Application();
//Open WorkBook
Excelx.Workbook excelWorkbook = excelApp.Workbooks.Open(workbookPath,
0, false, 5, "", "", false, Excelx.XlPlatform.xlWindows, "",
true, false, 0, true, false, false);
Excelx.Sheets excelSheets = excelWorkbook.Worksheets;
string currentSheet = "Sheet1";
Excelx.Worksheet excelWorksheet = (Excelx.Worksheet)excelSheets.get_Item(currentSheet);
Excelx.Range excelCell = (Excelx.Range)excelWorksheet.get_Range("A1", "A1");
Excelx.Range row = excelCell.EntireRow;
row.Delete(Excelx.XlDirection.xlUp);
// This should be all you need:
excelWorkbook.Save();
excelWorkbook.Close();
}
I'd be puzzled if this doesn't work, but if it doesn't, it might help when you are debugging to make Excel visible and step through the code:
excelApp.Visible = true;
As a final last attempt, before you save, you can disable warnings which will tell Excel to dispense with any of the dialogs to try to save you from yourself:
excelApp.DisplayAlerts = false;
excelWorkbook.Save();
excelWorkbook.Close();
excelApp.DisplayAlerts = true;
And again, I think you shouldn't even get to step 2 for this to work, but let me know if it doesn't. We would have quite a puzzle.

Creates a new excel file while inserting other time in excel file in c# windows form.

I have this below function which inserts value to particualr cell in excel file. When i call this function second time it opens a new excel file and both time the value is entered into differnt excel file. How to have open single excel file and both of my value are entered into same excel file.
private bool insertIntoExcel(string pathname , string sheetname ,int excelRow, int excelColumn,string value)
{
try
{
Microsoft.Office.Interop.Excel._Application oXL = new Microsoft.Office.Interop.Excel.Application();
oXL.Visible = true;
oXL.DisplayAlerts = false;
Microsoft.Office.Interop.Excel.Workbook mWorkBook = oXL.Workbooks.Open(pathname, 0, false, 5, "", "", false, Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "", true, false, 0, true, false, false);
//Get all the sheets in the workbook
Microsoft.Office.Interop.Excel.Sheets mWorkSheets = mWorkBook.Worksheets;
//Get the allready exists sheet
Microsoft.Office.Interop.Excel._Worksheet mWSheet1 = (Microsoft.Office.Interop.Excel.Worksheet)mWorkSheets.get_Item(sheetname);
Microsoft.Office.Interop.Excel.Range range = mWSheet1.UsedRange;
mWSheet1.Cells[excelRow, excelColumn] = value;
}catch
{
return false;
}
return true;
}
Do not new a Excel Application and open a workbook every time. You should keep the workbook as a handler and keep it. Use the handler to do insertion or other operations.

Read Excel First Column using C# into Array

I'm trying to read in the values of the first column into an array. What's the best way to do that? Below is the code I have so far. Basically I'm trying to get the range of data for that column so I can pull the cell values into a system array.
Microsoft.Office.Interop.Excel.Application xlsApp = new Microsoft.Office.Interop.Excel.Application();
if (xlsApp == null)
{
Console.WriteLine("EXCEL could not be started. Check that your office installation and project references are correct.");
return null;
}
//xlsApp.Visible = true;
Workbook wb = xlsApp.Workbooks.Open(filename, 0, true, 5, "", "", true, XlPlatform.xlWindows, "\t", false, false, 0, true);
Sheets sheets = wb.Worksheets;
Worksheet ws = (Worksheet)sheets.get_Item(1);
//***Breaks Here***
ListColumn column = ws.ListObjects[1].ListColumns[1];
Range range = column.DataBodyRange;
System.Array myvalues = (System.Array)range.Cells.Value;
Here is what I ended up using to get it to work. Once you know that Columns actually returns a range, storing it that way seems to compile and run fine. Here is the working method in my ExcelReader class. I plan to use this for test driven data on WebDriver.
public static string[] FirstColumn(string filename)
{
Microsoft.Office.Interop.Excel.Application xlsApp = new Microsoft.Office.Interop.Excel.Application();
if (xlsApp == null)
{
Console.WriteLine("EXCEL could not be started. Check that your office installation and project references are correct.");
return null;
}
//Displays Excel so you can see what is happening
//xlsApp.Visible = true;
Workbook wb = xlsApp.Workbooks.Open(filename,
0, true, 5, "", "", true, XlPlatform.xlWindows, "\t", false, false, 0, true);
Sheets sheets = wb.Worksheets;
Worksheet ws = (Worksheet)sheets.get_Item(1);
Range firstColumn = ws.UsedRange.Columns[1];
System.Array myvalues = (System.Array)firstColumn.Cells.Value;
string[] strArray = myvalues.OfType<object>().Select(o => o.ToString()).ToArray();
return strArray;
}
First, I'd work out how many rows are actually being used:
Excel.Range allCellsInColumn = xlWorksheet.Range["A:A"];
Excel.Range usedCells = allCellsInColumn.Find("*", Missing.Value, Missing.Value, Missing.Value,
Excel.XlSearchOrder.xlByRows, Excel.XlSearchDirection.xlPrevious, false, Missing.Value, Missing.Value);
Once you have that you can retrieve the values:
System.Array values = usedCells.Values;
Once you have the values in the array you can skip over the elements with nothing in them. I don't think there is a way of retrieving just the cells with something in them without looping through them one at a time, which is very time consuming in Interop.
Is your data in a list? Your code seems to be looking for an Excel List which may not be present. If not you can just get the entire first column (A:A) into a Range and get it's Value:
Range firstCol = ws.Range("A:A");
System.Array values = range.Value as System.Array;

Searching a collection of excel sheets w/C#

I'm trying to get C# to examine whatever workbook the user has selected and find any sheets which would
contain stock data. Concretely this would mean looking at a range of cells (say r<6, c<10) for "Close", "close" or
"CLOSE".
The following code shows the point at which the user has selected an .xls file.
I'm not sure how to loop through the sheets in the workbook to look for the desired text.
I'm assuming it involves creating a collection of sheets and assigning it to those in the
current workbook, but my attempts so far haven't worked.
private void button1_Click(object sender, System.EventArgs e)
{
try
{
OpenFileDialog dlg = new OpenFileDialog();
dlg.Filter = "Excel Files (*.xls)|*.XLS";
if (dlg.ShowDialog() == DialogResult.OK)
{
// MessageBox.Show(dlg.FileName, "My Application", MessageBoxButtons.OKCancel, MessageBoxIcon.Asterisk);
Excel.Application xlApp = new Excel.ApplicationClass();
xlApp.Visible = true;
Excel.Workbook xlWorkbook = xlApp.Workbooks.Open(dlg.FileName,
0, false, 5, "", "", false, Excel.XlPlatform.xlWindows, "",
true, false, 0, true, false, false);
}
}
catch (Exception theException)
{
String errorMessage;
errorMessage = "Error: ";
errorMessage = String.Concat(errorMessage, theException.Message);
errorMessage = String.Concat(errorMessage, " Line: ");
errorMessage = String.Concat(errorMessage, theException.Source);
MessageBox.Show(errorMessage, "Error");
}
}
Thanks for any ideas.
Jeff
Always take extra care to clean up when using the Interop libraries. Otherwise, you're likely to end up with a couple dozen EXCEL.EXE processes running in the background while you debug (or when a user hits an error).
private static bool IsStockDataWorkbook(string fileName)
{
Excel.Application application = null;
Excel.Workbook workbook = null;
try
{
application = new Excel.ApplicationClass();
application.Visible = true;
workbook = application.Workbooks.Open(fileName, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value);
foreach (Excel.Worksheet sheet in workbook.Worksheets)
{
if (IsStockWorksheet(sheet))
{
return true;
}
}
return false;
}
finally
{
if (workbook != null)
{
workbook.Close(false, Missing.Value, Missing.Value);
}
if (application != null)
{
application.Quit();
}
}
}
private static bool IsStockWorksheet(Excel.Worksheet workSheet)
{
Excel.Range testRange = workSheet.get_Range("C10", Missing.Value);
string value = testRange.get_Value(Missing.Value).ToString();
return value.Equals("close", StringComparison.InvariantCultureIgnoreCase);
}
You'll need to assign objSheets to something, most likely:
Excel.Sheets objSheets = xlWorkbook.Sheets;
Your foreach statement should look more like this (with no prior declaration of the ws variable):
foreach(Excel.Worksheet ws in objSheets)
{
rng = ws.get_Range(ws.Cells[1,1], ws.Cells[5,9]);
}
Obviously, you'll want to do something more substantial in that loop.
This is an easy one :) use the sheets collection in the workbook object.
Workbooks workbooks = xlApp.Workbooks;
foreach(Workbook wb in workbooks)
{
Worksheets worksheets = wb.Worksheets;
foreach(Worksheet ws in worksheets)
{
Range range = ws.get_Range(ws.Cells[1,1], ws.Cells[5,9]);
Range match = range.Find("close", ws.Cells[1,1],
xlFindLookIn.xlValues, xlLookAt.xlPart,
xlSearchOrder.xlByColumns, xlSearchDirection.xlNext,
false, false, false); //that first false means ignore case
// do something with your match here
// this will only return the first match; to return all
// you'll need to run the match in a while loop
}
}

Categories

Resources