I have developed a Shared Addin for Excel using Extensibility IDTExtensibility2 interface in Visual Studio 2008.
It's quite basic in functionality. When a workbook is opened it is stored in a list of opened workbooks, when it is closed the addin will create a new text file then write out some XML to the file, this file is then read by another process which will then deserialize the XML.
The addin works under normal operations - so if the user opens and closes a file then the addin does what it should, if they quit Excel with open workbooks then it does what it should.
The problem is when the user has Excel open with open workbooks and does a Log-Off. The two methods: OnDisconnection and OnBeginShutdown don't appear to be called, at all.
I did two things to test this:
I created a TextWriterTraceListener which wrote to a log file when these two methods were called. When Excel is quit normally they are hit and information is logged in the log file, but when a user logs off there is nothing in the log file.
Inside both of these methods using File.CreateText(filename) I created a blank file. When the user quits Excel normally these files are created, but once again, when Excel is closed through a Log-Off these files aren't created.
Does anyone have any ideas how I can get around this problem? I need to capture when Excel is being closed when the user is logging off their machine...
The solution in the end was to hook into Microsoft.Win32.SystemEvents.SessionEnding and, when this System event was fired, to manually call the OnBeginShutdown method.
This used to cause a methode ~ of object ~ failed error in VB6 days.
Try WorkbookBeforeClose or potentially ProtectedViewWindowBeforeClose.
One issue you may have with these if I remember correctly, is that you can't capture when if the event is cancelled, so if you're using this to clean up, I believe you need to also do some work in one of the activate or open events such that your addin will be useable if the user cancels the closing action....
Hope this makes sense.
I had the same problem with my Outlook 2010 addin. It may be something to do with the fact that Outlook 2010 does not signal add-ins that it is shutting down.
Specifically, Outlook [2010] no longer calls the OnBeginShutdown and OnDisconnection methods of the IDTExtensibility2 interface during fast shutdown.
Similarly, an Outlook add-in written with Microsoft Visual Studio Tools for Office no longer calls the ThisAddin_Shutdown method when Outlook is shutting down.
If you still want your addin to be notified when Outlook 2010 is shutting down (as I did), you need to latch on to the Application's ApplicationEvents_Event_Quit event, using code like mine below (your shutdown code should still run in both the OnDisconnection and OnBeginShutdown methods, in any case):
public void OnConnection(object application, Extensibility.ext_ConnectMode connectMode, object addInInst, ref System.Array custom)
{
// As this is an Outlook-only extension, we know the application object will be an Outlook application
_applicationObject = (Microsoft.Office.Interop.Outlook.Application)application;
// Make sure we're notified when Outlook 2010 is shutting down
((Microsoft.Office.Interop.Outlook.ApplicationClass)_applicationObject).ApplicationEvents_Event_Quit += new ApplicationEvents_QuitEventHandler(Connect_ApplicationEvents_Event_Quit);
}
private void Connect_ApplicationEvents_Event_Quit()
{
Array emptyCustomArray = new object[] { };
OnBeginShutdown(ref emptyCustomArray);
}
public void OnDisconnection(Extensibility.ext_DisconnectMode disconnectMode, ref System.Array custom)
{
addinShutdown();
}
public void OnBeginShutdown(ref System.Array custom)
{
addinShutdown();
}
private void addinShutdown()
{
// Code to run when addin is being unloaded, or Outlook is shutting down, goes here...
}
Related
I'm trying to open some files(between 50-400) in programmatically way by Add-in in orde to edit them.
I do this in sequential way, open one, edited, closed and so on.
Sometimes, in unexpected behavior, the OpenEx () method throws an system.accessviolationexception and the program stop, I can't clear the file that the program is trying to open and canot cancel the requst to open and move on.
I can catch the exception, but once it happens with a particular file then also trying to open all the following files throws an exception.
I would like to ask:
1. Why does this sometimes happen inconsistently?
2. Is there a way to cancel the file opening request? Does memory need / can be cleared? What do I need to do so that I can open files after it happens with some file?
This is the main code:
Globals.ThisAddIn.Application.Documents.OpenEx(currVisioFile,(int)Microsoft.Office.Interop.Visio.VisOpenSaveArgs.visOpenRW);
If opening a lot of files from the Visio add-in itself is or becomes unstable (issue I've been facing as well), I recommend opening and closing a new instance of Visio for each document you have.
A way to do that in Python for example is to install the pypiwin32 package and then run the following script:
import win32com.client
documentsToProcess = ["path/to/doc1", "path/to/doc2", "path/to/doc3"]
for path in documentsToProcess:
app = win32com.client.DispatchEx("Visio.Application") # Open new Visio instance
doc = app.Documents.Open(path) # Open your document (and hopefully blocks)
doc.Saved = true # For quiet exit
app.Quit()
You can find all official documentation about the Open function and all others objects and methods here.
Then you would change your add-in code so that it just waits for a document to be opened before processing it, like this:
private void ThisAddIn_Startup(object sender, System.EventArgs e) {
// We subscribe to the DocumentOpened/Created event
this.Application.DocumentOpened += Application_DocumentOpened;
}
private void Application_DocumentOpened(Document doc) {
// Process your document here.
// Do your work on your document here!
}
Usually the work you have to do on each document is much longer than just creating a new instance of Visio so it shouldn't be an issue.
Maybe you need to make a few changes to ensure that the Visio instance closes correctly but that's the idea. You could also force kill the process if needed when it gets stuck.
how I can programatically load and unload a VSTO add-in in Word on button click.
I have unloaded it on event click bu using below code.
foreach (Office.COMAddIn addin in Globals.ThisAddIn.Application.COMAddIns)
{
if (addin.ProgId == "DocDrafter")
{
addin.Connect = false;
return;
}
}
but on document change and document start I have to load the add-in again.
But once addin is unloaded I am unable to load it again.
You have a couple of possibilities - it depends on what, exactly, you want to do. To begin with, you should (have) read the information in the Word object model Help for the AddIns collection and the Addin object. (We're talking about Globals.ThisAddIn.Application.Addin/s for your VSTO project.)
There are basically two approaches. One is to used the Installed property of the Addin object which loads (=true)/unloads(=false) the add-in from the Word UI, leaving it in the list of Add-ins (the list in Word's File/Options/Add-ins tab) so that the user (or your code) can load it again as required. It sounds like this is what you need.
The other approach is to remove/add the add-in to/from that list. Use the Addin.Delete method to remove the add-in; use Addins.Add to add an add-in to the list.
If you disconnect the Add-in from within the VSTO project, as your code does, I don't think there's any way within the scope of VSTO that you're going to get it to connect, again...
Help topic in the documentation: start here:https://msdn.microsoft.com/en-us/vba/word-vba/articles/addins-add-method-word
So I got this problem with Excel, that seems to be specific to Excel 2010 only. When I run the file from SharePoint location, no workbook open event fires.
It's an Excel COM add-in and we use Excel.AppEvents_WorkbookOpenEventHandler, that's not being fired when the sheet is opened as it should be.
Is this a known issue and is there any way to work around it?
EDIT:
OK guys, as I stated somewhere in comments, I originally intended only to learn if this is a known issue and what options I had, but since there was a lot of negative feedback for a perfectly legit, short and to the point question, I will now provide all the info requested including my newest findings.
It is not a VSTO add-in, this is a COM add in, and yeah, I have eyes to see if the sheet has been opened in safe mode.
So, I created a small-scale add-in quickly to prove the point, registered it in my registry and tested the OnConnection method when I ran the workbook from my machine. However, when I ran it from the SharePoint server, even OnConnection method didn't get called.
Now our original add-in did get loaded, as it is a 2 part component, which is why I thought just the WorkbookOpen event didn't fire, but it turns out the Excel 2010 is run in some mode that prevents add-ins from running on start-up. Hopefully this clears things up a bit and maybe some of you will have another idea or two.
EDIT 2:
I just realized there was this one guy who downloaded sheet from the internet and thought he was replicating the issue. I was running it from SharePoint, meaning I opened it from its SharePoint location, but easiest way to replicate is this:
Suggest you convert your COM Add-in to a VSTO Add-In to future proof it. I know this is Office2010 specific though from Office2013 onwards COM Add-Ins are not fully supported - irrespective of Sharepoint.
Ignore below, its an old part of answer before context of problem was provided....
My AddIns Workbook open event is not firing when opening file
from SharePoint on Excel 2010, is this a known issue and is there any
way to work around it?
Something odd is going on in your environment or configuration because I cannot reproduce the issue using Excel 2010. Here is my VSTO AddIn code:
namespace ExcelAddIn1OpenEventFromSharepoint
{
public partial class ThisAddIn
{
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
this.Application.WorkbookOpen +=new Excel.AppEvents_WorkbookOpenEventHandler(Application_WorkbookOpen);
}
private void Application_WorkbookOpen(Microsoft.Office.Interop.Excel.Workbook Wb)
{
if (Globals.ThisAddIn.Application.ActiveWorkbook.Final)
{
System.Windows.Forms.MessageBox.Show("This is a readonly workbook");
}
else
{
System.Windows.Forms.MessageBox.Show("This is read/write workbook");
}
}
private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
{
}
//[VSTO Generated Code]
}
}
When I open a workbook from our SPS site, I see the messagebox:
Please try this same vanilla bare bones code and if the workbook open event still doesn't fire then check your Trust Center settings:
Go through all the settings:
Trusted Locations
Trusted Documents
AddIns
File Blocking
Privacy Options
Most probably the Workbook was open in the Protected View. In that case you will not get the WorkbookOpen event fired. Try to handle the ProtectedViewWindowOpen event of the Application class which is fired when a workbook is opened in a Protected View window.
Here is what MSDN states for the ProtectedView object:
A Protected View window is used to display a workbook from a potentially unsafe location. Unsafe locations are defined as the following:
Files opened from the Internet.
Attachments opened from Microsoft Outlook 2010.
Files blocked by File Block Policy.
Files that fail Office File Validation.
Files explicitly opened in Protected View by using the Open in Protected View command of the Open button in the Open dialog box.
Workbooks displayed in a Protected View window cannot be edited and are restricted from running active content such as Visual Basic for Applications macros and data connections. For more information about Protected View windows, see What is Protected View?.
To return a single ProtectedViewWindow object from the ProtectedViewWindows collection, use ProtectedViewWindows(Index), where Index is the index number of the window you want to open. You can also access the ProtectedViewWindow object that represents the active Protected View window by using the ActiveProtectedViewWindow property of the Application object.
After you access a ProtectedViewWindow object, use the Workbook property to access the Workbook object that represents the workbook file that is open in the Protected View window. Because a Protected View window is designed to protect the user from potentially malicious code, the operations you can perform by using a Workbook object returned by a ProtectedViewWindow object will be limited. Operations that are not allowed will return an error.
here is my question:
I have developed a program that uses Microsoft.Excel COM components in order to read/write over Excel files. Well, my app is doing good but when I open, for instance, another file directly with Excel while my program is running, the file(s) that my app uses appear within Excel. I do not want this. I tried also the Visibility property of Excel Application class, but that was not the solution, it just does not work.
NOTE : I have checked this question out.
Restrict access to excel file opened by C# program
Yet, it says no proper solution actually.
You can use Application.IgnoreRemoteRequests = true. This will avoid users opening excel files in the same Excel process as the one you are using.
There is one caveat though: you have to make sure that all execution paths of your application reset this property to false. This property WILL NOT reset itself when you quit and release your Excel application which means that Excel will not respond correctly to a subsequent user who double clicks on a *.xls file for example.
EDIT: Possible issues with IgnoreRemoteRequest
Ok, to make this clearer I'll detail a little bit more what issues you can run into using this feature (at least these are the only ones I've run into when I had to use this feature).
When setting IgnoreRemoteRequests = true you have to make sure you reset this property BEFORE quiting and/or releasing the COM Excel application. If you don't, Excel will not respond to DDE requests which means if someone double clicks on a *.xls file, the file will not open (Excel will start up, but it wont open the file automatically).
This however is only true if you quit the application and/or release it without reseting the property. You just have to make sure that wherever it is in your code that you are quitting/resetting you set the IgnoreRemoteRquests back to false before.
If you'r application crashes and it hasn't been able to clean up (unhandled exception) then the EXCEL process will keep running (if invisible, you will only see it in the Task Manager). That is normal as your app didnt have a chance to quit and release the internal Excel it is using. This however is not an issue. If a user ignores this "leaked" Excel process until it's eventually killed in next reboot or whatever, or manually kills it from the task bar, Excel will work perfectly fine.
Note: MS Excel 2007. Don't know about behavior of previous versions.
Have you tried running your program under a service account? This should avoid the excel com object interfering with the instance used by the logged in console user, so they will not see the effects of your com objects. It's probably also better security practice to run COM type applications under a service account instead of a user account as well, but that's for another question.
I have a C# Excel Add-in project "MyExcelAddIn" that has a public method Foo() to do something complex. For testing purposes, the add-in also defines a toolbar button which is wired to Foo() so I can test this and verify that clicking the button calls Foo() and does what I want it to do. This is fine.
Now I want to call this method from a C# Windows Forms project. In the Windows Forms project I can create an Excel instance and make it visible and verify that my VSTO add-in is running as I can see the button and it works. But I can't work out how to call Foo() programatically from the Windows Forms project. I've googled a bit and got as far as getting the "MyExcelAddIn" COMAddIn object, but can't work out how to call Foo().
It looks something like this:
// Create Excel and make it visible
Application excelApp = new Application();
excelApp.Visible = true;
// I know my VSTO add-in is running because I can see my test button
// Now get a reference to my VSTO add-in
Microsoft.Office.Core.COMAddIns comAddIns = _excelApp.COMAddIns;
object addinName = "MyExcelAddIn";
Microsoft.Office.Core.COMAddIn myAddin = comAddIns.Item(ref addinName);
// This works, but now what? How do I make a call on myAddin?
// Note that myAddin.Object is null...
So I want to know what I can do to call Foo() from my Windows Forms application. Note that I have full control over both the Windows Forms application and the add-in and I suspect I have to make changes to both of them (particularly the add-in) but I have no idea how to do this.
Note that this is a VS2008 C# application and I'm using Excel 2003.
If you're building an application-level add-in, I believe this may be your answer: MSDN VSTO Article
It involves two steps: (From the article)
In your add-in, expose an object to other solutions.
In another solution, access the object exposed by your add-in, and call members of the object.
The other solution may be: (Again from the article)
Any solution that is running in a different process than your add-in (these types of solutions are also named out-of-process clients). These include applications that automate an Office application, such as a Windows Forms or console application, and add-ins that are loaded in a different process.
I'm using the SendMessage Win32 API to do this. My C# Add-in creates a "NativeWindow" with a uniqe window title that the WinForm app can locate.
I assume that your method Foo somehow interacts with Excel. Otherwise you can just add a reference to the assembly containing the class with the Foo method and call it from there without instantiating Excel.
The only other way I can think is to get a reference to your CommandBarButton through the excelApp object. CommandBarButton has a method called Execute which is similar to clicking the button. Something like this:
Excel.Application excelApp = new Excel.Application();
CommandBarButton btn = excelApp.CommandBars.FindControl(...) as CommandBarButton;
btn.Execute();
For anyone else who finds this here's what I did:
object addInName = "AddinName";
var excelApplication = (Microsoft.Office.Interop.Excel.Application)Marshal.GetActiveObject("Excel.Application");
COMAddIn addIn = excelApplication.COMAddIns.Item(ref addInName);
addIn.Object.AddinMethodName(params);
Also had to add a reference to Microsoft.Office.Core under COM and Excel.Interop under Assemblies.