Excel Add-ins and COM

Niels van Vliet
It is difficult to write an Excel AddIn in .Net because of bugs in Microsoft products. This page explains several problems that I have had. It concerns Add-in and Com server written in C# using Visual 2005 for Excel XP (also called Excel 2002) and Excel 2003.
Other methods can be used to interface .Net with Excel, but this methods works using Excel XP(2002), and Visual 2005. Other methods might not work with Excel XP.

How to Create...

Add-In: What is a .Net Add-in, and how to build one

An add-in:
Solution:
How To Build an Office COM Add-in by Using Visual Basic .NET is a poor Microsoft tutorial, which explains the basis. I strongly advice to continue to read this page, before releasing your add-in to end-users. All problems related with Add-in are prefixed by Add-in below.

Com: What is a .Net Com server for Excel, and how to build one

A COM server:
Solution:
FIXME explain that MsCoree not found is not a problem.

C++: What is a C++ unmanage add-in, and how to build it

If you want to code in C or C++, you can create a unmanaged add-in (without .Net).
Solution 1
Solution 2
Solution 3

How to Fix ...

Add-In: Apply the Fix (Patch) 908002

Symptom
Solution 1
Solution 2

Add-In: Disable Items

For some reason, an add-in can be marked "Disabled". One reason is if the add-in failed at start-up.
Symptom
Solution

Add-In: Exception OnConnect

No exception should be received by Excel when the Add-In start up.
Symptom
Solution

Add-In: Delegate connected to an Excel event stops to work

If you add a delegate to an Excel event, ex:
menuItems.Last.Click += new ClickEvent(MyStaticFunctionClick); 
and that menuItems is a local variable, the delegate stops to be called once the local variable is garbaged.
Symptom
An event trigger an action correctly during a given period of time (ex: 1 minute), and then does not call the action anymore (ex: a button "MyButton" added to the menu bar or Excel).
Solution:
Move your local variable in a global object that is garbaged only when the Add-in is closed. The class that extends Extensibility.IDTExtensibility2 is a good place to have a private field.

Add-In, COM: Microsoft PIA

It is possible to create a .Net interface of a library that has a Com interface. So it is possible to build your own .Net interface, using directly Excel.exe. But you should use the one built by Microsoft, which is pretty much the same, but with some additional functions.
Symptom
You need Range object, you need to use advanced function in Excel.
Solution

Add-In,COM: Assembly of Dll not found

It is difficult to know what you want to install in the GAC, in System32, or in the Application Folder. I call the Application Folder, where your application will be installed (ex: C:/Program File/MyApplication/).
Unless you have a good reason, it is better not to install things in the GAC (thinks GAC as global variable in a program, and Application Folder as a local variable). So I am going to explain how I install everything in the Application Folder.
Symptom
Solution:

Add-In, COM: Problem of Version

According to the specification of .Net, you should increment the version of your library for each new release. Well, I strongly advice not to. I know that it is very bad not to follow a standard, but today (01/2007), if you have read all this page you understand that the amount of bug and problem is pretty high. By changing your version number you increase the complexity of the install, and the understanding of the conflict and problems.
Symptom
Solution

COM: Com server registry

Symptom
Your Com server does not start, or stops to work when another version is installed.
Solution
I excplain the solution in the following case, modify the solution according to your case: In this case:

Add-In, COM: Two Dlls with COM

Because a bug of Visual, the setup works not correctly in some cases, if you have two dll which needs to be registerd for COM.
Symptom
Solution 1
FIXME explain the solution 1. Basically: add in the setup all assemblies one by one, do not use primary output. Solution explained in several forums, did not work for me.
Solution 2
Add a VB script to your setup which call regasm at the end of the install, and at the beginning of the uninstall.

Add-In, COM: Problem with the .Net Framework

We had some problem on some PC, the Framewok .Net 2 was corrupted.
Symptom
Some .Net files where missing. Do not remember which.
Solution
Re-install the .Net Framework. Note: see the section SP1.

Add-In, COM: Problem with the SP1

We had a very strange error message at installation time. We had to re-install the SP2.
Symptom
Solution
Install the SP2.

COM: Result of formula is output in a wrong cell

It cause some problems to Excel to handle not finite doubles. Also, there is some bugs, when a property of the cell is read.
Symptom
A function in Excel called within a given range (ex:A1) writes in other cells (ex: A2).
Solution

Add-In, COM: Debug more than 60 seconds

In many cases it is not possible to debug more that 60 seconds, especially with Visual 2005+Excel. There is the following explaination on this MSDN web page: "The ContextSwitchDeadlock managed debugging assistant (MDA) is activated when a deadlock is detected during an attempted COM context transition".
Symptom
After around 1 minute of debug, you receive the following message, and can not continue to debug normally:
TheCLR has been unable to transition from COM context 0x157948 to COM
context 0x1577d8 for 60 seconds. The thread that owns the destination
context/apartment is most likely either doing a non pumping wait or
processing a very long running operation without pumping Windows
messages. This situation generally has a negative performance impact
and may even lead to the application becoming non responsive or memory
usage accumulating continually over time. To avoid this problem, all
single threaded apartment (STA) threads should use pumping wait
primitives (such as CoWaitForMultipleHandles) and routinely pump
messages during long running operations.
Solution
Important: this solution disable the MDA, but the MDA is a powefull tool that helps to finds some complex bugs. By removing the MDA you might hide a real problem. In the links above there are some solutions to remove the problem without disabling the MDA (but they have some drawbacks).
Fixme: one day I will have to read the article of Christo understand the problem...

How does Excel computes your workbook

This article explains how Excel computes your workbook.

I have a lots of tips, but here are 3 important ones:

Value of an argument changes

A function called with a given number (ex:"42") is called with a different one (ex:"42.000000000002").
Symptom
You have a function MyFunction:
double static double MyFunction(double d){/*your code*/}
And you call it in Excel with a given number:
=MyFunction(exp(a1))
In the debugger, the value of d differs from one call to another (very small difference, very rare).
Solution
Keep in mind that your are working with double, and that the less significant bits of doubles can change for a lot of reason. Another way to think, is to imagine that the function is alwasy call by Excel with +- epsilon.

Function called with zero and null

When Excel creates its dependency tree, your function might be called with uncomputed arguments. The value receive is the default value of the type (ex: double=> 0, Range =>null...).
Symptom
Your function is called with arguments not initialized.
Solution
I did not find any good solution with .Net, although I know how to do it with xll. I know a solution which works, but is not a good one (email me if you want to know more, or if you have a nice solution).

Excel: Other problems

Some problems are not explained in this page (example: do not use AddMonth, use EDate). If you want me to help you, send a mail to nielsATnvvDOTname(replace art by '@ 'and dot by '.').
If you know other problems, you can also send me a quick email, I will add it here. Thanks.
Niels van Vliet

FIXME: To be moved somewhere else:

XmlSerializer won't serialize objects that implement IDictionary

Contact niels AT nvv.name for any suggestion.
Licence: GPL
Weblint Valid HTML 4.01 Transitional Valid CSS!