I have developed a Microsoft Outlook Add-in for reporting suspicious emails to our IT Sec team. The Add-in basically consists of a button which is shown in the "Outlook Mail Explorer Ribbon", the "Outlook Mail Read Ribbon" and the context menu which opens up when right-clicking any email. The Add-in simply forwards any selected email to our IT Sec's mailbox for spam/phishing/...
The Add-in itself performs as expected, I only have trouble with the loading times (whenever I start Outlook, not only when first starting after installation).
I have taken a lot of information regarding Ribbon Designer VS XML from this SO answer (thanks bud!). I have decided to go for the Ribbon XML because of the ability to manipulate the context menu and the easy and clean way to design it. In addition, I created a Windows installer for installing the Add-in system-wide (see information below).
When developing this Add-in on my private PC, I had "normal" loading times and no problems with the Add-in. However, with the notebook of my company I am experiencing very high loading times. Outlook itself states that the average delay caused by the Add-in is around 2 seconds. The Add-in does not get disabled because of a GPO which places the Add-in on the "DoNotDisableAddinList" as described here.
Nevertheless, I do want to boost the performance. In the end, you can even see at the Outlook loading screen that Outlook "gets stuck" for those 2 seconds when loading the Add-in. This is not a solution for me.
What I have done so far
Basically, I tried a lot of stuff to boost the performance. First, I was having a look at this article of Microsoft which is about how to boost the performance of Office Add-ins. Unfortunately, I found none of these hints applicable for my case or I have already realized them (correct me if I am wrong):
Load VSTO Add-ins on demand: I want the buttons to be available as soon as Outlook starts, so that's not an option for me I guess
Publish Office solutions by using Windows Installer: Already done as described in this article of Microsoft
Bypass Ribbon reflection: Tried to do so but in my eyes not possible when using Ribbon XML
Perform expensive operations in a separate execution thread: Not applicable, there are no "expensive operations", simply the Add-in buttons should be loaded
What I could find out
I then tried to identify any "expensive" operation and therefore started my application in debug mode with a breakpoint at
protected override Microsoft.Office.Core.IRibbonExtensibility CreateRibbonExtensibilityObject()
{
return new Ribbon();
}
After stepping over the first breakpoint (2 x F10), the Outlook loading screen shortly shows up and then Visual Studio shows up again with the next operation which is the GetCustomUI-method of my Ribbon.cs. This one loads the XML which contains the definition of my button (location, callback, image, label, ...). As you can see in the picture below, the execution time for this functions is very long (> 2000 ms). This screenshot is taken from my company notebook. On my private PC, the loading time is around 300 - 400 ms.
I then thought about slow IO for loading the XML file and hardcoded the XML content into the source code (I know, very dirty, but just for testing purposes), but no change. The function still executed for 2000 ms.
Right now, I don't know what to try next and I am a bit helpless. Maybe someone of you has any idea. When required, I can also post more parts of the source code. Any help is appreciated. Thank you in advance!
If your addin only acts in response to ribbon and context menu clicks, it needs to be on-demand - Outlook loads it the first time, then caches the Ribbon XML and the next time it is loaded only when the users clicks on your control.
Also keep in mind that you get punished for using .Net - Outlook has to load the specific version of .Net you are using before it executes anything in your addin.
Related
I created VSTO Outlook add-in that has a button within the main ribbon of outlook and when clicked it lunches windows form. It works great to send the data from the form to the database and it composes email as well.
My problem is that users within a company that had it installed are getting slow load notification and the add-in is disabled. I tried to fix it and change all code for multi-threading but it seems not to improve the load time much. Is there any other way to make sure add-in on all user's machines is always enabled. Maybe some other way to publish other than ClickOnce.
Thanks in advance for any help.
Multithreading won't help you much - Outlook looks at your addin startup times, not the time it takes to respond to the button click.
If you are using .Net, you get punished for that - the .Net run-time has to be loaded before a single line of your addin code gets executed. Unless you switch to an unmanaged language (such as C++ or Delphi), there is nothing you can do about that.
I have a VSTO addin for Outlook 2013. The odd thing is sometimes when I do something seemingly unconnected. My button group disappears from the place it was in. Was there working perfectly for a long time. I added a ribbon group launcher, and it vanished. I then rolled back the pending changes and it still isn't there.
First, why does my ribbon group always disappear, it is set to Position: AfterOfficeId GroupMailDelete and the tab is set to TabReadMessage. This has always worked for me up until just now. I looked up the Office Id's for elements in office 2013 in the docs and they are correct (and as I said it's been fine for months).
Second, why does it not reappear when I rollback changes? I only changed that file and when I rolled back it looked like it did before I touched it.
Also it was working right until I made the change. Tested before, worked, tested again my ribbon group is gone.
I really can't stand this problem as it messes up my entire addin. Someone please offer assistance as none of the other SO answers or anything else I've found have helped.
Thanks a ton
EDIT:
Rewrote the program copying over the logic and redoing the ribbon and forms in designer. Register the event handlers was a pain as was constructing the two forms, one from EF and one using raw sql. But aside from that it works now. Why would it work when I rewrite it, but still not work when I rollback the breaking change? Not to mention that same change didn't break the copy I rewrote.
For anyone researching this in the future, try this link - which worked perfectly for me: https://msdn.microsoft.com/en-us/library/ms268871(v=vs.140).aspx
Did you check out the list of running COM add-ins right after the ribbon disappeared? Is your add-in listed in the list as an active one?
Microsoft Office applications can disable add-ins that behave unexpectedly. If an application does not load your add-in, the application might have hard disabled or soft disabled your add-in.
Hard disabling can occur when an add-in causes the application to close unexpectedly. It might also occur on your development computer if you stop the debugger while the Startup event handler in your add-in is executing.
Soft disabling can occur when an add-in produces an error that does not cause the application to unexpectedly close. For example, an application might soft disable an add-in if it throws an unhandled exception while the Startup event handler is executing.
When you re-enable a soft-disabled add-in, the application immediately attempts to load the add-in. If the problem that initially caused the application to soft disable the add-in has not been fixed, the application will soft disable the add-in again. Read more about that in the How to: Re-enable an Add-in That Has Been Disabled article.
Do you get any UI errors?
See How to: Show Add-in User Interface Errors for more information.
Finally, what ribbon XML do you use? Could you be more specific?
I'm trying to embed Excel 2013 in a WPF app. The problem is that when I call SetWindowLongPtr in the following code, Excel 2013 crashes immediately. I digged it and found that if I comment out WS.CHILD style, it works fine, but the Excel sheet becomes readonly, which is not what I want. The same code works fine with Excel 2010.
Excel.Application _excelApp;
IntPtr _wrappedApplicationHandle;
Int64 lngStyle;
Int64 lExStyle;
private void Button_Click_1(object sender, RoutedEventArgs e)
{
_excelApp = new Excel.Application()
{
Visible = true,
DisplayFormulaBar = true,
};
_wrappedApplicationHandle = new IntPtr( _excelApp.Hwnd);
lngStyle = GetWindowLongPtr(_wrappedApplicationHandle, (int)GWL.STYLE).ToInt64();
lngStyle &= ~(int)WS.CAPTION;
lngStyle &= ~(int)WS.SIZEBOX;
lngStyle |= (int)WS.MAXIMIZE;
lngStyle |= (int)WS.CHILD; //<< crashes with this line
lngStyle |= (int)WS.CLIPSIBLINGS;
lngStyle |= (int)WS.CLIPCHILDREN;
SetWindowLongPtr(new HandleRef(_excelApp, _wrappedApplicationHandle),
(int)GWL.STYLE,
new IntPtr(lngStyle));
...
}
EDIT
Some more information as I'm digging through. I tried wrapping the above code in a try/catch block to see what happens. It never reaches the catch block. Excel 2013 crashes with the infamous "Application has stopped working. Send report to MS" error. I have already turned on all Win32 / COM / C++ Exceptions in Visual Studio (through Debug menu > Exceptions dialog), but that doesn't help either. There is a Debug button in the error dialog. If I click that and open a debugger, the error msg I see is " 0xC0000005: Access violation reading location 0x0000000000000000."
I also found that commenting out the WS.CHILD line in the above code doesn't strictly make the worksheet readonly. It just blocks common keyboard/mouse input from reaching the worksheet. But some keys like the context-menu key of the keyboard still reaches there and the context menu is shown (right-clicking through the mouse doesn't work though). Similarly, I can interact with the Office Ribbon through the mouse. It appears as if only the worksheet area (the grid with the white background) is not receiving keyboard/mouse input.
EDIT 2
I just recalled that (apparently) in contrast to what Hans Passant has explained in his post below, VS2010 hosts an Excel instance in itself when you create a Excel VSTO Workbook project. Though I don't have VS2013 (Full) with me and can't confirm, I suspect VS2013 would do the same with Excel 2013 VSTO Workbook projects. Considering that VS2010 and above are all WPF applications themselves, how does that fit in the picture?
EDIT 3
That private interfaces theory suggested by #acelent (see comment below) appears to be correct. I spied into VS2010-hosted Excel instance and found that there was a new window with classname = EXCELI which is not there when we normally open Excel (normal hierarchy of windows is XLMAIN (application) > XLDESK (workspace area) > EXCEL7 (workbook)). Also that workbook is no longer available as an ActiveX object, which used to be the cases back when Office Web Components library was available (last shipped with Office 2003). So all in all, we seem to be on a dead end and I'm going to suggest #Hans's answer to my client unless someone comes up with an actual working method in the next few hours.
You'll need to give up on this, you cannot make it work reliably.
WS_CHILD cannot work by design, Windows has the rock-hard requirement that the parent window and its child belong to the same process. They pass messages between each other, the child will crash when it tries to dereference a pointer that belongs to the parent's process.
Windows does have some appcompat support for SetParent(), required because this was commonly done in Windows 3.x programs. The notion of processes with separate address spaces was entirely absent in that Windows version so it wasn't a problem back then. The odds that this still actually works properly after 20 years is proportional to how much the process behaves like a Windows 3.x app. It works okayish for a console window or a simple app like Notepad. Office 2013 apps are considerably beyond simple and 3.x. Having trouble with input is certainly a hallmark for this. You could tinker with AttachThreadInput() to try to solve it but you'll likely just run head-on into the next problem, including its nasty habit of causing deadlock when you debug.
Embedding Office apps used to be a strongly supported feature in Office, Microsoft intentionally designed them to be embeddable. The underlying technology was called OLE Linking and Embedding. This technology is however strongly deprecated. Support for it was intentionally left out of the .NET Framework. Office 2003 was the last version that still supported it well. Trouble started at 2007 and it was completely scrapped for 2010. It will not come back.
The way ahead here is the exact opposite one. Don't embed the Office app, let the Office app embed you. Writing add-ins for Office programs is strongly supported.
I wrote a small Outlook-PlugIn (VSTO/C#) which stores specific emails in a user-specified directory. My problem is that sometimes there are emails with big attachments and/or many (50+) emails to be saved at once.
The PlugIn handles all that but in some cases Outlook get's unresponsive and has a progressbar showing up. Is there a way to prevent that from happening? (for example another way of saving those emails)
If not, maybe someone has an idea on how to 'work around' this behavior (for example stop the saving when Outlook is 'used' by the user)
*edit this takes place in .NET 4 and must be compatible to Outlook 2007 and Outlook 2010.
You could define a delegate that will run the method asynchronously.
This would mean that the (possibly) long running SaveAs(filepath) will not block the UI and cause Outlook to show the progress bar/become unresponsive to user input.
A really good simple example of this style of asynchronous coding can be found here Using AsyncCallBack
And here is the official MSDN Article
I am working with a COM component. There is a method that does this call to the component, and this method is used many times in my application, for each document to be processed, this method is called.
One weird thing happens sometimes suddenly, doesn't matter the amount of documents processed, it can happen after processing 60, 100 or 300 documents, or just don't happen. The weird thing: the call to the component doesn't return. The method stays stuck into the call line. Do you know any COM particularity that could be causing this problem?
From your question I get that your are doing COM automation with documents. If your COM object is a document processing application (Would MS Office be the right guess?) then it might be that the application is simply blocked by a popup.
This phenomenon happens quite frequently when you automate e.g. Word or Excel. You should do several things to work around this problem (I'm talking about MS Word here):
disable alerts by setting Application.DisplayAlerts accordingly
install the complete products to avoid Windows Installer popups asking for missing features
implement a time-out mechanism that will kill the application if any modal dialog is requesting user input. The reason for that is that there are certain types of popups which cannot be suppressed (If you need further information please ask).