Deploying a Shared COM add-in for Office 2003 (Visual Studio 2008 SP1) [and how to work around a known issue which causes the add-in to fail to load if KB908002 is not installed.]

 

Office add-ins can target an application (or many applications if they are Shared COM Add-ins) or only a document. The article https://msdn.microsoft.com/en-us/library/hy7c6z9k.aspx (Office Solutions Development Overview) describes the difference:

Choosing an Office Project Type

Visual Studio provides the following types of project templates for Office development:

  • Document-level customizations. This type of solution is associated with a specific document.
  •  Application-level add-ins. This type of solution is associated with the application itself.

    To decide which of these project types is best for your solution, think about whether you want your code to run only when a specific document is open, or whether you want the code to be available whenever the application is running. For more information about the project templates, see Office Project Templates Overview.

    The types of projects you can create depend on which Office applications you have installed on the development computer. For more information, see Features Available by Office Application and Project Type.

                Document-level customizations consist of an assembly that is associated with a single document, workbook, or template in Microsoft Office Word or Microsoft Office Excel. The assembly is loaded when the associated document is opened. Features in customizations that you create are available only when the associated document is open. Customizations cannot make application-wide changes, such as displaying a new menu item or Ribbon tab when any document is open.

    Visual Studio includes tools to help you create document-level customizations. The document that you customize is hosted as a design surface in Visual Studio, which enables you to design the document by dragging and dropping controls onto it. Many other Visual Studio features are available in document-level projects, such as Windows Forms controls, drag-and-drop data binding, and an integrated debugger.

    For more information about customizations, see the following topics:

  •  Getting Started Programming Document-Level Customizations for Excel
  •  Getting Started Programming Document-Level Customizations for Word
  •  Architecture of Document-Level Customizations

                Application-level add-ins consist of an assembly that is associated with a Microsoft Office application. Typically, the add-in runs when the associated application is started, although users can also load add-ins after the application is already running. Features in add-ins that you create are available to the application itself, regardless of which documents are open.

    Visual Studio includes tools to help you create add-ins. Add-in projects include an automatically generated class that represents the add-in. This class provides properties and events you can use to access the object model of the host application and run code when the add-in is loaded and shut down. Many other Visual Studio features are available in application-level projects, such as Windows Forms and an integrated debugger.

    For more information about add-ins, see the following topics:

  •  Getting Started Programming Application-Level Add-Ins
  •  Architecture of Application-Level Add-Ins

So what kind of project should you use? For our goal, we need to develop an application-level add-in.

But wait… there are 2 possible kinds of application-level add-ins ! VSTO Add-ins or Shared COM Add-ins

* VSTO Add-ins

How do I get to them in Visual Studio?

Start Visual Studio > selectthe File menu> New Project > from the Project Types window, select a programming language > expand Office tree > select the application version > choose from either an application level add-in (DLL);

* Shared COM Add-ins

 

  • https://msdn.microsoft.com/en-us/library/ms165620(VS.80).aspx  (Visual Studio Add-ins Versus Shared Add-ins);

    A single Shared add-in can be loaded only into Microsoft Office applications such as Microsoft Word, Microsoft Publisher, Microsoft Visio, and Microsoft Excel.

  • https://msdn.microsoft.com/en-us/library/aa289518(VS.71).aspx  (Tips and Tricks: Building Microsoft Office Add-ins with Visual C# .NET and Visual Basic .NET – because the same .DLL file is loaded for every targeted Office application, you need to determine which one is running in that instance: read this article to learn how do it);
  • https://support.microsoft.com/kb/302901  (How to build an Office COM add-in by using Visual C# .NET);

    How do I get to them in Visual Studio?

    Open Visual Studio and create a new project: go to Other Project Types and find Extensibility. Select Shared Add-in type in a name and click OK.


**** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** *

Important:      

Please note that due to a flaw in our product, any Shared COM add-in built with Visual Studio 2010 and targeting Office 2003 will not load on the client machine.

