How to get Excel.Application instance in C#? - c#

I try to get Excel application in my code with this method:
Excel.Application xlApp = GetApplication();
if (xlApp == null) xlApp = new Excel.Application();
where
private Excel.Application GetApplication()
{
Excel.Application result = null;
try
{
result = (Excel.Application)System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application");
}
catch (System.Runtime.InteropServices.COMException ex)
{
//Excel is not open
}
return result;
}
but the
System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application")
always throws exception, even when Excel application is open.
Exception: HRESULT: 0x800401E3
StackTrace:
in System.Runtime.InteropServices.Marshal.GetActiveObject(Guid& rclsid, IntPtr reserved, Object& ppunk)
in System.Runtime.InteropServices.Marshal.GetActiveObject(String progID)
in RaceToolTests.UnitTest1.GetApplication() in C:\Users\...

Please try following code:
//import
using Excel = Microsoft.Office.Interop.Excel;
//define
Excel.Application xlApp;
Excel.Workbook xlWorkBook;
Excel.Worksheet xlWorkSheet;
private void openXL()
{
string fileName = "PATH\\FILE_NAME.xlsx"; ;
xlApp = new Excel.Application();
xlWorkBook = xlApp.Workbooks.Open(fileName);
xlWorkSheet = (Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1);
}

Yes it is possible to not create a new instance of Excel. Use below to get existing instance of Excel app.
var path = #"C:\Temp";
var excelFileName = $#"{path}\Sample.xlsx";
var excelApp = System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application") as Excel.Application;
// Make the object visible.
excelApp.Visible = true;
// open workbook
Excel.Workbook xlWorkbook = excelApp.Workbooks.Open(excelFileName);

Related

Unable to write response in Excel using ExcelDNA in C#

I'm unable to write my REST API response in Excel using ExcelDNA in C#. It is working fine with reading values from specific column.
xlCell[i, 1] = "TEST"; // fails with System.Runtime.InteropServices.COMException: 'Exception from HRESULT: 0x800A03EC'
currentSheet.Cells[i, 2].Value = "TEST"; // same issue
Excel reading code --
Excel.Range xlCell;
Excel.Application xlapp = (Excel.Application)ExcelDnaUtil.Application;
xlapp.Visible = true;
Worksheet currentSheet;
xlCell = xlapp.ActiveCell;
Excel.Workbook wbook = xlapp.ActiveWorkbook;
currentSheet = wbook.ActiveSheet;
Please note that same code piece is working fine with VSTO addin project (through ribbon button event click) but not in .net class library (for UDF).
Please can someone help.
It is basically a bad idea to modify the worksheet values from within a cell-function. When a cell-function is executed, Excel is typically busy. Thus an attempt to modify a certain cell value may fail.
Your code is correct. The only thing you are missing is the IsMacroType=true property of the ExcelFunction attribute. The attribute changes the way the function interacts with the worksheet. For more details, see here. The example of the code is below.
[ExcelFunction(IsMacroType = true)]
public static int TestWrite1()
{
Excel.Range xlCell;
Excel.Application xlapp = (Excel.Application)ExcelDnaUtil.Application;
xlapp.Visible = true;
Worksheet currentSheet;
xlCell = xlapp.ActiveCell;
Excel.Workbook wbook = xlapp.ActiveWorkbook;
currentSheet = wbook.ActiveSheet;
currentSheet.Cells[1, 1].Value = "Hello, world";
return 0;
}
Alternatively, you could manually queue your code for execution using the function ExcelAsyncUtil.QueueAsMacro. It will run your delegate safely when Excel is ready. See here for more details on that function. The example is below.
[ExcelFunction]
public static int TestWrite2()
{
ExcelAsyncUtil.QueueAsMacro(new ExcelAction(() => {
Excel.Range xlCell;
Excel.Application xlapp = (Excel.Application)ExcelDnaUtil.Application;
xlapp.Visible = true;
Worksheet currentSheet;
xlCell = xlapp.ActiveCell;
Excel.Workbook wbook = xlapp.ActiveWorkbook;
currentSheet = wbook.ActiveSheet;
currentSheet.Cells[1, 1].Value = "Hello, world";
}));
return 0;
}
Finally, if your aim is to return some value from the cell-funtion to the active cell, you could just return the value normally.
[ExcelFunction]
public static string TestWrite3()
{
Excel.Range xlCell;
Excel.Application xlapp = (Excel.Application)ExcelDnaUtil.Application;
xlapp.Visible = true;
Worksheet currentSheet;
xlCell = xlapp.ActiveCell;
Excel.Workbook wbook = xlapp.ActiveWorkbook;
currentSheet = wbook.ActiveSheet;
/* Insert to the current cell where the cell-function is being executed. */
return "Hello, world";
}
The complete code is below:
using ExcelDna.Integration;
using Microsoft.Office.Interop.Excel;
using Excel = Microsoft.Office.Interop.Excel;
namespace ClassLibraryExcelDna
{
public class UDF
{
[ExcelFunction(IsMacroType = true)]
public static int TestWrite1()
{
Excel.Range xlCell;
Excel.Application xlapp = (Excel.Application)ExcelDnaUtil.Application;
xlapp.Visible = true;
Worksheet currentSheet;
xlCell = xlapp.ActiveCell;
Excel.Workbook wbook = xlapp.ActiveWorkbook;
currentSheet = wbook.ActiveSheet;
currentSheet.Cells[1, 1].Value = "Hello, world";
return 0;
}
[ExcelFunction]
public static int TestWrite2()
{
ExcelAsyncUtil.QueueAsMacro(new ExcelAction(() => {
Excel.Range xlCell;
Excel.Application xlapp = (Excel.Application)ExcelDnaUtil.Application;
xlapp.Visible = true;
Worksheet currentSheet;
xlCell = xlapp.ActiveCell;
Excel.Workbook wbook = xlapp.ActiveWorkbook;
currentSheet = wbook.ActiveSheet;
currentSheet.Cells[1, 1].Value = "Hello, world";
}));
return 0;
}
[ExcelFunction]
public static string TestWrite3()
{
Excel.Range xlCell;
Excel.Application xlapp = (Excel.Application)ExcelDnaUtil.Application;
xlapp.Visible = true;
Worksheet currentSheet;
xlCell = xlapp.ActiveCell;
Excel.Workbook wbook = xlapp.ActiveWorkbook;
currentSheet = wbook.ActiveSheet;
/* Insert to the current cell where the cell-function is being executed. */
return "Hello, world";
}
}
}

