How to work around iManage limitation in Excel VSTO addin - c#

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.

Related

Does DisplayAlerts work in Word and PowerPoint when using automation?

I have an app that processes Word, PowerPoint and Excel documents. After processing each document, I call the SaveAs method (or SaveAs2). For word, I set the DisplayAlerts to wdAlertsAll. I never see the dialog asking if I want to overwrite the document. I added code to get the alsert level right before calling SaveAs2 and it is wdAlertsAll. But, I see no message box. If I run Word and open the same file and do SaveAs to the same location as my app, the dialog appears.
I have the same issue with PowerPoint where I set the level to ppAlertsAll.
I do the same thing for Excel and ... it works just fine. I am prompted before doing the SaveAs.
In all three cases my app starts the Office apps and make sure they are visible on the desktop. I was thinking that perhaps running in the background was an issue but apparently not.
There's some MS documentation about DisplayAlerts in PowerPoint here:
https://learn.microsoft.com/en-us/office/vba/api/powerpoint.application.displayalerts?f1url=%3FappId%3DDev11IDEF1%26l%3Den-US%26k%3Dk(vbapp10.chm502050)%3Bk(TargetFrameworkMoniker-Office.Version%3Dv16)%26rd%3Dtrue
The writer should be complimented for his/her vivid imagination. It's all a fantasy, probably copy/pasted from the Excel docs. DisplayAlerts is part of the PPT object model but it's never worked.
For example, try this in VBA:
Sub BeAlert()
Application.DisplayAlerts = ppAlertsNone
Debug.Print MsgBox("Testing", vbAbortRetryIgnore, "ArfArf")
Application.DisplayAlerts = ppAlertsAll
Debug.Print MsgBox("Testing", vbAbortRetryIgnore, "ArfArf")
End Sub
Both MsgBoxes appear, though the documentation suggests that only the second one will.
I'm not sure there'd be any difference between behavior in C# vs VBA, but in the latter, overwriting a file doesn't produce a message. It simply overwrites, no questions asked.
If you close a file that hasn't been saved since it was last changed, you may get a message; if you want to avoid that, set the presentation's .Saved property to True.
If you deal with open XML documents it makes sense to process documents without MS Office involved, see Welcome to the Open XML SDK 2.5 for Office for more information.
Otherwise, you can use the Application.DisplayAlerts in PowerPoint. The value of the DisplayAlerts property is not reset once a macro stops running; it is maintained throughout a session. It's not stored across sessions, so when PowerPoint begins, it is reset to ppAlertsNone.
Sub SetAlert
Application.DisplayAlerts = ppAlertsNone
End Sub

Excel XLL function linkage / race condition causing #NAME

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

Avoiding to change references to COM addins

I've used COM addins for some years and I've observed that often new versions were the cause of serious issues including crashes at Excel startup or when COM components are used.
From what I understand this is due to the internal Excel plumbing caching some information about the COM addin (inside the XLS/XLSX/XLA/XLAM file) that don't match its new versions: e.g. if a new method has been added to the API, or a new parameter has been added to a method.
This is really annoying as it forces us to remove the references in the VBA editor "Tools" -> "References" menu, close the popup, and add them back, which AFAIK forces Excel to resync with the current state of the addin.
A "simple" fix would be to add a hook on Workbook_Open, for each distributed workbook, to automatically remove, if it exists, then add each reference back.
Do you have any other/better idea ?

C# Word automation makes Windows Explorer crash

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.

Better option for hosting MS Office documents in custom app?

I am currently hosting an IE Browser control in a .NET (2.0) Form and using it to load Office files such as Excel and Word thusly:
_ieCtrl.Navigate("C:\\test.xls", False);
The hosting and loading works well except whenever I navigate to a file I am presented with a dialog that asks whether I want to save or open the file. (This is standard IE file-download behavior.) I always want to open it of course and I do not want the dialog to show.
Another issue is that when I close the window that hosts the IE control and the Office doc the document does not close and remains open on disk. This means that subsequent attempts to open the same file via my app or the native office app will fail because of the sharing violation.
Is there a programmatic way of avoiding this dialog and cleaning up resources afterward? I am asking for a programmatic answer because web research has only yielded solutions that entail modifying OS-level settings.
Bounty NOTE:
I am open to any solution to this issue that will allow me to:
Host an Excel spreadsheet inside my application
Work rather transparently (avoid usability issues like the one described above)
Avoid having to make any OS-specific changes that may affect other applications (especially icluding IE)
Is zero additional cost (no licensed 3rd party libs please) Code Project and other open source resources are OK
Not mess around with the DSO Framer ActiveX control, unless a stable version is developed/discovered
Is your intention for the user to be able to work with the Excel file in an Excel-ish way (i.e. columns, rows, formulas, etc.), possibly saving it back? If this is the case, I can't see how you can solve this problem well without relying on COM Interop with the Excel object model or by integrating third-party libraries to work with the Excel sheet. I know you said no paid solutions, but there are some feature-rich 3rd-party controls out there just for working with Excel files within applications.
I noticed in your comment to SLaks that the final product is a "dashboard of sorts". If your intention is to design a a custom dashboard application, have you considered parsing the Excel file(s) to extract the data and then presenting it in a logical manner within your application. This removes the need to directly display and work with the Excel file while still allowing you to work with the data inside that file. If you are trying to get the data outside of the file, here are two approaches among many:
You might consider using the Excel object model and COM interop to read the data from the Excel file into your application. Granted, this includes a dependency on Excel being installed, but it is a possibility. This article has some great code for getting started with reading Excel files in this way.
A better way might be to use a library that doesn't have a dependency on Excel being installed on the local system. This answer suggests using the Excel Data Reader library, available on CodePlex.
I know this answer side-steps your original answer of "hosting MS Office documents in [a] custom app," but in case what you're really interested in is the data inside those Excel files, hopefully this answer will prove helpful.
This is a horrible hack and should only be considered as a last resort: SendKeys.Send("{O}");
http://msdn.microsoft.com/en-us/library/system.windows.forms.sendkeys%28VS.71%29.aspx
Something similar to
_ieCtrl.Navigate("C:\\test.xls", False);
(code to sleep or wait may be needed here)
SendKeys.Send("{O}");
Basically, you send the "o" key to the dialog so it presses the "open" option. You are simulating a keyboard presses to click the "open" button. It is hackey because
1) you may need to wait in between
calls. If you send the o key before
the dialog is up it will be missed.
Hopefully the navigate call is finished when the dialog pops (dont know behavior of control in c#). You may need to experiment with the time since different computers will open faster\slower
2) If the dialog is not shown on a
computer, you will be inserting "o"s
into it. This may cause problems when
exiting because it may popup another dialog to try and save
the changes. May be able to prevent this by opening it in read-only mode
3) Different versions or windows may need different sendkeys commands. For example, you may need to send "o" and them the "{enter}" key
4) Probably more :)
If you want to open the file in a separate Excel instance (not embedded in the WebBrowser control), you can simply call
Process.Start(#"C:\Test.xls");
Office was never meant to run in embedded mode, not in a web page or in an ActiveX Document host. Microsoft had time and time again given us the warning. From pulling dsoframer from the knowledge base to skipping the BrowserFlags registry key in Office 2007.
Move to Office add-ins, Excel Web Access or Office Web Apps as quickly as you can.

Categories