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:
|
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
|
* Shared COM Add-ins
|
**** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** *
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.
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.
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:
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!):
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
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”
3. Please navigate to C:\Test\tst\SDK\BootStraper\Packages\ and copy the folder KB908002.
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.
As you can see this KB will also install KB 907417.
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.
7. Deploy the add-in on a new test environment. The Shared COM add-in should load fine.
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).
If everything works according to plan, you should you should see this Hello World message!
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
Comments
- Anonymous
June 11, 2012
Very Nice and helpful article, I was looking for the same, as we have developed the Office Shared Add-in using office - 12, and now need to support office 11 also, we were facing the issues in redistributing Extensibility.dll with the installer. This article solved the problem !Thanks,Robin