How to open, read and close excel interop processes (C#)

I have to do a program which reads an excel xlsx file and stores the data in to the database. This my actual simple code, I don't even take the values from the excel file, but I'm not able to kill the process. I've googled a lot and I've tried many things, but the excel process stays alive.
public void readFile(path)
{
try
{
Microsoft.Office.Interop.Excel.Application xlApp = null;
Microsoft.Office.Interop.Excel.Workbooks workbooks = null;
Microsoft.Office.Interop.Excel.Workbook xlWorkbook = null;
Microsoft.Office.Interop.Excel.Worksheet xlWorksheet = null;
Microsoft.Office.Interop.Excel.Range xlRange = null;
xlApp = new Microsoft.Office.Interop.Excel.Application();
workbooks = xlApp.Workbooks;
xlWorkbook = workbooks.Open(path);
xlWorksheet = xlWorkbook.Sheets[1];
xlRange = xlWorksheet.UsedRange;
//------Here is where I will read the data
xlWorkbook.Close();
workbooks.Close();
xlApp.Quit();
Marshal.FinalReleaseComObject(xlRange);
Marshal.FinalReleaseComObject(xlWorksheet);
Marshal.FinalReleaseComObject(xlWorkbook);
Marshal.FinalReleaseComObject(workbooks);
Marshal.FinalReleaseComObject(xlApp);
xlRange = null;
xlWorksheet = null;
xlWorkbook = null;
workbooks = null;
xlApp = null;
}
catch (Exception e)
{
}
}
And the process stays alive. I hope you can help me.
I've tried many things... The code below seems to work.
public void readFile(path)
{
try
{
Microsoft.Office.Interop.Excel.Application xlApp = null;
Microsoft.Office.Interop.Excel.Workbooks workbooks = null;
Microsoft.Office.Interop.Excel.Workbook xlWorkbook = null;
Microsoft.Office.Interop.Excel.Sheets xlsheets = null;
Microsoft.Office.Interop.Excel.Worksheet xlWorksheet = null;
Microsoft.Office.Interop.Excel.Range xlRange = null;
xlApp = new Microsoft.Office.Interop.Excel.Application();
workbooks = xlApp.Workbooks;
xlWorkbook = workbooks.Open(path);
xlsheets = xlWorkbook.Sheets;
xlWorksheet = xlsheets[1];
xlRange = xlWorksheet.UsedRange;
//--------------------------------------------------------------------
xlWorkbook.Close();
workbooks.Close();
xlApp.Quit();
Marshal.FinalReleaseComObject(xlRangeColumns);
Marshal.FinalReleaseComObject(xlRangeRows);
Marshal.FinalReleaseComObject(xlRange);
Marshal.FinalReleaseComObject(xlWorksheet);
Marshal.FinalReleaseComObject(xlsheets);
Marshal.FinalReleaseComObject(xlWorkbook);
Marshal.FinalReleaseComObject(workbooks);
Marshal.FinalReleaseComObject(xlApp);
xlRangeColumns = null;
xlRangeRows = null;
xlRange = null;
xlWorksheet = null;
xlsheets = null;
xlWorkbook = null;
workbooks = null;
xlApp = null;
}
catch (Exception e)
{
}
}
There are very little differences. A month ago I would tell you that both codes are the same. Can someone tell me why this code works, while the first code doesn't?
Try putting this at the end of your code and it will kill your excel
foreach (var process in Process.GetProcessesByName("myExcelFilename"))
{
process.Kill();
}
You need either to put the objects into using blocks, or put the closing statements into a finally.
Whenever your application throws an exception, it will leave the interop open. What you should do instead is put your shutdown code in a finally block, which means that code will always be run. Like so:
public void readFile(string path)
{
Microsoft.Office.Interop.Excel.Application xlApp = null;
Microsoft.Office.Interop.Excel.Workbooks workbooks = null;
Microsoft.Office.Interop.Excel.Workbook xlWorkbook = null;
Microsoft.Office.Interop.Excel.Worksheet xlWorksheet = null;
Microsoft.Office.Interop.Excel.Range xlRange = null;
try
{
xlApp = new Microsoft.Office.Interop.Excel.Application();
workbooks = xlApp.Workbooks;
xlWorkbook = workbooks.Open(path);
xlWorksheet = xlWorkbook.Sheets[1];
xlRange = xlWorksheet.UsedRange;
//------Here is where I will read the data
}
catch (Exception e)
{
}
finally
{
xlWorkbook?.Close();
workbooks?.Close();
xlApp?.Quit();
Marshal.FinalReleaseComObject(xlRange);
Marshal.FinalReleaseComObject(xlWorksheet);
Marshal.FinalReleaseComObject(xlWorkbook);
Marshal.FinalReleaseComObject(workbooks);
Marshal.FinalReleaseComObject(xlApp);
xlRange = null;
xlWorksheet = null;
xlWorkbook = null;
workbooks = null;
xlApp = null;
}
}
Another option is not to screw around with interop and use another library, such as EPPlus (available on NuGet), to handle the reading of excel files, which would close when the program closes.

xlApp.ActiveWorkbook returns null

I have a 2010 Excel ribbon add-in. When you press a button it simply writes some lines of data into the excel app. In order to write this data I need to get the active worksheet. And in order to get the active worksheet I need the activeWorkBook. I can get the xlApp fine with
xlApp = (Excel.Application)System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application");
And it retrieves the xlApp fine But whenever I try to get the active workbook it returns null. No matter what. Unless I restart my computer. Then it works, but only the first time. Below is the whole block of code in context. I'm wondering how to fix the xlApp.ActiveWorkBook being null. So that I can get the active worksheet. And then write data to the active worksheet.
public void sendData()
{
Excel.Application xlApp = null;
Excel.Workbook xlWorkBook = null;
Excel.Workbooks xlWorkBooks = null;
Excel.Worksheet xlWorkSheet = null;
object misValue = System.Reflection.Missing.Value;
try
{
xlApp = new Excel.Application();
//xlWorkBooks = xlApp.Workbooks;
//xlWorkBook = xlWorkBooks.Add(Properties.Settings.Default.FileToSend);
//xlWorkSheet = xlWorkBook.Sheets[1];
xlApp = (Excel.Application)System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application");
xlWorkBook = (Excel.Workbook)xlApp.ActiveWorkbook;
xlWorkSheet = (Excel.Worksheet)xlWorkBook.ActiveSheet;
}
catch (Exception ex)
{
// handle error...
}
finally
{
if (xlWorkSheet != null)
Marshal.ReleaseComObject(xlWorkSheet);
if (xlWorkBook != null)
Marshal.ReleaseComObject(xlWorkBook);
if (xlWorkBooks != null)
Marshal.ReleaseComObject(xlWorkBooks);
if (xlApp != null)
Marshal.ReleaseComObject(xlApp);
}
Thanks in advance!
You're probably getting hold of the wrong Excel instance with your call to GetActiveObject. Use the Application reference passed into your COM add-in's OnConnection (or however your ribbon code is bootstrapped).
If you're using the Excel-DNA framework to make your managed Excel add-in, then you get hold of the right Application object with a call to ExcelDnaUtil.Application.

Excel process still runs in background

I'm reading some data from a sheet of an excel file using interop library. I get the data just fine by using Microsoft.Office.Interop.Excel and then reading all data in the sheet.
Application ExcelApp = new Excel.Application();
Workbook excelWorkbook = ExcelApp2.Workbooks.Open(excelFileNamePath);
_Worksheet excelWorksheet = excelWorkbook.Sheets[excelSheetName];
Range excelRange = excelWorksheet.UsedRange;
//...for loops for reading data
ExcelApp.Quit();
But after my application terminates, I tried to edit my excel file and had some problems while opening. Apparently the excel process keeps on running in the background even though I called ExcelApp.Quit(). Is there a way to properly close the excel file that the application uses? I'm new to c# so i'm probably missing something here.
Edit: Solved! Apparently there's a rule for com objects that discourages using 2 dots.
Excel.Application ExcelApp2 = new Excel.Application();
Excel.Workbooks excelWorkbooks = ExcelApp2.Workbooks;
Excel.Workbook excelWorkbook = excelWorkbooks.Open(excelFileName);
Excel._Worksheet excelWorksheet = excelWorkbook.Sheets[excelSheetName];
//...
excelWorkbook.Close();
Marshal.ReleaseComObject(excelWorkbook);
Marshal.ReleaseComObject(excelWorksheet);
Marshal.ReleaseComObject(excelRange);
ExcelApp2.Quit();
There was another similar question - and answer (https://stackoverflow.com/a/17367570/3063884), in which the solution was to avoid using double-dot notation. Instead, define variables for each object used along the way, and individually use Marshal.ReleaseComObject on each one.
Copying straight from the linked solution:
var workbook = excel.Workbooks.Open(/*params*/)
---> instead use -->
var workbooks = excel.Workbooks;
var workbook = workbooks.Open(/*params*/)
Then, when done, release each COM object:
Marshal.ReleaseComObject(workbook);
Marshal.ReleaseComObject(workbooks);
Marshal.ReleaseComObject(excel);
Long time no answer but what worked for me was to call the GC.Collect(); from the caller,
i.e. instead of
main()
{
...
doexcelstuff();
...
}
void doexcelstuff()
{
Excel.Application ExApp2 = new Excel.Application();
Excel.Workbook excelWb = ExApp2 .Workbooks.Open(excelFName);
Excel._Worksheet excelWorksheet = excelWb.Sheets[excelSName];
//...
excelWb.Close();
ExApp2.Quit();
Marshal.ReleaseComObject(excelWb);
Marshal.ReleaseComObject(excelWorksheet);
Marshal.ReleaseComObject(ExApp2);
excelWb = null;
excelWorksheet= null;
ExApp2= null;
GC.Collect();
}
Using above Excel does not die
but a very small change, to where the GC is called from
main()
{
...
doexcelstuff();
GC.Collect(); // <<-- moved the GC to here (the caller)
...
}
void doexcelstuff()
{
Excel.Application ExApp2 = new Excel.Application();
Excel.Workbook excelWb = ExApp2 .Workbooks.Open(excelFName);
Excel._Worksheet excelWorksheet = excelWb.Sheets[excelSName];
//...
excelWb.Close();
ExApp2.Quit();
Marshal.ReleaseComObject(excelWb);
Marshal.ReleaseComObject(excelWorksheet);
Marshal.ReleaseComObject(ExApp2);
excelWb = null;
excelWorksheet= null;
ExApp2= null;
// removed the GC from here
}
My guess is the garbage collector needs to also quietly clean up internally created temp values (including refs/pointers) from the heap - some of which I guess in this case are pointing to COM objects.
(Just takes a smidgen of understanding of how machines work underneath the source code.)
You are not closing your workbook. Close it and then release it before quitting the Excel application:
excelWorkbook.close();
Marshal.ReleaseComObject(excelWorkbook);
ExcelApp.Quit();
For me this worked!
//Initializing
Application excelApp = new Application();
Workbook excelWorkbook = excelApp.Workbooks.Open(pathExcelFile, 0, true, 5, "", "",
true, XlPlatform.xlWindows, "\t", false, false, 0,
true, 1, 0);
Worksheet excelWorksheet = (Worksheet)excelWorkbook.Sheets[1];
//closing
excelWorksheet = null;
excelWorkbook.Close();
excelWorkbook = null;
excelApp.Quit();
excelApp = null;
Interop is notoriously buggy... I use the following method after saving my workbook, and no longer have issues with Excel remaining open when I exit my applications:
while (Marshal.ReleaseComObject(wb) > 0);
while (Marshal.ReleaseComObject(xl) > 0);
wb = null;
xl = null;
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
wb is my workbook object, and xl is my Excel.Application object.
For me this worked :
//opening
var excelFile = new Application();
Workbook workBook1 = excelFile.Workbooks.Open(goldenCopy_path);
Workbook workBook2 = excelFile.Workbooks.Open(new_path);
Worksheet goldWorkSheet, newWorkSheet;
goldWorkSheet = workBook1.Worksheets[1];
newWorkSheet = workBook2.Worksheets[1];
//closing
//it will release the workbook from visual studio
goldWorkSheet = null;
newWorkSheet = null;
workBook1.Close();
workBook2.Close();
workBook1 = null;
workBook2 = null;
excelFile.Quit();
excelFile = null;
This code worked on me.
Excel.Application excelApp = null;
Excel.Workbooks excelWorkbooks = null;
Excel.Workbook excelWorkbook = null;
Excel._Worksheet xlWorkSheet = null;
Excel.Range range = null;
excelApp = new Excel.Application();
excelWorkbooks = excelApp.Workbooks;
excelWorkbook = excelWorkbooks.Open(excelName);
xlWorkSheet = (Excel.Worksheet)excelWorkbook.ActiveSheet;
range = xlWorkSheet.Range["C3"] ;
range.Value = "Update Data";
Marshal.ReleaseComObject(range);
xlWorkSheet.SaveAs(path);
Marshal.ReleaseComObject(xlWorkSheet);
excelWorkbook.Close();
Marshal.ReleaseComObject(excelWorkbook);
excelWorkbooks.Close();
Marshal.ReleaseComObject(excelWorkbooks);
excelApp.Quit();
Marshal.ReleaseComObject(excelApp);

C# and Excel advanced filter

I'm trying to filter data from one workbook to another obne, but I get an unpsecified exception.
I can't find many examples for adavnced filtering in excel using c# and I don't know Where's exactly my error.
Here's my code:
public void excel()
{
Excel.Application xlApp;
Excel.Workbook xlWorkBookImport;
Excel.Workbook xlWorkBookOriginal;
Excel.Worksheet xlWorkSheetImport;
Excel.Worksheet xlWorkSheetOriginal;
xlApp = new Excel.ApplicationClass();
xlWorkBookImport = xlApp.Workbooks.Open("c:/open.xls");
xlWorkBookOriginal = xlApp.Workbooks.Open("c:/open1.xls");
xlWorkBookOriginal.Activate();
xlWorkSheetOriginal = (Excel.Worksheet)xlWorkBookOriginal.Worksheets.get_Item(1);
xlWorkSheetOriginal.get_Range("1:7").Delete();
xlWorkBookImport.Activate();
xlWorkSheetImport = (Excel.Worksheet)xlWorkBookImport.Worksheets.get_Item(1);
xlWorkSheetOriginal.Cells.AdvancedFilter(Excel.XlFilterAction.xlFilterCopy, Type.Missing, CopyToRange: "a1:a6", Unique: true);
xlApp.Visible = true;
}
Anyone can help me with a solution?
I got it figured out myeslf in the end:
public void excel()
{
Excel.Application xlApp;
Excel.Workbook xlWorkBookImport;
Excel.Workbook xlWorkBookOriginal;
Excel.Worksheet xlWorkSheetImport;
Excel.Worksheet xlWorkSheetOriginal;
xlApp = new Excel.ApplicationClass();
xlWorkBookImport = xlApp.Workbooks.Open("c:/open.xls");
xlWorkBookOriginal = xlApp.Workbooks.Open("c:/open1.xls");
xlWorkBookOriginal.Activate();
xlWorkSheetOriginal = (Excel.Worksheet)xlWorkBookOriginal.Worksheets.get_Item(1);
xlWorkSheetOriginal.get_Range("1:7").Delete();
// added code
Excel.Range range = xlWorkSheetOriginal.get_Range("a1:v53");
//
xlWorkBookImport.Activate();
xlWorkSheetImport = (Excel.Worksheet)xlWorkBookImport.Worksheets.get_Item(1);
// added code
xlWorkSheetImport.Activate();
Excel.Range target = xlWorkSheetImport.get_Range("a1:f1");
//
// modified code
xlWorkSheetOriginal.Cells.AdvancedFilter(Action : Excel.XlFilterAction.xlFilterCopy,
CriteriaRange: Type.Missing, CopyToRange: target, Unique: false);
//
xlApp.Visible = true;
}

Categories