Order of events reversed 'Ribbon_Load' and 'ThisAddin_Startup' Word VSTO Add-in. (Build 8201.2025 onwards) - ms-word

As of Build 8201.2025 there has been an unexpected change to the order of events when loading a VSTO addin with a Ribbon in Word.
Using Office version 16.0.8067.2115 or older. When loading the addin the following order of events is observed (as has always been the case).
Ribbon_Load event
ThisAddin_Startup event
Using Office versions 8201.2025, 8201.2064 or 8201.2075 or newer the order of events is reversed which is an unexpected breaking change.
ThisAddin_Startup event
Ribbon_Load event
I have created a simple VSTO Addin using a Visual Designer Ribbon to demonstrate the issue.
>
Public Class Ribbon1
Private Sub Ribbon1_Load(ByVal sender As System.Object, ByVal e As RibbonUIEventArgs) Handles MyBase.Load
System.Diagnostics.Debug.Write("Ribbon1_Load event called.")
'Pass the Ribbon to the Addin.
ThisAddIn.MyRibbon = Me
End Sub
End Class
Public Class ThisAddIn
Public Shared Property MyRibbon As Ribbon1 = Nothing
Private Sub ThisAddIn_Startup() Handles Me.Startup
Debug.Write("ThisAddin_Startup Called")
If (MyRibbon Is Nothing) Then
Debug.Write("MyRibbon is nothing - the ribbon was not captured.")
Else
Debug.Write("Ribbon captured successfully.")
End If
End Sub
End Class
Debug output for 16.0.8067.2115 32 bit
[7772] Ribbon1_Load event called.
[7772] ThisAddin_Startup Called
[7772] Ribbon captured successfully.
Debug output for 16.0.8201.2075 32 bit
[13556] ThisAddin_Startup Called
[13556] MyRibbon is nothing - the ribbon was not captured.
[13556] Ribbon1_Load event called
I have posted this up on the Microsoft Support forums however they have stopped responding and since released this version to the Current office channel I need help from the dev community.
Has anyone found a successful workaround? This change of timing is causing alot of problems with how we initialise. It would be ideal for Microsoft Support to provide a solution or workaround until they investigate this bug.

I always got Ribbon_Load before ThisAddin_Startup because I use Ribbon XML. Ribbon UI allow less controls ... As the both are "entry" points, I suggest you to use only Ribbon1_Load at startup. Or, if you use the Ribbon XML model and you want the very very first entry point, try its constructor
I am not feeling that issue as a bug, to make Word fast many processes are asynchronous. So, in my opinion, the first of ThisAddin_Startup or Ribbon1_Load to start can accidentally change depending on many factors: System performances, Word started alone, Word started via a doc ...

Hope this helps someone! We used the following workaround successfully to work around the changed office load behavior.
Within ThisAddIn_Startup loop until the Ribbon load event has fired and the ribbon is captured.
While m_oRibbon Is Nothing
If (timeWaited >= MAX_WAIT_TIME) Then
Exit Try
End If
Threading.Thread.Sleep(50)
timeWaited = timeWaited + 50
End While

Related

Why does my Outlook add in randomly stop working?

