Fun with Shimming Wizards, Shimming and Outlook

As promised, I
thought I'd report on my experience with the new Shimming wizards that I blogged
about earlier
. The wizards are available for download here.

 

I've been working
mainly with Visual Studio 2005 Beta 1 and the 2.0 CLR. Silly me, I assumed
that these would work out of the box with the bleeding edge bits.
Unfortunately they don't. What they do is install some cool new shimming
projects under the Visual C++ tree in Visual Studio 2003. So I fired up
Visual Studio 2003 and started the add-in shimming wizard. I then pointed
it at my add-in that was written using the 2.0 CLR. It didn't like
this--apparently it can only read the metadata out of a 1.0/1.1
assembly.

 

Fortunately, the
install included some sample 1.0/1.1 assemblies, so instead of pointing it at my
2.0 assembly, I pointed it at one of the 1.1 assemblies. Then it worked
like a champ. I then went through the generated code and replaced the
references to the test assembly with references to my assembly--this was a
pretty easy search and replace. There is one spot in the code where
you have to figure out the strong name for your assembly, or at least the
public key token--this is easily done using ildasm or reflector or some tool like
that.

 

I compiled the
resulting shim dll. Then I added it back to my Visual Studio 2005
solution--actually, I just added it to my setup project and set it to ComSelfReg
on install.

To make sure that only the shim is loading as an add-in and not both the
shim and your managed add-in, you need to either uninstall your old managed
add-in or go to the registry
(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\Outlook\Addins if you chose to
install for all users,
HKEY_CURRENT_USER\Software\Microsoft\Office\Outlook\Addins if you chose to
install for the current user only) and remove the registry key created for your
non-shimmed add-in.

Another
thing that you will want to do
is turn off in the project settings in the Compile section the "Register
for COM Interop" option. Otherwise,
both your shim and your non shimmed add-in will keep registering themselves in
the registry.

I had to
verify that everything was loading as I expected--that is that all my stuff was
loading in its own appdomain.

To do this,
I launched Outlook with my add-in running and without the Visual Studio debugger
attached. Being the rocket scientist type, I fired up cordbg.exe. To
launch mscordbg, launch your .NET Framework SDK Command Prompt from your start
menu, programs, Microsoft .NET Framework SDK group. Then type "cd
bin". Then type cordbg.exe. I always then type ? to get a list of
commands because I can never remember them. I then typed "attachn
Outlook.exe" to attach to Outlook. Then I typed "ap" to list all the
appdomains. Sure enough, there was my add-in, in its own appdomain rather
than in default domain. You can then type "q" to exit cordbg.exe which
will also quit the Outlook process.\

Speaking of
Outlook, getting Outlook to shutdown when you write an add-in is pretty hard
right now. If you have outstanding references to Outlook OM objects (which
you almost always will) the Outlook add-in doesn't get shutdown because it is
waiting for you to release your Outlook OM objects before it shuts you
down. Yeah--it's wierd. The present recommendations that I see to
get around this is to listen to Explorer and Inspector close events and when you
detect that all the Explorer and Inspectors have closed down, you release all
your COM objects (just set all your COM objects you've held on to to null or
nothing, you may also force a GC.Collect()
GC.WaitForPendingFinalizers()). Then, you get OnDisconnection called
because Outlook is happy that you've released all your Outlook OM objects.
The problem is, you really shouldn't have to worry about this! You don't
have to in VSTO because we unload the appdomain thereby cleaning up any
references you may have had.

It would be
cool if someone wrote a modified add-in shim for Outlook that listened to these
Explorer and Inspector close events--ideally, this would just be built into the
existing C++ based shim wrapper. When the last Explorer or Inspector
closes, it could call OnDisconnection on the managed add-in's implementation of
IDTExtensibility2. The managed add-in wouldn't have to do the silly
setting of all COM objects to null because after OnDisconnection is called by on
the managed add-in by the shim, the shim would then unload the appdomain of the
add-in--thereby making Outlook happy because all the outstanding references used
by the add-in would be cleaned up. Then, Outlook would call the
OnDisconnection implementation provided by the shim. The shim would just
return immediately from this because it has already unloaded the actual managed
add-in. This would theoretically fix the nightmare that is Outlook
add-in development right now when it comes to getting the silly things to shut
down.