So what happens with the add-in? Answer: Nothing appears when you open the target application, you only notice that the LoadBehavior gets set to 2.

If you use this article: https://blogs.msdn.com/b/vsod/archive/2008/04/22/troubleshooting-com-add-in-load-failures.aspx (Visual Studio Office Development (VSOD) Support Team: Troubleshooting COM Add-In load failures) and enable Fusion Log Viewer, you will get these files (please note that these files are taken from the <FusionLogPath>\NativeImage folder):

> ExplicitBind!FileName=(Test2003SharedCOMAddIn_Wd_XL_PPT.dll).HTM

*** Assembly Binder Log Entry ***

The operation was successful. Bind result: hr = 0x0. The operation completed successfully.

Assembly manager loaded from: c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\mscorwks.dll Running under executable C:\Program Files\Microsoft Office\OFFICE11\WINWORD.EXE --- A detailed error log follows.

=== Pre-bind state information === LOG: User = V-ICRISB13\Administrator LOG: Where-ref bind. Location = C:\Program Files\ms\Test2003SharedCOMAddIn_Wd_XL_PPTSetup\ Test2003SharedCOMAddIn_Wd_XL_PPT.dll LOG: Appbase = file:///C:/Program Files/Microsoft Office/OFFICE11/ LOG: Initial PrivatePath = NULL LOG: Dynamic Base = NULL LOG: Cache Base = NULL LOG: AppName = WINWORD.EXE Calling assembly : (Unknown). === WRN: Native image will not be probed in LoadFrom context. Native image will only be probed in default load context, like with Assembly.Load().

> Extensibility, Version=7.0.3300.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a.HTM

*** Assembly Binder Log Entry ***

The operation was successful. Bind result: hr = 0x0. The operation completed successfully.

Assembly manager loaded from: c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\mscorwks.dll Running under executable C:\Program Files\Microsoft Office\OFFICE11\WINWORD.EXE --- A detailed error log follows.

=== Pre-bind state information === LOG: User = V-ICRISB13\Administrator LOG: DisplayName = Extensibility, Version=7.0.3300.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a (Fully-specified) LOG: Appbase = file:///C:/Program Files/Microsoft Office/OFFICE11/ LOG: Initial PrivatePath = NULL LOG: Dynamic Base = NULL LOG: Cache Base = NULL LOG: AppName = WINWORD.EXE Calling assembly : Test2003SharedCOMAddIn_Wd_XL_PPT, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null. === WRN: Native image will not be probed in LoadFrom context. Native image will only be probed in default load context, like with Assembly.Load().

> mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089

*** Assembly Binder Log Entry ***

The operation was successful. Bind result: hr = 0x0. The operation completed successfully.

Assembly manager loaded from: c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\mscorwks.dll Running under executable C:\Program Files\Microsoft Office\OFFICE11\WINWORD.EXE --- A detailed error log follows.

LOG: Start binding of native image mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089. LOG: Start validating native image mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089. LOG: Bind to native image succeeded.

>Test2003SharedCOMAddIn_Wd_XL_PPT, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null.HTM

*** Assembly Binder Log Entry ***

The operation failed. Bind result: hr = 0x80070002. The system cannot find the file specified.

Assembly manager loaded from: c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\mscorwks.dll Running under executable C:\Program Files\Microsoft Office\OFFICE11\WINWORD.EXE --- A detailed error log follows.

=== Pre-bind state information === LOG: User = V-ICRISB13\Administrator LOG: DisplayName = Test2003SharedCOMAddIn_Wd_XL_PPT, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null (Fully-specified) LOG: Appbase = file:///C:/Program Files/Microsoft Office/OFFICE11/ LOG: Initial PrivatePath = NULL LOG: Dynamic Base = NULL LOG: Cache Base = NULL LOG: AppName = WINWORD.EXE Calling assembly : (Unknown). === LOG: Start binding of native image Test2003SharedCOMAddIn_Wd_XL_PPT, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null. WRN: No matching native image found.

 

