How to improve the performance of an VSTO Outlook addin at startup / prevent Outlook from disabling AddIns - vsto

As described in How to programmatically set registry setting to prevent Outlook from removing the VSTO Addin Outlook disables automatically AddIns which show poor performance on startup.
Microsoft describes basic rules for Addins which the developer should comply with: https://learn.microsoft.com/de-de/office/vba/outlook/concepts/getting-started/support-for-keeping-add-ins-enabled
Although what I don't understand is which code is executed when the startup gets loaded (during startup of Outlook)
The following 3 things get executed during startup of the Addin:
1.) All initialization of global variables
2.) The c'tor of the AddIn class (ThisAddIn)
3.) The method ThisAddIn_Startup
Is this correct?
Furthermore, I use
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
to initialize logging. Is this an expensive method regarding startup performance?
How can I find out which part (procedure calls) of this startup process takes most of the time? How do I use the profiler on an Addin? Do I guess I have to trace the Outlook.exe, correct? How can I extract the profiling info just for my AddIn?
I chose to explore this topic even deeper and removed the complete code in ThisAddIn_Startup, switched to Release (instead of Debug) in Visual Studio:
Guess what? The Add-in is still too slow! There are no c'tors, no initialization of any framework whatsoever, so we talk about a complete blank Add-in ... and still, Outlook fires up the message: This add-in is too slow!! Furthermore, I saw that you have to start OUtlook (without VisualStudio) several times so that OUtlook calculates a meantime for the startup of the add-in. But still, my complete clean Add-in takes 0,61sec to load which is too slow for Outlook. I have no idea what makes an empty Add-in take 0,61sec to start. I run OUtlook on an i7-CPU intel laptop (16 GB) so there see no reason for this.

It might not be you - keep in mind that that you get penalized for using .Net run-time; there is nothing you can do about that except switch to an unamanged language, such as VC++ or Delphi. I has success before with creating a stub COM addin in Delphi (load time about 20 ms) which loads a VSTO addin asynchronously (when Outlook is not looking) using IManageAddin interface.
Also keep in mind that VSTO run-time will validate the certificate used to sign your addin unless it resides in a secure location that requires admin rights to install to, such as a subfolder of the Program Files folder.

Is this correct?
You are absolutely right. You need to move any long-running or time-consuming tasks to another thread. Or just wait until everything is initialized by running a timer. In the timer's event, you can do any initialization stuff. Be aware that .NET includes four classes named Timer, each of which offers different functionality:
System.Timers.Timer: fires an event at regular intervals. The class is intended for use as a server-based or service component in a multithreaded environment; it has no user interface and is not visible at runtime.
System.Threading.Timer: executes a single callback method on a thread pool thread at regular intervals. The callback method is defined when the timer is instantiated and cannot be changed. Like the System.Timers.Timer class, this class is intended for use as a server-based or service component in a multithreaded environment; it has no user interface and is not visible at runtime.
System.Windows.Forms.Timer (.NET Framework only): a Windows Forms component that fires an event at regular intervals. The component has no user interface and is designed for use in a single-threaded environment.
System.Web.UI.Timer (.NET Framework only): an ASP.NET component that performs asynchronous or synchronous web page postbacks at a regular interval.
Please remember that the Outlook object model should be accessed from the main thread only. Based on this knownledge you must choose the timer class.
Another approach is to handle Outlook's events, for example, the Startup event is fired when Microsoft Outlook is starting, but after all add-in programs have been loaded. So, you can finish your initialization there.

Related

Two-way communication between VSTO add-in and a Task Pane app (JS/HTML)

Is it possible to have a VSTO add-in and a JS/HTML based Task Pane app talk to each other when running inside desktop Office application?
The goal would be to bridge capabilities not yet present (or never will be present) in the x-platform Office APIs using VSTO.
As Evgene mentioned, there is no out-of-the-box functionality for this (though the topic has certainly come up in internal discussions, so I would be very curious to hear your scenario, and particularly how you would handle the VSTO-only-on-the-desktop case).
If you wanted to try this now, you probably still could with a bit of a creative solution. Imagine you have a hidden sheet with a named range on it, which you use as a communication buffer between VSTO and your taskpane app.
For VSTO => JS communication: have the taskpane create a binding over the named range, then use the binding-data-changed event to listen to an incoming message from your VSTO add-in.
For JS => VSTO communication: have the JS set data to the binding (which, as above, was created over the named range). On the VSTO side, listen to data changed event, intersect the incoming range with the named range's range, and if the intersection is not null, fetch the text to get the incoming message.
If you do end up trying out this approach, do keep the thread posted on how it went -- I'd be quite curious to hear.
~ Michael Zlatkovsky (developer on Office Extensibility team)
Need to admit that VSTO based add-ins can be run only on Windows and desktop clients. Other platforms can't be supported with such scenario.
Anyway, VSTO nor Mail Apps (based on Office JavaScript API) don't provide anything for that out of the box.

