I am creating an excel application with c#.
Since I will maintain the excel file in urgency I want to keep its handler open.
I want to keep the excel process id so I will be able to kill it in case the system crashs.
How can I get the Excel Pid when creating it?
using Excel = Microsoft.Office.Interop.Excel;
using System.Runtime.InteropServices;
using System.Diagnostics;
class Sample
{
[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);
}
}
Here's an example of how to open an Excel file using Excel-Interop, and properly disposing the instance
(source: google)
Application ExcelObj = new Application();
Workbook WB = ExcelObj.Workbooks.Open(fileName,
0, true, 5, "", "", true, XlPlatform.xlWindows, "\t",
false, false, 0, true, false, false);
Sheets sheets = WB.Worksheets;
Worksheet WS = (Worksheet)sheets.get_Item(1);
Range excelRange = WS.UsedRange;
... (DO STUFF?)
// Get rid of everything - close Excel
while (Marshal.ReleaseComObject(WB) > 0) { }
WB = null;
while (Marshal.ReleaseComObject(sheets) > 0) { }
sheets = null;
while (Marshal.ReleaseComObject(WS) > 0) { }
WS = null;
while (Marshal.ReleaseComObject(excelRange) > 0) { }
excelRange = null;
GC();
ExcelObj.Quit();
while (Marshal.ReleaseComObject(ExcelObj) > 0) { }
ExcelObj = null;
GC();
public static void GC()
{
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
}
Related
My problem is that I have a program that reads data from an Excel sheet .xlsb, but when the Excel file is open, then it asks me to save. Why?
async Task<bool> ReadVariable()
{
bool succeeded = false;
while (!succeeded)
{
//open file excel using microsoft dll
Excel.Application app = new Excel.Application();
//open workbook
Workbook wk = app.Workbooks.Open(excelpath, ReadOnly : true);
//get first sheet
Worksheet sh = wk.Worksheets[1];
//get cell
// Cells[unten/rechts] Example: [1,2] = B1
var day1tag = sh.Cells[27, 2].Value.ToString();
exceltest1.Text = day1tag;
var day1früh = sh.Cells[26, 2].Value.ToString();
Day24oee24.Text = day1früh;
app.DisplayAlerts = false;
wk.Close(SaveChanges : false);
app.Quit();
await Task.Delay(15000);
//await Task.Delay(108000000);
}
return succeeded;
}
[In addition to #JohnG]
First, you should put the app.Quit(); command line out side of the while loop and then do your algorthyms, after that save your workbook with this code;
xlWorkbook.SaveAs(saveFileDialog1.FileName + ".xlsx", Excel.XlFileFormat.xlWorkbookDefault, null, null, null, null, Excel.XlSaveAsAccessMode.xlExclusive, null, null, null, null, null);
and then use
app.Quit();
In addition;
After all process zombie excel will be shown on your task manager to solve that I would like to recommend as follow;
Import;
using System.Diagnostics;
To kill zombie excel use this function;
private void KillSpecificExcelFileProcess(string excelFileName)
{
var processes = from p in Process.GetProcessesByName("EXCEL")
select p;
foreach (var process in processes)
{
if (process.MainWindowTitle == excelFileName)
process.Kill();
}
}
And call the function as follow; (Interop excels are nameless due to we should use ("").
KillSpecificExcelFileProcess("");
I am trying to read the cell value from an Excel sheet, and used Value and Value2 to see the Cell value. It keeps throwing "System.NullReferenceException: 'Object reference not set to an instance of an object.'" Error. I am unable to figure out where is the issue in code.
I know the file path and the Excel sheet it's reading is correct.
public String readEDriver()
{
int nRows = 1;
int nCols = 1;
String driverLoc = null;
Excel.Application excelApp = new Excel.Application();
if (excelApp != null)
{
Workbook excelWorkbook;
excelWorkbook = excelApp.Workbooks.Open("C:\\A\\Config.xlsx", 0, true, 5, "", "", true, Excel.XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);
Worksheet excelWorksheet;
excelWorksheet = (Excel.Worksheet)excelWorkbook.Worksheets[1];
excelWorksheet.Activate();
String eName =excelWorksheet.Name;
if (excelWorksheet == null)
{
throw new Exception(string.Format("Named worksheet ({0}) not found.", excelWorksheet));
}
else
{
var cellVal = ((Microsoft.Office.Interop.Excel.Range)excelWorksheet.Cells[nRows, nCols]).Value2;
if (excelWorksheet.Cells[nRows,nCols]!=null)
{
cellVal = ((Microsoft.Office.Interop.Excel.Range)excelWorksheet.Cells[nRows, nCols]).Value2;
driverLoc = cellVal.ToString();
}
}
excelWorkbook.Close();
excelApp.Quit();
}
return driverLoc;
}
it breaks at the
var cellVal = ((Microsoft.Office.Interop.Excel.Range)excelWorksheet.Cells[nRows, nCols]).Value2;
It fails to retrieve the Excel Range and gives NullReferenceException
Message:
System.NullReferenceException : Object reference not set to an instance of an object.
Stack Trace:
I tried your code because it seemed OK.
It works for me with a couple of minor compiling topics:
1.- Use "/" instead od "\" for the paths. Windows doesn´t like those.
2.- I had to add the Excel.Workbook and Excel.Worksheet because the compiler warned me
Apart from that it works. Below your code with my slight corrections.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Excel = Microsoft.Office.Interop.Excel;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
string aux = readEDriver();
Console.WriteLine(aux);
Console.ReadLine();
}
public static string readEDriver()
{
int nRows = 1;
int nCols = 1;
String driverLoc = null;
Excel.Application excelApp = new Excel.Application();
if (excelApp != null)
{
Excel.Workbook excelWorkbook;
excelWorkbook = excelApp.Workbooks.Open("C:/Users/Usuario/Desktop/PruebasTxtFile.xlsx", 0, true, 5, "", "", true, Excel.XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);
Excel.Worksheet excelWorksheet;
excelWorksheet = (Excel.Worksheet)excelWorkbook.Worksheets[1];
excelWorksheet.Activate();
String eName = excelWorksheet.Name;
if (excelWorksheet == null)
{
throw new Exception(string.Format("Named worksheet ({0}) not found.", excelWorksheet));
}
else
{
var cellVal = ((Microsoft.Office.Interop.Excel.Range)excelWorksheet.Cells[nRows, nCols]).Value2;
if (excelWorksheet.Cells[nRows, nCols] != null)
{
cellVal = ((Microsoft.Office.Interop.Excel.Range)excelWorksheet.Cells[nRows, nCols]).Value2;
driverLoc = cellVal.ToString();
}
}
excelWorkbook.Close();
excelApp.Quit();
}
return driverLoc;
}
}
}
Hope that helps!
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);
Hi friends,
I am beginner for using Excel. I need to figure out how to find the selected sheet name from the workbook in Excel.
Thanks a lot.
Devaraj
This is a little old. In 2004, but I used it and it helped me. What I understand is you want to call a certain excel sheet to your c# app? Anyway, check this site out, it will help if thats what your doing.
C# - Retrieve Excel Workbook Sheet Names.
You can use this solution ....
Taken from here ....using excel oledb to get sheet names in sheet order
private Dictionary<int,string> GetExcelSheetNames(string fileName)
{
Excel.Application _excel = null;
Excel.Workbook _workBook = null;
Dictionary<int,string> excelSheets = new Dictionary<int,string>();
try
{
object missing = Type.Missing;
object readOnly = true;
Excel.XlFileFormat.xlWorkbookNormal
_excel = new Excel.ApplicationClass();
_excel.Visible = false;
_workBook = _excel.Workbooks.Open(fileName, 0, readOnly, 5, missing,
missing, true, Excel.XlPlatform.xlWindows, "\\t", false, false, 0, true, true, missing);
if (_workBook != null)
{
int index = 0;
foreach (Excel.Worksheet sheet in _workBook.Sheets)
{
// Can get sheet names in order they are in workbook
excelSheets.Add(++index, sheet.Name);
}
}
}
catch (Exception e)
{
return null;
}
finally
{
if (_excel != null)
{
if (_workBook != null)
{
_workBook.Close(false, Type.Missing, Type.Missing);
}
_excel.Application.Quit();
}
_excel = null;
_workBook = null;
}
return excelSheets;
}
Application xlsxApp;
string sheetname = (xlsxApp.ActiveSheet).Name;
Here is a more up to date answer:
Excel.Worksheet activeWorksheet = ((Excel.Worksheet)Application.ActiveSheet);
string activeWorksheetName = activeWorksheet.Name;
hth
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!"