HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

IDTExtensibility2 implementation for Rubberduck's entry point

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
entrypointidtextensibility2forimplementationrubberduck

Problem

Here is the new & improved Rubberduck 2.x entry point class, based on MZ-Tools 8.0's Connect.VBA implementation of the IDTExtensibility2 COM interface, graciously shared to the Rubberduck team by Mr. MZ-Tools himself, Carlos Quintero.

The modifications made are not yet merged into the project's main repository at the time of this writing, but you can view it on commit 8ac768d9 in my own fork.

I've yet to shim the add-in to get it to be a good citizen and run in its own dedicated AppDomain (.net creates RCW's per-AppDomain, that's why it does matter), so there are still a number of issues beyond just the "entry point" to completely stabilize Rubberduck 2.x, but for now I'm interested in feedback specifically on the IDTExtensibility2 implementation - a useful add-in, I'm discovering, is much trickier to "do right" than what the "hello world" tutorials imply.

Also, since we're using ninject for dependency injection, I'm also interested in feedback on the composition root here, the handling of the _kernel instance and, given the various different ways an add-in can be loaded/unloaded, whether this (which seems to work fine) is done right.

Of course, any other feedback is welcome, too.

```
using Extensibility;
using Microsoft.Vbe.Interop;
using Ninject;
using Ninject.Extensions.Factory;
using Rubberduck.Root;
using Rubberduck.UI;
using System;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Windows.Threading;
using Ninject.Extensions.Interception;
using NLog;
using Rubberduck.Settings;
using Rubberduck.SettingsProvider;

namespace Rubberduck
{
///
/// Special thanks to Carlos Quintero (MZ-Tools) for providing the general structure here.
///
[ComVisible(true)]
[Guid(ClassId)]
[ProgId(ProgId)]
[EditorBrowsable(EditorBrowsableState.Never)]
// ReSharper disable once InconsistentNaming // note: under

Solution

The code that handles the methods OnConnection, OnStartupComplete, OnBeginShutdown and OnDisconnection and their flags is correct. I cannot comment on root composition or NInject.

Some things worth documenting in the code of why it must be that way, just in case some day someone thinks that the code can be "simplified":

-
If you call InitializeAddIn in the OnConnection method when ConnectMode is ext_ConnectMode.ext_cm_Startup, some hosts (VB5) may not have initialized yet the MainWindow property, or the MainWindow.hWnd property. So, the OnStartupComplete method must be used.

-
If you call ShutdownAddIn in the OnDisconnection method when RemoveMode is ext_DisconnectMode.ext_dm_HostShutdown, some hosts crash. So, the OnBeginShutdown method must be used. This has a small side effect: Office prompts to save dirty documents after OnBeginShutdown is called. If the user cancels in the prompt to save dialog, the host is not closed but the add-in was unloaded. It is a small price to pay.

There is only one VB6 host and only one VB5 host, but there are tons of VBA hosts: not only Office versions (2000, 2002, 2003, 2007, 2010, 2013 and 2016) but also other 3rd party hosts that provide VBA:

  • CorelDRAW



  • Autodesk



  • PI ProcessBook (http://www.osisoft.com/pi-system/pi-capabilities/pi-system-tools/pi-processbook/)



  • SCADA systems such as GE iFix (http://www.geautomation.com/products/proficy-hmiscada-ifix)



  • Etc.



The code above is robust enough to handle all those in my experience.

Context

StackExchange Code Review Q#141956, answer score: 3

Revisions (0)

No revisions yet.