Writing a COM AddIn Using Visual Studio


Writing a class that implements IDTExtensibility2 is not particularly difficult, but setting up the registry settings for the application you are targeting and creating the setup package for the COM add-in can be tricky. Luckily, Visual Studio provides a wizard that makes writing COM add-ins considerably easier. The wizard creates two projects: one for implementing the COM add-in and a separate setup project for the COM add-in. The COM AddIn Wizard has actually been part of Visual Studio since version 7.0, but you might not have come across it because it is somewhat hidden in the project hierarchy and listed as a "Shared AddIn" project.

The wizard can be displayed by choosing Other Project Types > Extensibility > Shared AddIn and is shown in Figure 23.6. The only clue that the Shared AddIn project might have something to do with Office is the Office icon included on the Shared AddIn icon.

Figure 23.6. Creating a Shared AddIn project in Visual Studio.


The Shared AddIn Wizard steps you through the process of creating a COM add-in. One of the advantages of a generic interface such as IDTExtensibility2 is that it can be used from just about any application that has a COM object model, and as a result, all the Office applications support loading IDTExtensibility2 COM add-ins. The wizard enables you to select the Office application that you want your COM add-in to load into, as shown in Figure 23.7. If you select the check boxes next to multiple Office applications, Visual Studio will register your COM add-in in a way that enables the same COM add-in to load in multiple Office applications.

Figure 23.7. Selecting the application host.


Although it is possible to write a single COM add-in that works in all the Office applications, it actually is quite difficult to write and even more difficult to maintain, because bugfixing different application behavior often leads to the code in the COM add-in's becoming overcomplicated. If you want to be able to share code among COM add-ins, creating a common library called by an applicationspecific COM add-in provides a more manageable solution.

In this example, the application host for the COM add-in will be Microsoft Word. Selecting Microsoft Word in the wizard will result in the setup project's registering the COM add-in in the correct location for Word so that you do not have to worry about dealing with the registry when you run the project. The registry settings for the COM add-in require a name and description, and this information is collected in the next step of the wizard, as shown in Figure 23.8.

Figure 23.8. Setting a name and description for a COM add-in.


The final step of the wizard is used to determine the load behavior of the COM add-in and whether the COM add-in will be installed in HKEY_CURRENT_USER or HKEY_LOCAL_MACHINE. As mentioned before, it is preferable to register the COM add-in in HKEY_CURRENT_ USER so that it will be visible in the COM AddIns dialog box. Leaving the second check box in Figure 23.9 unchecked will ensure this behavior.

Figure 23.9. Setting load behavior for the COM add-in.


When the wizard has finished, a solution is created in Visual Studio containing the two projects, as shown in Figure 23.10. The main COM add-in project is a standard Visual Basic class library project that has been pre-populated with the core references required and a class called Connect in a Connect.vb file that has a basic implementation of the IDTExtensibility2 interface. The setup project will create an installer for the COM add-in that will include all the dependencies detected and will register the COM add-in in the registry.

Figure 23.10. The Solution Explorer view of a default COM add-in solution.


Changing the COM AddIn Project to Be More OfficeSpecific

The COM AddIn Wizard will create a project for any application that supports IDTExtensibility2, and as a result, it creates a very generic project. The whole point of writing a COM add-in is to integrate with a particular Office application, so the first thing you need to do is add the appropriate primary interop assembly (PIA) for the application the COM add-in is targeting. The COM add-in being built in this example will load into Microsoft Word, so it needs to have a reference to the Word PIA. Then you will be able to cast the application object passed in OnConnection to the Microsoft.Office.Interop.Word.Application object defined in the Word PIA.

Adding the PIA for Word to a project is quite straightforward; it is just a matter of adding the reference to the Microsoft Word 11 Object Library. Rightclick the WordAddin project node in the Solution Explorer tree view, and choose Add Reference. Doing so brings up the Add Reference dialog box, shown in Figure 23.11. Click the COM tab and then select the Microsoft Word 11 Object Library from the list. Finally, click OK to add a reference to the Word PIA to your Visual Studio project.

Figure 23.11. Adding a reference to the Word PIA.


The Connect class that is created by the wizard contains untyped code, so a few changes need to be made to make it more Wordaware. By default, the project sets up two member variables within the class that are of type Object. The addInInstance variable can be redeclared as type Microsoft.Office.Core.COMAddin object, as defined by the Microsoft Office 11.0 Object Library PIA. After you have typed the addInInstance variable as a COMAddin object, you can use it to determine the registry settings for the COM add-in, such as the GUID, the ProgID, and the description. It also has a Connect property of type Boolean that can be set to False to disconnect the COM add-in.