I have a plugin running in Outlook 2013 that is intended to filter out spam. It looks at incoming emails, runs through a half-dozen heuristics, and if 1 or 2 of them return positive, it moves the mail to another folder for inspection; if three or more are tripped, it permanently deletes the mail.
My problem is that it randomly stops working. The add in isn't marked as "disabled" or "inactive"; it just doesn't work anymore. Disabling it and then enabling it brings it back on line, and then it will work fine, including on the mail that it just failed on. And then it will fail randomly again a few messages later. No error is being raised as far as I can see; no exception is being thrown.
I feel like this has something to do with Outlook's limits for add-ins, since it is (a) non-deterministic and (b) seems to happen more often the more checks I do. But nothing I am doing is very heavy; most of it is just checking things in the email address, subject, and headers, or looking for key phrases in the body. Eyeballing it, every message appears to be processed in just a fraction of a second. I guess the first question is, is there a way to see Outlook's decision process on this, i.e. the specific perf statistics it is tracking? (ETA: Does Outlook enforce its restrictions when the add-in is invoked, or just at startup? Everything I can find only refers to startup.)
No single one of the checks seems to be responsible; I've disabled each one individually and it keeps happening.
Here's the central code. SpamMarkerCheckList is a list of delegates for the individual heuristic checks. ClearAllMailMarkers() does things like mark the mail as read, set the priority to normal, remove any flags, etc. headers is a Dictionary with the header names as the keys and lists of strings as the values.
private static void FilterInbox(object mailItem)
{
try
{
if (mailItem != null)
{
var mail = (Outlook.MailItem)mailItem;
var markers = SpamMarkersCount(mail, 3);
if (markers > 2)
{
PermanentlyDeleteEmail(mail);
}
else if (markers > 0)
{
ClearAllMailMarkers(mail);
mail.Move(_sequesteredFolder);
}
}
}
catch (Exception e)
{
MessageBox.Show("FilterSpam caught unexpected exception -- " + e);
}
}
private static int SpamMarkersCount(Outlook.MailItem mail, int threshold)
{
var spamMarkerCount = 0;
var headers = GetHeaderProperties(mail);
var emailAddressList = BuildAddressList(mail, headers);
var fullStringList = GetAllStrings(mail, headers);
foreach (var spamMarkerCheck in SpamMarkerCheckList)
{
if (spamMarkerCheck(mail, headers, emailAddressList, fullStringList))
{
spamMarkerCount++;
if (spamMarkerCount >= threshold)
{
return spamMarkerCount;
}
}
}
return spamMarkerCount;
}
The checks I am doing are:
Non-ASCII characters in the sender's name or address (e.g. hearts, shopping carts, etc.)
Indicators in the headers, like the presence of List-Unsubscribe or failed SPF, DKIM, or DMARC authentication
Email addresses that are badly formed or missing
If it was sent from a fake domain (via doing a DNS lookup)
The presence of the user's email alias in the subject or sender name ("delius1967 is a winner!")
If it was sent from a known blocked list of domains
If it contains specific phrases (e.g. "this is an advertisement")
I'd suggest checking the Windows event viewer for Outlook-specific records. Most probably your add-in fires an exception at runtime.
First and foremost, Microsoft Office applications can disable VSTO Add-ins that behave unexpectedly. If an application does not load your VSTO Add-in, the application might have hard disabled or soft disabled your VSTO Add-in.
Hard disabling can occur when a VSTO 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 VSTO Add-in is executing.
Soft disabling can occur when a VSTO Add-in produces an error that does not cause the application to unexpectedly close. For example, an application might soft disable a VSTO Add-in if it throws an unhandled exception while the Startup event handler is executing.
When you re-enable a soft-disabled VSTO Add-in, the application immediately attempts to load the VSTO Add-in. If the problem that initially caused the application to soft disable the VSTO Add-in has not been fixed, the application will soft disable the VSTO Add-in again. Read more about that in the How to: Re-enable a VSTO Add-in that has been disabled article.
Second, extending the add-in resiliency pillar of Outlook 2010, Outlook 2013 and later versions monitor add-in performance metrics such as add-in startup, shutdown, folder switch, item open, and invoke frequency. Outlook records the elapsed time in milliseconds for each performance monitoring metric.
For example, the startup metric measures the time required by each connected add-in during Outlook startup. Outlook then computes the median startup time over 5 successive iterations. If the median startup time exceeds 1000 milliseconds (1 second), then Outlook disables the add-in and displays a notification to the user that an add-in has been disabled. The user has the option of always enabling the add-in, in which case Outlook will not disable the add-in even if the add-in exceeds the 1000 millisecond performance threshold. Read more about that in the Performance criteria for keeping add-ins enabled section.
You may find the Outlook’s slow add-ins resiliency logic and how to always enable slow add-ins article helpful.
Finally, Outlook uses a single-threaded apartment model and may prevent any calls from secondary threads by throwing exceptions at runtime. You should use the OOM on the main thread only. If you need to do any processing on the background you may consider extracting the required plain data and passing it for processing. Extended MAPI allows running secondary threads also.
I found the proximate cause: garbage collection. After running in the debugger for quite a while, I noticed that the add-in stopped working immediately after a GC event; several more runs through show that this happens consistently.
The ultimate cause was in how I was associating the function with the folder:
private static Outlook.Account emailStore; // outside ThisAddIn_Startup
[...]
emailStore.DeliveryStore.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox).Items.ItemAdd += FilterInbox;
For reasons I do not fully understand, that causes a problem after GC, but this does not:
private static Outlook.Items emailItems; // outside ThisAddIn_Startup
[...]
emailItems = emailStore.DeliveryStore.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox).Items;
emailItems.ItemAdd += FilterInbox;
I get why having it as static is critical, but I don't understand why it makes a difference in the assignment. Anyone with a deeper understanding of C# objects who can explain, I would love to hear it.

How can I get Excel to close when I'm done with it?