…. And if you try to run a simple VBS script which tries to create an instance of the add-in, it will fail with the error code: 8013141A.

1

 

What is the cause? How to fix this?

        The issue occurs because the Extensibility.dll is not installed in the GAC on the end-user’s machine. Deploying KB 908002 and KB 907417 will fix this issue.   

        However KB 908002 will not install on a Visual Studio 2008 machine, as one of its prerequisite checks needs to detect Visual Studio 2005! To find out what has to be done follow the steps below.

**** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** *

 

Building a simple Shared COM add-in for Word, Excel, PPT 2003

1. Open Visual Studio 2008. Go to File > New > Project. From the Project Types tree expand Other Project Types and select Extensibility > Shared Add-in.

2

In the Project Name text box, input a string (ex: Test2003SharedCOMAddIn_Wd_XL_PPT).

2. In the first screen of the Shared Add-in Wizard, click Next > select Create an Add-in using Visual C# , and then click Next > clear all of the selections except Microsoft Word, Excel and PowerPoint and then click Next > type a Name and Description for the add-in, and then click Next > in the Choose Add-in Options screen, select "I would like my Add-in to load when the host application loads" and “My add-in should be available to all users… ”.

3. The wizard should offer to you a very basic COM add-in frame:

3

You can skip directly to step 4 or you can add a little more functionality to your code.

3.1    In a real-word application you would build a much more complex solution, but for our example, here is a sample code in C# that detects which application is getting loaded and creates a custom menu with a “Hello World” button.

In the Solution Explorer, right-click Project References then go ahead and add the Office Interop libraries for Word, Excel and Power Point (make sure you select the 11.0.0.0 versions!):

4

5

3.2 Paste the following code:

namespace Addin_WD_XL_PPT_Test {     using System;     using System.Windows.Forms;     using Extensibility;     using System.Runtime.InteropServices;     using MSWord = Microsoft.Office.Interop.Word;     using MSExcel = Microsoft.Office.Interop.Excel;     using MSPPT = Microsoft.Office.Interop.PowerPoint;     using Microsoft.Office.Core;     using System.Reflection;

    #region Read me for Add-in installation and setup information.     // When run, the Add-in wizard prepared the registry for the Add-in.     // At a later time, if the Add-in becomes unavailable for reasons such as:     //   1) You moved this project to a computer other than which is was originally created on.     //   2) You chose 'Yes' when presented with a message asking if you wish to remove the Add-in.     //   3) Registry corruption.     // you will need to re-register the Add-in by building the Addin_WD_XL_PPT_TestSetup project,     // right click the project in the Solution Explorer, then choose install.     #endregion

    /// <summary>     ///   The object for implementing an Add-in.     /// </summary>     /// <seealso class='IDTExtensibility2' />    [GuidAttribute("4C072E47-09B9-4794-B210-D57DA6329111"), ProgId("Test2003SharedCOMAddIn_Wd_XL_PPT.Connect")]     public class Connect : Object, Extensibility.IDTExtensibility2     {         private MSWord.Application wordApp;         private MSExcel.Application excelApp;         private MSPPT.Application pptApp;         private object addInInstance;

        //CommandBarPopup topLevelPopupMenuItem = null;         CommandBarButton btn = null;         CommandBar menuBar;         _CommandBarButtonEvents_ClickEventHandler btnHandler;

        Object missing = Missing.Value;

        public Connect()         {         }