The applicationObject member variable is also of type Object. Because this COM add-in will only ever run inside Word, it can be safely redeclared as type Microsoft.Office.Interop.Word.Application. Making this change will make developing considerably easier and safer. After you change the declaration of the applicationObject variable, all that remains is to change the assignment lines within the OnConnection method to cast the application argument using CType from Object to Microsoft.Office.Interop.Word.Application and the addInInst to Microsoft.Office.Core.COMAddin. Listing 23.3 shows the redeclaration of the addInInstance and applicationObject variables, along with the new casts in OnConnection.

Listing 23.3. Strongly Typing applicationObject and addInInstance

Public Sub OnConnection(ByVal application As Object, _   ByVal connectMode As Extensibility.ext_ConnectMode, _   ByVal addInInst As Object, ByRef custom As System.Array) _   Implements Extensibility.IDTExtensibility2.OnConnection     applicationObject = CType(application, _       Microsoft.Office.Interop.Word.Application)     addInInstance = CType(addInInst, _       Microsoft.Office.Core.COMAddIn) End Sub Private applicationObject As Word.Application Private addInInstance As Microsoft.Office.Core.COMAddIn 


Setting the Start Action

The COM add-in is almost ready to go. The last problem to solve is that the project is set to start up a new instance of Visual Studio rather than Word. This is easily solved by changing the debug settings for the project so that the project will start winword.exe rather than devenv.exe. To do this, bring up the properties for the project by doubleclicking the Properties project item in the Solution Explorer window; then select the Debug tab. Doing so brings up the dialog box with a Start Action section, as shown in Figure 23.12.

Figure 23.12. The Start Action section of the Debug tab.


The start action for the project should be set to Start External Program. This value needs to change to the location of the Word process on your machinetypically, C:\Program Files\Microsoft Office\Office11\winword.exe. Now when you run the project, Word will be started rather than a new instance of Visual Studio.

Word will reuse existing running instances of Word when you run the project. This can cause problems with COM add-in development. If an instance of Word is already running when you run the project, the debugger will attach to that running instance, but your COM add-in will not be loaded into that alreadyrunning instance. A way to ensure that the COM add-in will always be loaded in a new instance of Word is to pass the commandline switch /w, which will cause Word always to start a new instance.

Excel automatically creates a new instance if you start it at the command line, so there is no need to do this trick for Excel. Outlook is a singleinstance application without the ability to override this behavior, so when programming against Outlook applications, you need to shut down Outlook after every run of the project.

When a COM AddIn Project Stops Working

A common issue that occurs in COM add-in development goes like this: "I just pressed F5 on my COM add-in project, and nothing happened! My COM add-in doesn't appear to load. What's the deal?" Office has a system to protect itself from COM add-ins that fail. When you understand the system, you will better understand how to protect against your COM add-in's not loading.

Office automatically disables a COM add-in if it detects that it crashed the host application while starting. When the Office application loads and starts a COM add-in, it puts a sentinel in the registry associated with the COM add-in that it is loading. Then it calls the COM add-in's OnConnection and method. If the COM add-in successfully returns from this method, Office removes the sentinel in the registry, and everything works fine. If the COM add-in crashes in OnConnection, or if you stop debugging and kill the Office process before OnConnection returns, the sentinel is still sitting in the registry. When you relaunch the Office application, Office detects that a sentinel got left in the registry on the last run, and it disables your COM add-in.

It is very easy to have this happen during development; you might be stepping through code invoked by your OnConnection entry point, and you get to a line of code and say to yourself, "This line of code is completely wrong." You stop debugging, change the code, and press F5 to rerun the COM add-in. But on the second run, the COM add-in does not work. Office detects the sentinel in the registry left over from the last run, when you killed the process in the middle of OnConnection, and it disables your COM add-in.

The situation is even worse for unshimmed managed COM add-ins. The sentinel put in the registry for a managed COM add-in is the name of the DLL that bootstraps the COM add-in. In the case of a nonshimmed COM add-in, the bootstrap DLL is always mscoree.dlla component of the CLR. mscoree.dll acts as a class factory to create COM objects implemented in managed code for a host such as Office that expects a COM object that implements IDTExtensibility2. It bootstraps the CLR into the Office application process; loads the managed COM add-in registered in the registry; and gives the Office application the managed COM add-in class that implements IDTExtensibility2 and, through interop, makes that class looks like a COM object to Office.