Custom splash/start page for SL4 OOB application using Prism

I could use some help figuring out the best way to implement a "splash"/start-up page for my Silverlight 4 client applications that are built using Prism 2 and run out-of-browser.
I am supporting a suite of applications and am working on a common library of controls and services that all of the applications may use. As part of this, I am creating a subclass of the UnityBootstrapper class to register the services.
I've run into a situation where I need to 'pre-load' a couple of the services with data from the server on start-up. This could take a bit of time so we'd like to display a splash screen while all of the start-up steps are executed. Since we are running out-of-browser, I know this isn't straight forward. Any help is appreciated.
I'm also open to other approaches for start-up data that can't be 'lazy loaded'.
Check prism's sample project(under your PRISM installation):
Prism\Quickstarts\Modularity
That will show you how to KNOW when module loads/completed
You can just use Busy indicator with style over your Shell to indicate that you loading.
So after many trials and errors, I've come up with the following approach that I am now working through to see how well it works.
I've created a Shell UserControl in my class library that acts as a wrapper (container) for the UI. I set this control as the RootVisual. Within the content of this control, I add my splash control/view and make all of the necessary startup service calls. Using WaitHandles, I wait until all of the calls have returned before replacing the splash control with the application's start page.
The application has no idea how any of this works, which was my goal. They simply override a method I've added to the bootstrapper to make any startup service calls. The service calls are executed on a background thread and the code uses WaitHandle.WaitAll to block until all of the calls are completed which then uses Dispatcher.BeginInvoke to replace the splash with the application's main page.
This all seems to work pretty well.

How to Cache Real-time Data?

I'm working on a windows forms application (.NET 4.0).
My form contains a 'Fast Line' chart using the Microsoft chart control included in VS2010.
The chart gets filled with about 20,000 datapoints.
My application then starts receiving market data from a server via DDE (Dynamic Data Exchange) in real-time and adds it the chart.
Note: I have no control over the server and so I have to deal with DDE only even though it's an outdated technology. VS doesn't support DDE anymore and so I use the Ndde library which works like a charm.
First we connect to the server, create an advise loop, and then subscribe to the OnAdvise event to receive notifications of new data:
Dim client As DdeClient = New DdeClient("ServerApplication", "Bid")
Private Sub StartDDE()
client.Connect()
client.StartAdvise("EURUSD", 1, True, 60000)
AddHandler client.Advise, AddressOf OnAdvise
End Sub
Now we can put the commands to update the chart inside the event:
Private Sub OnAdvise(ByVal sender As Object, ByVal args As DdeAdviseEventArgs)
Dim myPrice As Double = args.Text
Chart1.Series("Bid").Points.AddY(myPrice)
End Sub
You get the idea.
THE PROBLEM:
This works fine for a few seconds until the chart crashes throwing the exception:
"Collection was modified; enumeration operation may not execute."
I spent a lot of time researching what may be the cause of this in my particular case, and I've come to the conclusion that it's because the chart is receiving data quicker than it can handle. It's already loaded with a lot of data and needs a certain time (less than a second) to add the received data in a new DataPoint and invalidate (refresh) itself. Whereas the server often sends data values very quickly (for example 5ms in between). So I've tried the following:
System.Threading.Thread.Sleep(800)
Chart1.Series("Bid").Points.AddY(myPrice)
thus pausing the application to give time to the chart to finish its work before adding a new point, and guess what? The application now works for minutes before throwing the exception. (altering the value in Sleep() doesn't help any further however)
The only help I could find online is an old post of someone mentioning that you should put incoming data on a cache queue, with one new data value released from the cash at a time (every time the chart finishes working).
My question is how would you do this?
Other suggestions are welcome!
This is most likely an issue caused by attempting to modify a UI element from a thread other than the UI thread.
The way you have it coded now the DdeClient.Advise event handler is being executed on a worker thread managed by the library. See, DDE sucks and because it sucks it has these requirements that it has to run on a thread with a message pump.1 To make the library compatible with other types of applications besides windows forms I coded it in such a manner that it will create a dedicated thread with a message loop and marshal all of the operations onto that thread by default.
But, you can override this behavior by specifying an ISynchronizeInvoke instance manually in the DdeClient constructor. The library will then use whatever thread is hosting the ISynchronizeInvoke instance for all of its DDE operations. All Form and Control instances implement ISynchronizeInvoke so it is easy enough to tell the library to use the main UI thread.
Dim client As DdeClient = New DdeClient("ServerApplication", "Bid", yourForm)
If you tell the library to use your Form instance then the Advise event handlers will be executed on the same thread hosting that Form; the UI thread.
By the way, I realize that you have no control over the server, but I would at least begin talking with the vendor of the software to use more modern (not 20 years old) mechanisms for doing interprocess communications.
1It also has the unfortunate requirement of thread affinity which made dealing with the garbage collector a real pain.
Get real ;) DDE is slow, graphics is slow. Do not do them in the same thread.
Try that:
Create a second thread that handles DDE, queues the items.
The chart thread then pulls the updates and draws them.
Now, here comes the point:
ONLY the ui thread is allowed to modify the chart control. Yes, sucks. No, not negotiable. - old UI rule since the dawn of time.
Threads needs locking ;)

