Saturday, August 06, 2005

.NET add-ins for both Office 2000 and XP/2003

My current project is implementing MSCRM and SharePoint with some enhancements such as archive documents and e-mails directly from Office/Outlook to SharePoint document archives integrated into the MSCRM account, contact and case modules. The enhancements are Office add-ins .NET components that we deliver as part of our MSCRM and/or SharePoint projects. The add-ins were implemented with the Office XP PIAs and as such support only Office XP and 2003 (version 10.x and 11.x) out-of-the-box.

This customer unfortunately has Office 2000 and Outlook 2000 (version 9.x), and has no money or willingness to upgrade to Office 2003. Thus, I had to figure out how to make the .NET add-ins support Office 2000. It turned out that this is actually quite possible, following these guidelines:

  • Use the Office XP PIAs, always install them to GAC (download from MSDN)
  • Use the Office object model interfaces, not the classes
  • Implement version switch code whenever Office 9 methods have different signatures than Office 10/11
  • Use .NET reflection and .GetType().InvokeMember(...) to call the Office 9 methods that are different from Office 10/11
Microsoft provides no PIAs for Office 2000, only for Office XP and for Office 2003.

The Office object model provides you with both interfaces and classes, typically in pairs. E.g. Application/ApplicationClass, Document/DocumentClass, MailItem/MailItemClass. Always declare your object references using the interface, not the class. The class is version specific, and you might get cast errors when your add-in runs on a different Office version.

Some methods have different number of parameters in Office 2000 than in XP/2003. This applies e.g. to Word.Documents.Open(...) and Word.Document.SaveAs(...). Thus, you cannot use these methods directly in your code as it will cause run-time errors when loaded in e.g. Word 2000. Note that the code will compile OK, afterall the code references the Office XP PIAs. Your code must check the version number at run-time and switch between calling the Office XP/2003 or the Office 2000 methods.

To be able to call some of the Office 2000 methods from your code, you must use .NET reflection to call the methods using .InvokeMember(...), which is similar to COM "late binding". The code will look like this:

string version = this._application.version;
if(version.StartsWith("9."))
{
Object[] params = new Object[]{fileUrl, Type.Missing, ...};
doc.GetType().InvokeMember("SaveAs", BindingFlags. InvokeMethod, null, doc, params);
}
else
{
doc.SaveAs(fileUrl, ref param1, ref param2, ...);
}

Deploying at this customer also required some changes to our installers. The .NET 1.1 add-ins (not VSTO 2005 add-ins) read settings from our Objectware.OfficeAddin.dll.config file which has to be installed to the Office executable folder, which differs between the versions:

C:\program files\microsoft office\
plus the applicable folder office\ or office10\ or office11\

In addition, this customer runs both Windows XP and Windows 2000, which requires the add-in registry settings to be modified to refer to the correct location of MSCorEE.DLL:

C:\winNT\system32\MSCorEE.DLL for Win2000Pro
C:\windows\system32\MSCorEE.DLL for WinXPPro

A final issue about using .NET web-services in your Office add-in: if the web-service uses serializable objects as parameters in the WSDL, the .NET framework will try to auto-generate and -compile classes for these objects on the client side at run-time. As your add-in runs within the Office process, this will most likely cause hard to track and resolve exceptions. I recommend using only basic .NET value types and array types (e.g. ArrayList) in any web-service that is to be consumed by an Office add-in.

Office 2000 object model reference at MSDN.
Office XP object model reference at MSDN.
Office 2003 object model reference at MSDN.
(Navigate to the VBA language reference using the treeview if the link does not)

Sunday, July 31, 2005

Summer holiday scope complete

My three weeks of summer holiday is over. It has been nice weather most of the time, only a few rainy days. The first two weeks in our boat down the Swedish west coast (bästkusten), with lazy days in the archipelago of Bohuslän. The last week included some slavery at home at the whim of my wife, plus a few days of trekking in Rondane.

Now it's back to work implementing MSCRM and SharePoint, but it is only six weeks until we go hunting grouse (Lagopus Lagopus) in
Finnmark.

PS! Congrats to my colleague Mads Nissen with becoming a Microsoft MVP.

Tuesday, July 05, 2005

Using SharePoint lists.asmx web service

SharePoint (WSS 2.0) provides several web services that gives you access to different parts of the object model and its data, without the need to use the object model directly and without worrying where the data is stored in a farm deployment. Getting started is a bit tricky, but here is a list of articles that will get you started:

In addition, these tools are priceless for helping with CAML and for viewing the real names of columns in the SharePoint list:

The real column names are those you see in the CAML output area of the U2U tool, and you must use these column names in the XML parameters of the web service methods (e.g. 'Name' is really called 'Title'). If you do not get the name right or reference the wrong list, you will get an error like this:

Exception of type Microsoft.SharePoint.SoapServer.SoapServerException was thrown.

<detail> <errorstring xmlns="...">No such field name.
No field was found with that name. Check the name, and try again.</errorstring><errorcode xmlns="...">0x81020014</errorcode</detail>

Note how easily Microsoft has left out an important piece of information: which field name ?

An important detail when programming against the SharePoint web services is to remember to set the .Url property of your web reference to point to the correct WSS team site. The lists.asmx web service is a virtual service and is by SharePoint magic available in all WSS sites, not only in the IIS virtual directory /_vti_bin/. In addition, remember to apply correct credentials for authentication.


Set the .Url like this (avoid getting //_vti_bin/):

_svcList.Url = siteUrl + "/_vti_bin/lists.asmx";
_svcList.Credentials = System.Net.CredentialCache.DefaultCredentials;

The standard SDK documentation of the lists web service is available on MSDN, along with the rest of the WSS web services.

Saturday, July 02, 2005

Finishing touches on VSTO-O deployment

My colleague Mads Nissen has posted additional details about how to deploy a VSTO Outlook 2003 solution to client PCs. The post shows how to use a custom action to set the CAS rights for the install directory, in addition to naming three more VSTO assembly dependencies beyond VSTAddin.DLL.

These details completes my previous posting on VSTO-O deployment.