So suppose that you have two add-in projects: Addin1 and Addin2, both of which are unshimmed. You are debugging Addin1's OnConnection handler, and you hit Stop Debugging in the middle of it. This leaves the sentinel in the registry saying not that Addin1.dll crashed Office, but that mscoree.dll crashed Office. Now you open the Addin2 project and run it, and because Addin2 is also registered with mscoree.dll as its class factory, both Addin1 and Addin2 (and any other unshimmed managed add-ins) will be disabled.

To reenable a COM add-in that has been disabled, go to the Help > About box of the Office application, and click the Disabled Items button. Doing so pops up a dialog box that will let you reenable mscoree.dll for an unshimmed add-in or, for a shimmed add-in, the DLL that is shimming the add-in.

There is a second way your COM add-in can get disabled. If your COM add-in throws an exception in OnConnection code and does not catch it, that exception propagates out to Office, and Office disables the COM add-in by setting the LoadBehavior key to 2 (HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\<<Application Name>>\Addins\<<Addin ProgID>>\LoadBehavior). There is an easy way to deal with this issue. Always put your code that handles OnConnection inside a TRy Catch block. Do not leak any exceptions in OnConnection back to Office. To undisable a COM add-in that has been disabled in this way, you can change the LoadBehavior key back to 3 using regedit.exe or reenable the COM add-in using the COM AddIns dialog box.

A Simple Word COM AddIn

To understand what is possible with COM add-ins in Office applications, refer to chapters on the object models of Excel (Chapters 35), Word (Chapters 68), and Outlook (Chapters 911). To show that the COM add-in being developed actually works, let's add some code to the OnStartupComplete method of the COM add-in, as shown in Listing 23.4. The code will use the application object to add a button to the standard command bar in Word and show a message box when a user clicks the button.

Listing 23.4. A Simple Word COM AddIn

Imports Extensibility Imports System.Runtime.InteropServices Imports Microsoft.Office.Core Imports Word = Microsoft.Office.Interop.Word <GuidAttribute("F91A3358-8DDB-4F6C-850D-7B79CD6F3310"),_ ProgIdAttribute("WordAddin2.Connect")>_ Public Class Connect   Implements Extensibility.IDTExtensibility2   Private applicationObject As Word.Application   Private addInInstance As Microsoft.Office.Core.COMAddIn   Private WithEvents simpleButton As CommandBarButton   Public Sub OnStartupComplete(ByRef custom As System.Array) _     Implements Extensibility.IDTExtensibility2.OnStartupComplete     Dim commandBars As CommandBars     Dim standardBar As CommandBar     commandBars = applicationObject.CommandBars     ' Get the standard CommandBar from Word     standardBar = commandBars("Standard")     Try       ' try to reuse the button if it is not deleted       simpleButton = CType(standardBar.Controls( _         "Word Addin"), CommandBarButton)     Catch       ' If it is not there, add a new button       simpleButton = CType(standardBar.Controls.Add(1), _         CommandBarButton)       simpleButton.Caption = "Word Addin"       simpleButton.Style = MsoButtonStyle.msoButtonCaption     End Try     ' Make sure the button is visible     simpleButton.Visible = True     standardBar = Nothing     commandBars = Nothing   End Sub   Private Sub simpleButton_Click( _     ByVal Ctrl As Microsoft.Office.Core.CommandBarButton, _     ByRef CancelDefault As Boolean) Handles simpleButton.Click     MsgBox("You clicked on the button")   End Sub   Public Sub OnConnection(ByVal application As Object, _     ByVal connectMode As Extensibility.ext_ConnectMode, _     ByVal addInInst As Object, ByRef custom As System.Array) _     Implements Extensibility.IDTExtensibility2.OnConnection     applicationObject = CType(application, _       Word.Application)     addInInstance = CType(addInInst, _       Microsoft.Office.Core.COMAddIn)   End Sub End Class 





Visual Studio Tools for Office(c) Using Visual Basic 2005 with Excel, Word, Outlook, and InfoPath
Visual Studio Tools for Office: Using Visual Basic 2005 with Excel, Word, Outlook, and InfoPath
ISBN: 0321411757
EAN: 2147483647
Year: N/A
Pages: 221

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net