Unattended application best practice question

We have an unattended app w/o a user interface that is is periodically run.
It is a VB.NET app. Instead of it being developed as a service, or a formless Windows application, it was developed with a form and all the code was placed in the form_load logic, with an "END" statement as the last line of code to terminate the program.
Other than producing a program that uses unneeded Windows form resources, is there a compelling reason to send this code back for rework to be changed to put the start up logic in a MAIN sub of a BAS file?
If the program is to enter and exit the mix (as opposed to running continuously) is there any point in making it a service?
If the app is developed with a Form do I have to worry about a dialog box being presented that no one will respond to even if there are no MessageBox commands in the app?
I recall there used to be something in VB6 where you could check an app as running unattended, presumably to avoid dialogs.
I don't know whether there are conditions where this will not run.
However, if the code was delivered by someone you will work with going forward, I would look at this as an opportunity to help them understand best practices (which this is not), and to help them understand that you expect best-practice code to be delivered.
First of all, you don't need it to be run in a Form.
Forms are there for Presentation, so it should not be done there.
If you don't want to mess with converting the application a Service (not difficult, but not very easy neither), you shoud create a Console Application, and then, schedule it with Windows Task Scheduler.
This way, you create a Console Application, with a Main function, that does exactly what you need.
Anyway, the programmer could show windows, so there should not be any messagebox. Any communication should be done via Logging to: local files, windows events, database.
If you want more information on any of them, ask me.
If you don't want it to be a service, nothing says that it has to be a windows service. Scheduling it to run via the Task Scheduler or something similar is a valid option.
However, it does sound like the developer should have choose a "Console App" project, instead of a "Windows Forms" project to create this app.
Send it back. The application is bulkier and slower than it needs to be, although that won't be much of an issue. It is somewhat more likely to run out of resources. But the main reason: converting it to a console app is very easy.
If you don't prefer for the Console window to popup, simply do the following.
Create a new class "Program.vb", add a public shared Main() method, and move the "OnLoad" logic from the form to this method.
Next delete the form, and change the project start up object (Available in the project properties window) to use the Program.Main instead of the Form.
This will have the same effect, without the windows forms resources being used. You can then remove the references to System.Windows.Form and System.Drawing.

Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on

I am writing a filewatcher windows application which will look for changes in a specified folder and then logs the details in a txt file.
I followed exactly what is mentioned in this article below
http://www.codeproject.com/KB/dotnet/folderwatcher.aspx
When I hit F5 from my application and then create or modify a file in the folder that is being watched it throws the below mentioned error.
Please help
Cross-thread operation not valid: Control 'txtFolderActivity' accessed from a thread other than the thread it was created on.
You have to use the Invoke method on the form e.g. with an anonymous delegate to make your changes in reaction to the event.
The event handler is raised with another thread. This 2nd thread cannot access controls in your form. It has to "Invoke" them to let the thread do all control work that initially created them.
Instead of:
myForm.Control1.Text = "newText";
you have to write:
myForm.Invoke(new Action(
delegate()
{
myForm.Control1.Text = "newText";
}));
You are trying to update the UI from a non-UI thread. UI has a thread affinity and can only be updated from a thread that created it. If you're using WinForms, check out How to: make thread-safe calls to Windows Forms Controls MSDN article. Basically you will need to update the UI via Control.Invoke method. For WPF, you need to use DispatcherObject.
Basically you must have two threads in your application, at least, and the thread that your control logic is on is different, so you get this error, as the controls are not threadsafe.
This is to protect you from problems that could be caused by multiple threads changing the same control at the same time.
You can find considerably more detail by looking here:
http://msdn.microsoft.com/en-us/library/ms171728%28VS.80%29.aspx