Writing to sheet leads to "name already taken" - c#

I'm learning how to use Interop.Excel. The test Winforms program reads an existing Excel file, checks if a tab names "Added_by_program" exists, deletes the sheet if it does, and creates a new sheet named "Added_by_program." If I don't try to write to the new sheet, the program runs perfectly, over and over. I get problems when I try to write to it. If the sheet is not present in the original file, the program runs perfectly one time, and writes correctly to the newly created sheet. but on subsequent runs, I get:
"System.Runtime.InteropServices.COMException: 'That name is already taken. Try a different one.'"
for the line that tries to name the new sheet. I have to manually kill the open Excel instance. What am I missing?
Code (irrelevant lines taken out)
using System;
using System.IO;
using System.Windows.Forms;
using Microsoft.Office.Interop.Excel;
namespace excelReadWrite
{
public partial class Form1 : Form
{
string readFolder = myPath;
string inFileName = #"Aram test excel file.xlsx";
string newSheetName = "Added_by_program";
Range rawRange = null;
Range pasteRange = null;
int rawCols = 0;
int rawRows = 0;
int iInSheet = 0;
int iNewSheet = 0;
int nInSheets = 0;
bool foundRawSheet = false;
bool foundNewSheet = false;
Worksheet worksheet = null;
public Form1()
{
InitializeComponent();
}
private void start_Button_Click(object sender, EventArgs e)
{
string inFile = myPath+ inFileName;
int nSheets = 0;
string sheetNames = "";
// Open Excel workbook to read
Microsoft.Office.Interop.Excel.Application xl = new Microsoft.Office.Interop.Excel.Application();
Workbook workbook = xl.Workbooks.Open(inFile);
// Count worksheets in opened Excel file
nSheets = workbook.Worksheets.Count;
nSheets_TextBox.Text = nSheets.ToString();
nInSheets = 0;
foreach (Worksheet worksheet in workbook.Worksheets)
++nInSheets;
//foreach (Worksheet worksheet in workbook.Worksheets)
for (int iSheet = nInSheets; iSheet >= 1; --iSheet)
{
worksheet = workbook.Worksheets[iSheet];
sheetNames += " " + worksheet.Name;
// The program is going to add a worksheet. If it already exists, delete it before adding it.
if (string.Equals(worksheet.Name, newSheetName))
{
workbook.Worksheets[iSheet].Delete();
}
}
// Add a new sheet and name it
if (foundRawSheet)
{
newWorksheet = workbook.Worksheets.Add();
newWorksheet.Name = newSheetName;
// THE NEXT LINE IS THE PROBLEM LINE
// "Written" WILL BE WRITTEN TO A1:C3 WHEN THE SHEET IS CREATED, BUT THIS LINE
// CAUSES THE ERROR IN SUBSEQUENT RUNS
// IF I COMMENT IT OUT, THE PROGRAM RUNS FINE, REPEATEDLY
newWorksheet.Range["A1", "C3"].Value2 = "Written";
workbook.Save();
workbook.Close();
xl.Quit();
}
}
}

Did you set xl.DisplayAlerts=false?
If not, deleting a worksheet with existing data will cause a confirm dialog to be displayed. .
If the Excel application is visible, the Worksheet.Delete will block until the dialog is acknowledged.
If the Excel application is not visible, your code execution will proceed (the dialog is effectively canceled --> delete not confirmed), but the worksheet will not be deleted.

Related

How to return cell address if you know cell value

Heads up that I am very new to coding.
I'm writing code in c# and I've linked to an excel document.
I need to be able to find the cell address of a cell that has a cell value that I already know. For example, I need to find the address of the cell that has the word 'chocolate' and for it, for example, to return "A2".
Here's what I've written so far but I'm very new to coding so don't know if it works:
using System;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using Microsoft.Office.Interop.Excel;
using _Excel = Microsoft.Office.Interop.Excel;
namespace Name
{
class Program
static void Main(string[] args)
{
}
class Excel
// client can put in their own excel spreadsheet and whatever worksheet within it, they then put the time down
static int GetCellValue(string ExcelSheet = "C:\Users\Laura Dennis\Desktop\Translation
Internship\Coding\Data.xlsx", int worksheet = 2, int time)
{
// create app
var excelApp = new Excel.Application();
// open workbook
var workbook = excelApp.Workbooks.Open(
# ExcelSheet
);
// open worksheet
var Excel.Worksheet worksheet = worksheet;
// get the value for each cell
int value = cell.InnerText
// read each cell within the first column
Excel.Range namedRange = (Excel.Range)worksheet.get_Range("A2", "A55");
// see if it matches
foreach (Excel.Range cell in namedRange.Cells)
{
// return cell address and from here can get other cell values to calculate the
outstanding series of payments
if (value == time)
{string RangeAddress(Excel.Range rng)
{
return rng.get_AddressLocal(false, false, Excel.XlReferenceStyle.xlA1,
missing, missing);
}
}
}
}
}

C# How to write each table in a Word file to its own Excel file

I'm trying to write code in C# WinForms that allows a user to select a directory tree, and extract all of the table data from a word document into an excel file. Presently, the code compiles and you can select your directories, etc, but once it begins to iterate through the loop for each table it crashes.
The program successfully opens the first word file and writes the first excel file (table_1_whatever.xlsx) and saves it in the destination folder. However, on the second table in the same file I get this error on this line of code:
worksheet.Cells[row, col] = objExcelApp.WorksheetFunction.Clean(table.Cell(row, col).Range.Text);
System.Runtime.InteropServices.COMException: 'The requested member of the collection does not exist.'
I can't seem to figure out why it doesn't exist. Each time it goes through the foreach loop it should be creating a new worksheet, but it doesn't appear to be working. Any insight, examples, or suggestions are welcome!
Code:
private void WordRunButton_Click(object sender, EventArgs e)
{
var excelApp = new excel.Application();
excel.Workbooks workbooks = excelApp.Workbooks;
var wordApp = new word.Application();
word.Documents documents = wordApp.Documents;
wordApp.Visible = false;
excelApp.Visible = false;
string[] fileDirectories = Directory.GetFiles(WordSourceBox.Text, "*.doc*",
SearchOption.AllDirectories);
foreach (var item in fileDirectories)
{
word._Document document = documents.Open(item);
int tableCount = 1;
foreach (word.Table table in document.Tables)
{
if (table.Cell(1, 1).ToString() != "Doc Level")
{
string wordFile = item;
appendName = Path.GetFileNameWithoutExtension(wordFile) + "_Table_" + tableCount + ".xlsx";
var workbook = excelApp.Workbooks.Add(1);
excel._Worksheet worksheet = (excel.Worksheet)workbook.Sheets[1];
for (int row = 1; row <= table.Rows.Count; row++)
{
for (int col = 1; col <= table.Columns.Count; col++)
{
var cell = table.Cell(row, col);
var range = cell.Range;
var text = range.Text;
var cleaned = excelApp.WorksheetFunction.Clean(text);
worksheet.Cells[row, col] = cleaned;
}
}
workbook.SaveAs(Path.Combine(WordOutputBox.Text, Path.GetFileName(appendName)), excel.XlFileFormat.xlWorkbookDefault);
workbook.Close();
Marshal.ReleaseComObject(workbook);
}
else
{
WordOutputStreamBox.AppendText(String.Format("Table {0} ignored\n", tableCount));
}
WordOutputStreamBox.AppendText(appendName + "\n");
tableCount++;
}
document.Close();
Marshal.ReleaseComObject(document);
WordOutputStreamBox.AppendText(item + "\n");
}
WordOutputStreamBox.AppendText("\nAll files parsed");
excelApp.Application.Quit();
workbooks.Close();
excelApp.Quit();
WordOutputStreamBox.AppendText("\nExcel files closed");
Marshal.ReleaseComObject(workbooks);
Marshal.ReleaseComObject(excelApp);
WordOutputStreamBox.AppendText("\nExcel files released");
wordApp.Application.Quit();
wordApp.Quit();
WordOutputStreamBox.AppendText("\nWord files have been quit");
Marshal.ReleaseComObject(documents);
Marshal.ReleaseComObject(wordApp);
WordOutputStreamBox.AppendText("\nWord files have been released\n");
}
Edit 1:(Sorry for posting in the wrong place the first time!)
Ok, so the problem has been isolated...
The code logic of the code was fine, and the table was in fact there. The issue is that the second table of these files has a set of split cells in it, so, when it reaches the cell that contains it, the program crashes.
As a temp fix, I have just set it to ignore the table if the header == whatever. Does anyone know of a solution that actually allows to extract this data though?

Workbook is Created with two worksheets in C# and Always ask for overwriting with Book7, Book8, etc

I want to create an Excel Workbook with one WorkSheet, then Save the data and close it. But it creates two Sheets. Sheet2 and Sheet1. It writes the data successfully though. Everytime I delete the xlsx file manually, it asks me for overwriting Book8.xlsx even though there is not such a file with this name.
I just want one worksheet and I want to have it with a specific name too, instead of Sheet1
class Program
{
static void Main(string[] args)
{
Excel excel = new Excel(#"d:\Test.xlsx", 1);
excel.WriteToCell(0, 0, "Test2");
excel.Save();
excel.SaveAs(#"d:\Test.xlsx");
excel.Close();
}
}
Inside Excel.cs
using Microsoft.Office.Interop.Excel;
using _Excel = Microsoft.Office.Interop.Excel;
namespace Excel
{
class Excel
{
_Application excel = new _Excel.Application();
Workbook wb;
Worksheet ws;
public Excel(string path, int Sheet)
{
excel.Visible = false;
wb = excel.Workbooks.Add();
ws = wb.Worksheets.Add();
}
public void WriteToCell(int i, int j, string s)
{
i++;
j++;
ws.Cells[i, j].Value = s;
}
public void Save()
{
wb.Save();
}
public void SaveAs(string path)
{
wb.SaveAs(path);
}
public void Close()
{
wb.Close();
}
}
}
Remove the line ws = wb.Worksheets.Add(); from Excel constructor to not create the second sheet.
public Excel(string path, int Sheet)
{
excel.Visible = false;
wb = excel.Workbooks.Add();
ws = (Worksheet)wb.Worksheets[1];
if (ws == null)
{
Console.WriteLine("Worksheet could not be created. Check that your office installation and project references are correct.");
}
}
To rename the worksheet:
public RenameWorksheet(string newName)
{
ws.Name = newName;
}
If you delete a file from a directory, it should be deleted. However, if you are having a pop saying the file exists and do you want to replace it then my guess is it is probably a human error here. Maybe you are looking under a wrong directory? Happens to the best of us at times ;) Either way as a programmer you can choose to delete the file if it exists before creation to avoid human errors:
if(File.Exists(fileName))
{
File.Delete(fileName);
}
or simply rename it if you don't want to replace it.
EDIT
On second look I realized you are creating a new file:
Excel excel = new Excel(#"d:\Test.xlsx", 1);
and then calling:
excel.SaveAs(#"d:\Test.xlsx");
what else do you expect?

Get values of Excel Cells in C# including blank cells

I'm trying to get all Excel selected range values including blank entries. What I have worked until is,when a blank is hit and then it exits the loop. I need to recognize the blank, do something with it and then go on to the next cell in the selected range.
This is my code:
// Get active excel instance
Excel.Application xlApp = (Excel.Application)System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application");
// Get active excel workbook
string wbn = xlApp.ActiveWorkbook.Name;
Excel.Workbook xlWorkBook = (Excel.Workbook)xlApp.ActiveWorkbook;
// Get active excel worksheet
Excel.Worksheet xlWorkSheet = (Excel.Worksheet)xlWorkBook.ActiveSheet;
// Get range of values from Excel
Microsoft.Office.Interop.Excel.Range SelectedRange = xlApp.Selection as Microsoft.Office.Interop.Excel.Range;
string[] ExcelSelection = new string[1000];
int counter = 0;
foreach (object cell in xlApp.Selection.Cells) //original loop stopping when it hits a blank cell
{
try
{
ExcelSelection[counter] = ((Excel.Range)cell).Value2.ToString().Trim();
//run query to return SAP Description for Excel Active Cell Text
dataGridView1.DataSource = GetSAPDescription(ExcelSelection[counter]);
counter++;
}
catch
{
}
}
}

Background Excel Thread Not Getting Killed C#

Need to work with Excel Interop. I can successfully open and read from an excel file but while closing it, the background process for that excel does not get killed. Tried using several solutions from previous SO links, but no luck! So my ask is, how to kill the background process???
Below is the UPDATED CODE that I am currently using:
Excel.Application application = new Excel.Application();
var workbooks = application.Workbooks;
Excel.Workbook workbook = workbooks.Open(path);
Excel.Worksheet worksheet = workbook.ActiveSheet;
Excel.Range range = worksheet.UsedRange;
var rows = range.Rows;
// Some business logic
for (int row = 2; row <= rows.Count; row++)
{
//Read the data from the excel
}
// Some business logic
//close the excel
rows.Clear();
cell.Clear();
range.Clear();
workbook.Close(false);
application.Quit();
while (Marshal.FinalReleaseComObject(rows) != 0) { }
while (Marshal.FinalReleaseComObject(cell) != 0) { }
while (Marshal.FinalReleaseComObject(range) != 0) { }
while (Marshal.FinalReleaseComObject(worksheet) != 0) { }
while (Marshal.FinalReleaseComObject(workbook) != 0) { }
while (Marshal.FinalReleaseComObject(workbooks) != 0) { }
while (Marshal.FinalReleaseComObject(application) != 0) { }
rows = null;
cell = null;
range = null;
worksheet = null;
workbook = null;
workbooks = null;
application = null;
GC.Collect();
GC.WaitForPendingFinalizers();
By following the above code, I get the below exception in my debugger:
Any help on this will be appreciated.
You are using range.Rows.Count, this might violate the "Never use 2 dots with com objects." rule. See here
You could try including this ;
var rows = range.Rows
for (int row = 2; row <= rows.Count; row++)
{
//Read the data from the excel
}
rows.Clear(); //rows is itself a range object
The few times I've had to use Excel interop, I haven't had any issues when following these simple rules:
Always wrap any Excel interop in try-finally
blocks. In the finally block put all releasing logic.
Use Marshal.FinalReleaseComObject to release named COM
references as its essentially doing the ref count loop for you.
Eagerly release COM objects from deepest to
shallowest. In your case I'd start with range then worksheet
then workbook and so on.
Correctly release unreferenced COM objects (two dot rule) with GC.Collect() and GC.WaitForPendingFinalizers(). Do this before manually
releasing pending COM objects you hold a named reference to.

Categories

Resources