This is in a COM API Word AddIn. And yes normally Hans Passant's advice to let .NET clean everything up works.
But it is not working for the following case - and I have tested running normally (no debugger) and have narrowed it down to this specific code:
private Chart chart;
private bool displayAlerts;
private Application xlApp;
Chart chart = myShape.Chart;
ChartData chartData = chart.ChartData;
chartData.Activate();
WorkbookData = (Workbook)chartData.Workbook;
xlApp = WorkbookData.Application;
displayAlerts = xlApp.DisplayAlerts;
xlApp.Visible = false;
xlApp.DisplayAlerts = false;
WorksheetData = (Worksheet)WorkbookData.Worksheets[1];
WorksheetDataName = WorksheetData.Name;
WorksheetData.UsedRange.Clear();
// ... do a bunch of stuff including writing to the worksheet
xlApp.DisplayAlerts = displayAlerts;
WorkbookData.Close(true);
I think the problem is likely Word is giving me this workbook and so who knows what it is doing to instantiate Excel. But even after I exit Word, the Excel instance is still running.
Again, in Word (not Excel), accessing a chart object to update the data in the chart.
COM objects need to be released completely, else "orphaned" objects can keep an application in memory, even after the code that called it goes out-of-scope.
This particular case may be special (compared to other code you've used previously) due to using xlApp. By default, an Excel Application object is not needed or used when manipulating charts with the object model introduced in Office 2007 (I think it was). It's used in the code in the question in order to hide the Excel window, which is visible by default (and by design). But the object model isn't designed to handle cleaning that up - it assumes it isn't present...
In my tests, the object is released correctly when (referencing the code in the question):
All Excel objects are set to null in the reverse order they are instantiated, being sure to quit the Excel application before trying to set that object to null:
WorksheetData = null;
WorkbookData = null;
xlApp.Quit();
xlApp = null;
Then, C# has a tendency to create objects behind the scenes when COM dot-notation is used - these don't always get cleaned up (released) properly. So it's good practice to create an object for each level of the hierarchy being used. (Note: VBA doesn't have this problem, so any code picked up from a VBA example or the macro recorder needs to be re-worked in this respect.) From the code in the question this affects WorksheetData.UsedRange.Clear();
Excel.Range usedRng = WorksheetData.UsedRange;
usedRng.Clear();
usedRng = null;
And the standard clean up, to make sure everything is released at a predictable moment:
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
When things like this crop up, I always refer to ".NET Development for Office" by Andrew Whitechapel. That's really "bare bones" and some of it no longer relevant, given the changes to C# over the years (to make it "easier to use" in the way VB.NET is "easier"). But how COM interacts with .NET hasn't changed, deep down...

How to control the handle order of multiple coexisting VSTO Word Addins

If I have two different VSTO AddIns installed on the same Word application, is there a way to know which one of them first receives the events raised by Word. For example the DocumentOpen event?
Can I control that order?
Thanks
I didn't find anything specific about event order in multiple add-ins (but I believe I've read something about it years ago) so I did simple test
I created three Excel add-ins with this code
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
this.Application.WorkbookBeforeSave += Application_WorkbookBeforeSave;
}
void Application_WorkbookBeforeSave(Excel.Workbook Wb, bool SaveAsUI, ref bool Cancel)
{
System.Windows.Forms.MessageBox.Show("Add-in 1");
}
One of them I gave it random name as I wanted to know if Excel runs them in alphabetical order, in one of the add-in I set the Cancel = true
I don't know why but Excel always fired the ExcelAddin 2 first, then ExcelAddin 1 and finished with AAExcelAddin. I tried to rebuild (I also cleaned my solutions first) in different order but the order was still the same (note the very first addin created was the ExcelAddin 1) No matter if I run it from VS or just simply start Excel and pressed CTRL+S
Based on above I would say that you should not have any logic in your code that assume certain order of add-ins. You never know if any new add-in will break the order.
Also keep in mind that if you use any of the cancellation events (has the argument Cancel) and you cancel it (Cancel=true) then all the other add-ins will receive the event and will still run it but the Cancel flag will be set to true from the previous one.
In my case I set Cancel=true in the add-in that was fired at first (ExcelAddin 2) and even the two other add-ins received the event, they didn't save as saving was cancelled in the first one.

EF6/Code First: Super slow during the 1st query, but only in Debug

