The problem I am having involves saving a workbook that is created in C# code on a clients machine. The published version of the program works perfectly on my machine, but not on the clients.
Overview of the Process:
When the program has completed it's iterations it will create worksheets once complete it will save the workbook and display a message box saying that the export was successful and display the workbook that was created.
The Problem:
When ran on the clients machine it will go through the process of creating the worksheets, but for some reason it will not save the file. It dose keep the instance of excel in memory, so when you open task manager you will see the process. Also after you run the program and try to turn off the computer it will prompt you to save or not save the workbook that was created since the instance is still in memory. This is how i have worked around and got to save the workbook to verify that the program did create the worksheets.
My thoughts:
I am thinking that there maybe some settings for windows or for excel that is preventing the excel file to be saved and automatically opened. I am also using background worker to accomplish the task of monitoring the progress of the workbook creation process, so this could also be a problem.
If anyone has encountered this problem or know of a solution please let me know. If more information is needed then i can provide that.
I did not include code, because i did not find it useful in knowing why the problem is happening since everything works on my development machine, but not the clients.
EDIT
I work with Jared and was finally able to debug the program on another machine with VS installed. It turns out that the problem wasn't in the save function at all but with the number of default sheets created on a client machine.
When creating the workbook, the program is supposed to go through the worksheets and delete all of them but the first one. There was a logic flaw (coded by me :/) that would leave an empty sheet in the workbook which caused the problem at this statement:
int lastRow = _excelWorksheet.Cells.Find("*", Type.Missing, Type.Missing, Type.Missing,
XlSearchOrder.xlByRows, XlSearchDirection.xlPrevious, false, Type.Missing, Type.Missing).Row;
Apparently it doesn't like searching for stuff on an empty sheet. Our development machines are currently set to default to one sheet only, so we didn't run into the problem.
What I found odd was that while debugging, the program would throw a System.NullReferenceException at that line. However, the program wouldn't crash on the client machines. Instead, it would just leave the Excel instance in memory as described and would just sit there. As Jared said, the excel stuff runs on a background thread so maybe that has something to do with it.
Related
As far as I can see, this is not a duplicate question, as the question here is about why the accepted answers of seemingly duplicate questions, do not solve my issue in what appears to be the same circumstances.
For days, I have been struggling with my application failing to end instances of Excel, which have been opened using interops.
My application opens and closes Word applications just fine (i.e. the process disappears from task manager), but not Excel apps. Excel always remains open in the task manager. After much, much searching - this seems to be the definite guide on how to close Excel instances. However, if I copy and paste that exact example code - and run it as a console app, in either .NET Framework, or .NET 6 - Excel still fails to close, exactly as per the behaviour of my application.
Presumably, this must be an issue with the latest/newer versions of Word?
Completely and utterly lost on this one...
Code from the above link that leaves Excel open:
using System;
using System.Runtime.InteropServices;
using Microsoft.Office.Interop.Excel;
namespace TestCsCom
{
class Program
{
static void Main(string[] args)
{
// NOTE: Don't call Excel objects in here...
// Debugger would keep alive until end, preventing GC cleanup
// Call a separate function that talks to Excel
DoTheWork();
// Now let the GC clean up (repeat, until no more)
do
{
GC.Collect();
GC.WaitForPendingFinalizers();
}
while (Marshal.AreComObjectsAvailableForCleanup());
}
static void DoTheWork()
{
Application app = new Application();
Workbook book = app.Workbooks.Add();
Worksheet worksheet = book.Worksheets["Sheet1"];
app.Visible = true;
for (int i = 1; i <= 10; i++) {
worksheet.Cells.Range["A" + i].Value = "Hello";
}
book.Save();
book.Close();
app.Quit();
// NOTE: No calls the Marshal.ReleaseComObject() are ever needed
}
}
}
It does seem a bit weird with the current version of Excel. It depends on how the console program is run. If I double-click in Windows Explorer, it closes perfectly like it used to. But if I run from a console window, then Excel stays open until the console window closes. Similarly when I run from the debugger, Excel only closes when the Debug window is closed and not when the driving process is terminated. It would be good if you can confirm this too.
I suspect Excel has added some defensive code to not close in specific situations - maybe watching for a 'parent' window to close, or something similar.
Aaaaand then. . . . just as I try again after posting this, Excel starts closing beautifully every time, however I run it.
OK, so I've stumbled across the solution to this. The solution is really rather an unrelated issue.
My instance of Excel had the Analysis Toolpak enabled (spelling mistake included!), from some work I was doing a few months back. The add-in is found at File --> Options --> Add-ins --> Analysis ToolPak.
I've disabled the add-in, and now Excel apps open and close just fine, using only explicit garbage collection, as expected.
I should add - the behaviour is still different to Word. Closed Word files kill Word almost instantly on closure of the document, even before the explicit GC. With Excel, I still have the close the form where the Workbook was opened from, which then calls the explicit GC, and sometimes it takes a few seconds, sometimes 30 seconds, sometimes it takes a minute - but having disabled the add-in, Excel does eventually get stopped.
I have a C# ExcelDna XLL function library that I register during startup from a VSTO add-in.
this.Application.RegisterXLL(xllPath);
When I shell execute an Excel file (Process.Start the .xlsx file) then most of the time everything works and the functions evaluate when the workbook opens.
When opening the workbook in this manner Excel reuses a currently running EXCEL.EXE process if one exists. Most of the time this is fine, but under certain conditions, for example if Excel was opened through COM and then closed, then when the Excel instance is closed, it doesn't really close, but instead shuts down all of its add-ins and unregisters all XLLs but remains alive. When process invoking into one of these zombie processes the functions, obviously, no longer evaluate.
To attempt to get around this I have tried to open Excel directly, using the .xlsx file as a command line parameter, but in this case there seems to be some kind of race condition and the workbook opens before the XLL has finished registering and the functions always evaluate as #NAME. If the cell is modified and reevaluated then the function correctly evaluates. Calling Application.CalculateFull() and all possible variations has no effect.
If I open a file via Explorer (i.e. double clicking on it) then strangely it now returns #N\A rather than #NAME but still it is the same problem.
I've even tried registering the XLL so it loads on start-up (see here) and it still doesn't work.
Has anyone else encountered this and found a reliable way to get XLL functions to evaluate when opening an Excel instance?
We've found that, if the Add-In isn't loaded properly and the functions don't calculate, performing a global search and replace (e.g. search for = replace with =) forces a recalc when the regular recalc doesn't work. You could potentially automate this with VBA Application.OnTime
I have faced a big and inconvenient problem with Excel spreadsheets that have internal data connections (queries that selects ranges of the own workbook) in them.
I wiil start by showing the problem e exposing how to reproduce it.
Problem: if I have some internal connetion and another instance (pay attention, instance, not other workbook) was already open, when refreshing that connection, the same workbook (which have the internal connection) is instantly opened in readonly mode in another window...
Why this occurs? The problem is that I'm developing a .NET automation application for Excel and this behavior breaks some process flow (because the readonly file stays open and sometimes it causes a non refresh in the original workbook)
To solve this problem I began to use the same instance if it is already open, but this is not the best solution because I need to handle the concurrency when running several VBA macros in the same instance via .NET Interop.
Here a workbook for those who wish to test this issue that I presented (just change the string connection inserting the new file path in your computer):
Excel File for Test
I recorded a video to show you the issue with more details and how to reproduce it. Please, take a look:
Video
I hope someone knows how to solve this problem because I've been searching for this several days...
Thanks
Obs.: The Office version is 2013
I'm having a really hard time finding good documentation on how to properly close and save an Excel file using the Exce; Office Interop dll.
Basically, I have everything working with regards to creating a new Excel workbook and filling the first worksheet with data from a list (when I set the visible property to true on the application object I can watch the Excel file open and fill the worksheet exactly the way I want it to.)
I just need to know how to properly close the Excel file and save it to a location of my choice. I want to make sure I kill Excel completely when the code is done -- don't want it to continue to hold memory when I'm finished.
Right now I am doing the following after filling the worksheet with the data I want:
workbook.Save();
workbook.Close(Type.Missing, Type.Missing, Type.Missing);
application.Quit();
UPDATE: I am now able to save the Excel file to the location I want. I simply removed the Save() call and changed the Close call to (true, filePath, Type.Missing) and it is now putting the file in the desired location.
Only issue to resolve is properly closing the COM object.
POSSIBLE SOLUTION At least this seems to remove Excel from the task manager every time the code finishes executing.
// Close the workbook, tell it to save and give the path.
workbook.Close(true, workbookPath, Type.Missing);
// Now quit the application.
application.Quit();
// Call the garbage collector to collect and wait for finalizers to finish.
GC.Collect();
GC.WaitForPendingFinalizers();
// Release the COM objects that have been instantiated.
Marshal.FinalReleaseComObject(workbook);
Marshal.FinalReleaseComObject(worksheet);
Marshal.FinalReleaseComObject(workrange);
Marshal.FinalReleaseComObject(application);
I believe this will work. I think the problem was that I wasn't releasing everything that had been instantiated and I think forcing the GC to collect was the final missing piece. I ran this 20 times and every time Task Manager would show Excel and then remove it once the code finished.
FINAL UPDATE: The code above is definitely disposing of all the COM objects and everything is working properly.
There is no 100% method to always under all cicumstances get rid of a COM object from .NET but appending Marshal.FinalReleaseComObject (application); application = null; after the call to Quit() increases your chances...
For MSDN reference see http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.finalreleasecomobject.aspx
Lets say I have an Excel file named "DataSheet.xls" open. How can I kill that Excel file using c#?
using Excel = Microsoft.Office.Interop.Excel;
(...)
var app = (Excel.Application)System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application");
// I have english excel, but another culture and need to use english culture to use excel calls...
Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("en-US");
app.Workbooks["DataSheet"].Close(false, false, false);
It's very likely that a single process will contain multiple open workbooks. Especially because Excel prefers to re-use an existing instance when opening files from Explorer, Outlook, etc. instead of creating a new process for each workbook.
Not to mention killing the process will require you to either 1) close the main window which will prompt if there's unsaved changes or 2) forcefully kill the process which will likely cause Excel to show its crash recovery options the next time you launch it.
The best course of action is to use the Excel COM API to close the workbooks. You can use the Marshal.GetActiveObject method to get a running instance of Excel and then refer to the Office developer reference for more information about how to close specific named workbooks without prompting.
The Process class would allow you to kill a process. You could inspect all running excel.exe processes, get the main window handle for the process, check whether the caption of that window contains the name of the XLS file and then kill the process for that window.
Otherwise you could use the Office COM classes to talk to Excel. This may also allow you to shut down open workbooks.
off hand don't have the code as such. but generally, in the past i'd have used a couple of api calls to do this. first, you'd get the window handle (hWnd) and then you'd use the sendmessage api call with the wm_close parameter.
that's about all i can remember -it's been about 10 yrs since i had to do that kinda stuff, so the field may have changed since then :)