        private void BuildMenu_Word()         {             try             {                 menuBar = (CommandBar)wordApp.CommandBars.Add("Test", missing, missing, true);                 // by-default the newly added commandbar is invisible !                 menuBar.Visible = true;                 btn = (CommandBarButton)menuBar.Controls.Add(MsoControlType.msoControlButton, missing, missing, missing, missing);                 btn.Caption = "MyButton";                 btn.FaceId = 59;                 btnHandler = new _CommandBarButtonEvents_ClickEventHandler(OnClick_ButtonWd);                 btn.Click += btnHandler;             }             catch (Exception e)             {                 MessageBox.Show(e.StackTrace + "\r\n" + e.Source + "\r\n" + e.Message);             }         }

        private void OnClick_ButtonWd(CommandBarButton ctrl, ref bool cancel)         {             MessageBox.Show("Hello World - Word!");         }

        private void BuildMenu_Excel()         {             try             {                 CommandBars oCommandbars = (CommandBars)excelApp.GetType().InvokeMember("CommandBars", BindingFlags.GetProperty, null, excelApp, null);                 menuBar = (CommandBar)oCommandbars.Add("Test", missing, missing, true);                 menuBar.Visible = true;

                btn = (CommandBarButton)menuBar.Controls.Add(MsoControlType.msoControlButton, missing, missing, missing, missing);                 btn.Caption = "MyButton";                 btn.FaceId = 59;                 btnHandler = new _CommandBarButtonEvents_ClickEventHandler(OnClick_ButtonXl);                 btn.Click += btnHandler;                 MessageBox.Show("Excel menu Built!");             }             catch (Exception e)             {                 MessageBox.Show(e.StackTrace + "\r\n" + e.Source + "\r\n" + e.Message);             }         }

        private void OnClick_ButtonXl(CommandBarButton ctrl, ref bool cancel)         {             MessageBox.Show("Hello World - Excel!");         }

        private void BuildMenu_PPT()         {             try             {                 CommandBars oCommandbars = (CommandBars)pptApp.GetType().InvokeMember("CommandBars", BindingFlags.GetProperty, null, pptApp, null);                 menuBar = (CommandBar)oCommandbars.Add("Test", missing, missing, true);                 // by-default the newly added commandbar is invisible !                 menuBar.Visible = true;                 btn = (CommandBarButton)menuBar.Controls.Add(MsoControlType.msoControlButton, missing, missing, missing, missing);                 btn.Caption = "MyButton";                 btn.FaceId = 59;                 btnHandler = new _CommandBarButtonEvents_ClickEventHandler(OnClick_ButtonPPT);                 btn.Click += btnHandler;                 MessageBox.Show("PPT menu Built!");             }             catch (Exception e)             {                 MessageBox.Show(e.StackTrace + "\r\n" + e.Source + "\r\n" + e.Message);             }         }

        private void OnClick_ButtonPPT(CommandBarButton ctrl, ref bool cancel)         {             MessageBox.Show("Hello World - PPT!");         }

        public string GetOfficeVersion()         {             string sVersion = String.Empty;             switch (wordApp.Version.ToString())             {                 case "7.0": sVersion = "95";                     break;                 case "8.0": sVersion = "97";                     break;                 case "9.0": sVersion = "2000";                     break;                 case "10.0": sVersion = "2002";                     break;                 case "11.0": sVersion = "2003";                     break;                 case "12.0": sVersion = "2007";                     break;                 case "14.0": sVersion = "2010";                     break;                 default: sVersion = "Too Old!";                     break;             }             return sVersion;         }

        public void OnConnection(object application, Extensibility.ext_ConnectMode connectMode, object addInInst, ref System.Array custom)         {             if (application is MSWord.Application)             {                 wordApp = (MSWord.Application)application;                 BuildMenu_Word();             }             if (application is MSExcel.Application)             {                 excelApp = (MSExcel.Application)application;                 BuildMenu_Excel();             }             if (application is MSPPT.Application)             {                 pptApp = (MSPPT.Application)application;                 BuildMenu_PPT();             }             addInInstance = addInInst;         }

        /// <summary>         ///     Implements the OnDisconnection method of the IDTExtensibility2 interface.         ///     Receives notification that the Add-in is being unloaded.         /// </summary>         /// <param term='disconnectMode'>         ///      Describes how the Add-in is being unloaded.         /// </param>         /// <param term='custom'>         ///      Array of parameters that are host application specific.         /// </param>         /// <seealso class='IDTExtensibility2' />         public void OnDisconnection(Extensibility.ext_DisconnectMode disconnectMode, ref System.Array custom)         {         }

        /// <summary>         ///      Implements the OnAddInsUpdate method of the IDTExtensibility2 interface.         ///      Receives notification that the collection of Add-ins has changed.         /// </summary>         /// <param term='custom'>         ///      Array of parameters that are host application specific.         /// </param>         /// <seealso class='IDTExtensibility2' />         public void OnAddInsUpdate(ref System.Array custom)         {         }

        /// <summary>         ///      Implements the OnStartupComplete method of the IDTExtensibility2 interface.         ///      Receives notification that the host application has completed loading.         /// </summary>         /// <param term='custom'>         ///      Array of parameters that are host application specific.         /// </param>         /// <seealso class='IDTExtensibility2' />         public void OnStartupComplete(ref System.Array custom)         {         }

        /// <summary>         ///      Implements the OnBeginShutdown method of the IDTExtensibility2 interface.         ///      Receives notification that the host application is being unloaded.         /// </summary>         /// <param term='custom'>         ///      Array of parameters that are host application specific.         /// </param>         /// <seealso class='IDTExtensibility2' />         public void OnBeginShutdown(ref System.Array custom)         {         }     } }

Please note that you must replace the line highlighted in yellow ([GuidAttribute("4C072E47-09B9-4794-B210-D57DA6329111"), ProgId("Test2003SharedCOMAddIn_Wd_XL_PPT.Connect")]) with the GUID and ProID generated by your Visual Studio Wizard.

4. Compile the code.

_

_

Building the Shared COM add-in setup project

We will use a little trick: add KB 908002 to the Visual Studio setup project prerequisites list (even though this hotfix would not deploy if executed as a stand-alone file as it is expecting Visual Studio 2005 on the target machine). But if we extract the contents of the .EXE using commandline parameters or retrieve the installed package from a machine already having VS 2005, then we can add it into our 2008 version.

1. First we must ensure that the Extensibility.DLL is getting installed into the GAC on the end-user’s machine.

Download the vs2005-Kb908002-enu-x86.exe file from here, place it in a temporary folder (like C:\Test) and open a CMD prompt. Next, run the following command to extract its contents:

C:\Test\vs2005-kb908002-enu-x85.exe /T:”C:\Test” /C

6.1

 

2. The .exe will be unpacked into 3 sub-components. We need to further unpack this .MSI archive:

C:\Test>msiexec /a bootstrapper.msi /qb TARGETDIR=”C:\Test\tst”

7 

 

3. Please navigate to C:\Test\tst\SDK\BootStraper\Packages\ and copy the folder KB908002.

8

4. Navigate to the folder C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bootstrapper\Packages and place the extracted KB908002 folder there.

5. Open Visual Studio 2008 (in case it was already working, please restart the application) and edit the Setup Project’s prerequisites.

9

As you can see this KB will also install KB 907417.

10

 

6. The KB908002 should now become available in the list. Please select its check-box and re-build your solution. Also make sure Visual Studio Tools for the Office system 3.0 Runtime entry is selected in the Prerequisites list and also that the Microsoft Office 2003 Primary Interop Assemblies is installed on the test machine.

11

 

7. Deploy the add-in on a new test environment. The Shared COM add-in should load fine.

12

 

Execute the Setup.exe (do not run the .MSI file as it will simply install the add-in and the prerequisites will not get deployed).

13

14

 

If everything works according to plan, you should you should see this Hello World message!

15

16

17

 

The End.

 

I hope you enjoyed my article.

For any questions, feel free to add a comment.

SharedCOM add-in deployment for Office 2003_v1.pdf