I'm using EF6 rc1 with Code First strategy, without precompiled views and the problem is:
If I compile and run the exe application it takes like 15 seconds to run the first query (that's okay, since I'm still working on the pre-generated views). But if I use Visual Studio 2013 Preview to Debug the exact same application it takes almost 2 minutes BEFORE running the first query:
Dim Context = New MyEntities()
Dim Query = From I in Context.Itens '' <--- The debug takes 2 minutes in here
Dim Item = Query.FirstOrDefault()
Is there a way to remove this extra time? Am I doing something wrong here?
Ps.: The context itself is not complicated, its just full with 200+ tables.
Edit: Found out that the problem is that during debug time the EF appears to be generating the Views ignoring the pre-generated ones.
Using the source code from EF I discovered that the property:
IQueryProvider IQueryable.Provider
{
get
{
return _provider ?? (_provider = new DbQueryProvider(
GetInternalQueryWithCheck("IQueryable.Provider").InternalContext,
GetInternalQueryWithCheck("IQueryable.Provider").ObjectQueryProvider));
}
}
is where the time is being consumed. But this is strange since it only takes time in debug. Am I missing something here?
Edit: Found more info related to the question:
Using the Process Monitor (by Sysinternals) I found out that there its the 'desenv.exe' process that is consuming tons of time. To be more specific its consuming time with an 'Thread Exit'. It repeats the Thread Exit stack 36 times. I don't know if this info is very useful, but I saved a '.cvs' with the stack, here is his body: [...] (edit: removed the '.cvs' body, I can post it again by the comments if someone really think its going to be useful, but it was confusing and too big.)
Edit: Installed VS2013 Ultimate and Entity Framework 6 RTM. Installed the Entity Framework Power Tools Beta 4 and used it to generate the Views. Nothing changed... If I run the exe it takes 20 seconds, if I 'Start' debugging it takes 120 seconds.
Edit: Created a small project to simulate the error: http://sdrv.ms/16pH9Vm
Just run the project inside the environment and directly through the .exe, click the button and compare the loading time.
This is a known performance issue in Lazy (which EF is using) when the debugger is attached. We are currently working on a fix (the current approach we are looking at is removing the use of Lazy). We hope to ship this fix in a patch release soon. You can track progress of this issue on our CodePlex site - http://entityframework.codeplex.com/workitem/1778.
More details on the coming 6.0.2 patch release that will include a fix are here - http://blogs.msdn.com/b/adonet/archive/2013/10/31/ef6-performance-issues.aspx
I don't know if you have found the solution. But in my case, I had similar issue which wasted me close to a week after trying different suggestions. Finally, I found a solution by changing my web.config to optimizeCompilations="true" and performance improved dramatically from 15-30 seconds to about 2 seconds.

Application not detecting input language changes via Text Service Framework DLL

OK, I have been at this for a while ...
I am trying to track when user changes input languages from Language Bar.
I have a Text Service DLL - modeled from MSDN and WinSDK samples - that registers fine, and I can use the interfaces ITfActiveLanguageProfileNotifySink & ITfLanguageProfileNotifySink and see those events just fine.
I also have finally realized that when I change languages these events occur for the application/process that currently has focus.
What I need to do is to simply have these events able to callback to my own application, when it has the focus. I know I am missing something.
Any help here is appreciated.
Thanks.
I did some double-checking, and you should be able to create a thread manager object without implementing ITextStoreACP so long as you don't call ITfThreadMgr::Activate.
So, the code should look like:
HRESULT hr = CoInitialize(NULL);
if (SUCCEEDED(hr))
{
ITfThreadMgr* pThreadMgr(NULL);
hr = CoCreateInstance(CLSID_TF_ThreadMgr, NULL, CLSCTX_INPROC_SERVER, IID_ITfThreadMgr, (LPVOID*) &pThreadMgr);
if (SUCCEEDED(hr))
{
ITfSource *pSource;
hr = pThreadMgr->QueryInterface(IID_ITfSource, (LPVOID*)&pSource);
if(SUCCEEDED(hr))
{
hr = pSource->AdviseSink(IID_ITfActiveLanguageProfileNotifySink,
(ITfActiveLanguageProfileNotifySink*)this,
&m_dwCookie);
pSource->Release();
}
}
}
Alternatively, you can use ITfLanguageProfileNotifySink - this interface is driven from the ItfInputProcessorProfiles object instead of ItfThreadMgr. There's a sample of how to set it up on the MSDN page for ItfLanguageProfileNotifySink.
For both objects, you need to keep the source object (ITfThreadMgr or ITfInputProcessorProfiles) as well as the sink object (what you implement) alive until your application exits.
Before your application exits, you need to remove the sink from the source object using ITfSource::UnadviseSink, and then release the source object (using Release). (You don't need to keep the ItfSource interface alive for the life of your application, though.)