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
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.
We have developed a VSTO addin for Excel, which pulls excel sheets from a web server, and allows users to manipulate the data on the sheet. It works with a local copy of the file but we don't really care about that copy. But some of our clients have "iManage Integration for Office" installed as well, and these clients experience odd behaviour. In this environment we are not able to cancel the Close event.
More specifically, if a user opens one of our files and makes changes, then closes the file, our event handler fires and prompts them to save changes (to the data on the server). If they choose Cancel, or if they choose Save and the save fails for some reason, we set Cancel = true in an attempt to keep the file open. Ordinarily this works perfectly.
For those clients with iManage, the file closes anyway. If our code has saved the local copy of the file, then the file just closes. If the local copy has not been saved, the user gets another prompt after our prompt, from iManage, asking if they want to save the file. From here, the user could click Cancel and the file remains open. But the users are reporting that it's confusing to see the second prompt after clicking Cancel on the first. And they are not willing to disable the iManage addin. We would like to be able to keep the file open in this case, preferably without seeing the iManage prompt.
Working in a test environment a client set up for me (Excel 2010), I have tried a few things:
I tried setting Cancel on the workbook level BeforeClose and the applicationlevel WorkbookBeforeClose; neither one worked (file still closes whenever iManage is enabled)
I tried to make sure my handler for WorkbookBeforeClose is registered last, by registering it in the workbook level event handler; no change
I can use the iManage ribbon to manually switch to Local Mode, which makes everything work, but I don't know whether/how I can make that change through code.
I can find the iManage addin in Globals.ThisAddIn.Application.COMAddIns; I tried setting its Connect = false, but this gives an error to the effect that only an administrator can connect/disconnect the addin.
I can save the local file during the Closing event, then do SaveAs to create a second copy; the second copy is now active and iManage closes it; then I reopen the original local file. It looks pretty good to the user, but then I have a bunch of COM references to cells on the old file, and these are all garbage. I can probably loop through and serialize them and recreate them with the new file, but it will be time consuming to code and to run, so I'm looking for other ideas first.
Is there any way I can keep the file open, without making the users do anything extra?
UPDATE
Using iManage 9.3.0.0 and Excel 2010, I created a VBA macro with a reference to Worksite Integration Interfaces Library(Ex). The event fires, but the file still closes. If I don't set the Saved property, and I type on the grid, I always get a prompt from iManage asking if I want to save.
Private WithEvents oWS As iManageExtensibility
Private Sub oWS_DocumentBeforeClose2(ByVal Doc As Variant, IgnoreIManageClose As Boolean, Cancel As Boolean)
IgnoreIManageClose = True
Cancel = True
End Sub
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Set oWS = Application.COMAddIns("WorkSiteOffice2007Addins.Connect").Object
Cancel = True
ActiveWorkbook.Saved = True
End Sub
You do not need to disable the iManage integration add-in for Office. Your application should detect the presence of the iManage integration add-in and then handle that add-in's own equivalent Close event
To do that, first get an instance of the iManage add-in by examining the Excel Application.COMAddins collection and fetching back the COM add-in instance with a ProgID of either WorkSiteOffice2003Addins.Connect (Excel 2003 or earlier) or WorkSiteOffice2007Addins.Connect (Excel 2007 or later).
Actually there should also be a so-called "backwards compatibility" add-in installed also registered with a ProgID of oUR02k.Connect which you may reference instead. Again, whether that is installed or not depends somewhat on the version of FileSite/DeskSite that is installed on the machine.
That all may seem confusing to you however it's because the add-ins have changed over the years, so it rather depends on how old the iManage client is (i.e. FileSite, DeskSite) as well as the version of Office you're targeting. You may need to compensate for different Excel/iManage client versions in your code
Once you have the right COM add-in reference, examine the COMAddin.Object property. This value represents an instance of the iManage Office integration add-in
From there you can cast that object to the strongly typed COM interface of iManageExtensibility
You're then able to hook into all the Office application events that iManage have hijacked (much like you have done in your application) and respond to those events rather than the native Excel ones.
In your case you will need to monitor the DocumentBeforeClose2 event. Note the character '2' at the end. There is also a legacy event named DocumentBeforeClose but the newer DocumentBeforeClose2 has the following method signature:
DocumentBeforeClose2(object doc, ref bool ignoreimanageclose, ref bool cancel)
Finally, in your DocumentBeforeClose2 event handler add your business logic for cancelling the close event and/or the iManage close event. You do that by setting the ignoreimanageclose and/or cancel Booleans to true/false as appropriate. The doc parameter in this case will be the Excel workbook instance, so you may safely cast it to the Excel.Workbook interface if you wish
PS: if you are developing against the iManage APIs and require support you should consider purchasing the iManage SDK. That SDK contains help on various APIs, code samples, and crucially I believe it gives you access to some dev support.
This question already has answers here:
How to inject VBA code into Excel .xlsm without using Interop?
(2 answers)
Closed 7 years ago.
Scope:
I am generating automated reports using excel, but since we have restrictions on our server, we can't install Excel.Interop or use any COM object to perform such action.
We have been using EPPLUS as our main helper on this task but it has some serious restrictions when it comes to more intricated things such as PivotCharts and Macros.
We have just finished the project, missing only the PivotCharts since EPPLUS has no support for them.
Question:
How can we :
A) Write Macros to a .xlsm file? (We could write one named "Auto_Open" so that excel would run it uppon opening, creating the charts and stuff)
OR
B) Run a macro within our code to generate those charts after putting all the needed data there?
Not using Excel.Interop is a must at this moment. We are open for some suggestions such as
SpreadsheetGear and EasyXLS, but we can't find any sample or piece of code that actually shows how to Write or Run macros.
Thanks in advance
I realise this question is a bit old, but in the interest of helping future visitors I'll add that the current version of EPPlus (4.0.4 at time of writing) supports the inclusion of VBA.
Where excelDoc is your ExcelPackage, you can drop it in using something along the lines of:
excelDoc.Workbook.CreateVBAProject();
StringBuilder vbaCode = new StringBuilder();
vbaCode.AppendLine("Private Sub Workbook_Open()");
vbaCode.AppendLine(" Application.DisplayFormulaBar = False");
vbaCode.AppendLine("End Sub");
excelDoc.Workbook.CodeModule.Code = vbaCode.ToString();
Hope this helps.
Open Excel
Press Alt+F11
You will now be in the VBA IDE.
To run something on Auto Open:
Select ThisWorkbook From the VBAProject in Project Explorer on the Left of your screen
There are Two drop downs near the top of the window one will say (General) and The other (Declarations) click on (General) and select workbook
Excel should automatically bring you to the Open declaration.
Anything written in this sub will execute on open.
Rather than attempting to programatically write macros to an xlsm file, why don't you create a template that already contains the macros you want. Then generate your reports from this template.
When a user opens the report (and allows macros to run), then your macro will run and do the manipultations you want (your option A). Your Auto_Open macro could check some suitable condition (e.g. presence of a value in a specific location, perhaps on a VeryHidden sheet so the user can't easily interfere with it) before doing any work, so that it doesn't run when you open an "empty" template.
As for your option B (run the macro before providing the report to the user): this isn't feasible without Excel Automation.
I have written a C# program which import a product list from a .xlsx file and let the user create an order based on that product list.
When the user is finished, the program builds one or more system specifications based on the order.These specifications is written to a .docx file. I have Office 2007 installed on the computer and are using the Microsoft.Office.Interop.Excel and the Microsoft.Office.Interop.Word namespaces.
The problem:
After I have runned the program, Windows Explorer crashes very often and has to restart. This happens when navigating in folders or when right- clicking on folders etc.
This also happens after the program have been closed and the only solution to make it stop is to restart the computer. It seems like it only happens when I have created the output files (.docx). If i start the program and use it like I normally do, but without creating the word files, the problem don't seem to occur.
After the program have created the output files, Word gets "Visible" to the user for manual editing. The user closes the word application when finished editing the documents.
What can make the Windows Explorer crash when running word automation?
I really need help on this one. Any suggestions are welcome.
After execution, do you have ghost excel.exe and word.exe processes remaining?
These ghost are likely to make the system unstable.
You're likely not releasing properly the COM objects you instantiated via automation.
Use Marshal.ReleaseComObject(yourobj); on each and every COM objects you instantiate. It's a real pain, I know.
Note: be sure that you don't instantiate COM objects without knowing it:
mySheet = myExcelObject.workbooks[0].Sheet[0] won't just instantiate a sheet object, but also a workbook object.
Rule of thumb: never ever use a secondary property on a COM object ( foo.bar.baz ) and release everything.
Final note: don't use office automation at all on the server, it's bad, per Microsoft own words, there are fully managed libraries for that.
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 :)