Understanding the Life Cycle of an Add-in

When you create a new add-in, the wizard takes care of writing all the code that connects the add-in to the IDE. If you drill into your Solution Explorer, you'll see the Connect.vb or Connect.cs class file. This class is responsible for handling the interaction between your code and the instance of the IDE that the add-in is connecting to. Notice the namespaces referenced at the top of the class file:

  • Microsoft.Office.Core

  • Extensibility

  • System.Runtime.InteropServices

  • EnvDTE

These namespaces are unique to dealing with the extensibility objects in the IDE, and the InteropServices namespace handles the interaction with COM at runtime. All the IDE-specific objects are located in the EnvDTE namespace.

In the class declaration, GuidAttribute specifies the COM globally unique identifier (GUID) for this add-in. Each COM component must have a unique ID in the Registry; that unique ID lives with the component forever. The wizard automatically creates a unique GUID for each add-in that you create. The following snippet shows the GUID and the program ID attribute for the Visual Basic version of my add-in:

<GuidAttribute("72940252-6B6A-4F8D-8CCD-1F9B3CDFF892"),
        ProgIdAttribute("FirstAddIn_vb.Connect")> _
Public Class Connect

If no GUID exists, the add-in can't be installed into the Registry. When you use the Windows Installer deployment project that the Add-in Project Wizard created as part of this solution, it uses the GUID to register the add-in component with COM.

The Connect class itself implements two interfaces: IDTExtensibility2 and IDTCommandTarget. In COM, an interface represents the methods, properties, and events that are exposed to a caller of a COM component. The reason a GUID must exist for a COM component is to maintain the validity of the exposed interfaces. If the interface to a component changes, the GUID must change. An interface might change as the result of a method parameter value or a return value changing. This is one of the reasons COM was such a pain in the neck: It was too difficult to maintain a consistent interface without breaking the functionality of existing components.

By exposing the two aforementioned interfaces, the Connect class can directly interact with the events that occur within the IDE. Those events are what make up the life cycle of the add-in. The events that occur are

  • OnConnection

  • OnStartupComplete

  • OnAddInsUpdate

  • QueryStatus

  • Exec

  • OnBeginShutdown

  • OnDisconnection

Each event has a predefined method that you can write code for in the Connect class.

Note

Each of the methods for the life cycle of an add-in handles its corresponding event that fires in the Connect class. When I refer to the OnConnection method, I mean that the host application is firing that event and the method in the Connect class file is consuming that event.


Understanding the OnConnection Method

The OnConnection event is fired when the add-in is first loaded. When you used the wizard to create the add-in, you had the option of having the add-in load at the time Visual Studio .NET starts. Enabling that selection fires the OnConnection event. The OnConnection event also fires when the add-in is loaded through the Add-in Manager dialog and when the Connect property of the Connect class is set to true. This event sets the object instances that you use in either the Connect class itself or that you pass as a parameter to other classes that must use this instance hook. The key object being created in the OnConnection event is the applicationObject and the addInInstance object that you use to work with the IDE. This Visual Basic .NET code snippet is when the objects are actually created:

applicationObject = CType(application, EnvDTE.DTE)
addInInstance = CType(addInInst, EnvDTE.AddIn)

At this point, the IDE environment is set to an object and you can work with it.

The code in Listing 17.1 is the C# and Visual Basic .NET code that the wizard generates to make the connection into the IDE possible. The comments in the code will give you help on how to make sure that the OnConnection event fires.

Listing 17.1. Wizard-Generated Code for the OnConnection Event That Connects Your Add-in to the IDE
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, EnvDTE.DTE)
   addInInstance = CType(addInInst, EnvDTE.AddIn)

   If connectMode = Extensibility.ext_ConnectMode.ext_cm_UISetup Then
      Dim objAddIn As AddIn = CType(addInInst, AddIn)
      Dim CommandObj As Command

      ' When run, the Add-in wizard prepared the registry for the Add-in.
      ' At a later time, the Add-in or its commands may become
      ' unavailable for reasons such as:
      '   1) You moved this project to a computer other than the one
      ' it was originally created on.
      '   2) You chose 'Yes' when presented with a message
      ' asking if you wish to remove the Add-in.
      '   3) You add new commands or modify commands already defined.
      ' You will need to re-register the Add-in by building
      ' the FirstAddIn_vbSetup project,
      ' right-clicking the project in the Solution Explorer,
      ' and then choosing install.
      ' Alternatively, you could execute the ReCreateCommands.reg
      ' file the Add-in Wizard generated in
      ' the project directory, or run 'devenv /setup' from a command prompt.
      Try
       CommandObj = applicationObject.Commands.AddNamedCommand(objAddIn, _
            "FirstAddIn_vb", "FirstAddIn_vb", _
            "Executes the command for FirstAddIn_vb", _
            True, 59, Nothing, 1 + 2)

        CommandObj.AddControl(applicationObject.CommandBars.Item("Tools"))
      Catch e as System.Exception
      End Try
   End If
End Sub
						

public void OnConnection(object application,
    Extensibility.ext_ConnectMode connectMode,
          object addInInst, ref System.Array custom)
      {
         applicationObject = (_DTE)application;
         addInInstance = (AddIn)addInInst;
         if(connectMode ==
             Extensibility.ext_ConnectMode.ext_cm_UISetup)
         {
            object []contextGUIDS = new object[] { };
            Commands commands =
                   applicationObject.Commands;
            _CommandBars commandBars =
                   applicationObject.CommandBars;

            // When run, the Add-in wizard prepared the registry
            // At a later time, the Add-in or its commands may
            //    become unavailable for reasons such as:
            //   1) You moved this project to a computer other
            //        than which it was originally created on.
            //   2) You chose 'Yes' when presented with a
            //       message asking if you wish to remove the Add-in.
            //   3) You add new commands or modify
            //       commands already defined.
            // You will need to re-register the Add-in by
            // building the firstAddin_csSetup project,
            // right-clicking the project in the Solution Explorer,
            // and then choosing install.
            // Alternatively, you could execute the
            // ReCreateCommands.reg file the Add-in Wizard generated in
            // the project directory, or run 'devenv /setup'
            // from a command prompt.
            try
            {
               Command command =
                   commands.AddNamedCommand(addInInstance,
                         "firstAddin_cs", "firstAddin_cs",
                         "Executes the command for firstAddin_cs", true,
                   59, ref contextGUIDS,
                   (int)vsCommandStatus.vsCommandStatusSupported+
                   (int)vsCommandStatus.vsCommandStatusEnabled);
               CommandBar commandBar =
                   (CommandBar)commandBars["Tools"];
               CommandBarControl commandBarControl =
                  command.AddControl(commandBar, 1);
            }
            catch(System.Exception /*e*/)
            {
            }
         }
      }
						

After the OnConnection method fires, any menu items and Toolbox items that your add-in creates are added to the IDE. The next stage in processing for the add-in is the OnStartupComplete event.

Understanding the OnStartupComplete Method

The OnStartupComplete event fires when the host IDE is initialized. In Visual Studio .NET, this is when the Start Page can be seen. Any application initialization that must occur for your add-in can happen in the OnStartupComplete event. An example of this might be a custom project template. After the Visual Studio .NET IDE starts, you might display a custom project template options dialog, which in turn creates an application. A scenario like this essentially bypasses the normal flow that a developer would go through when creating a new project in Visual Studio .NET. Recall that you can write shared add-ins that interact with other IDEs, not just Visual Studio .NET. So, this event could serve as a general dialog that fires when any Office application starts.

Understanding the OnAddInsUpdate Method

The OnAddInsUpdate event doesn't exactly correspond to the life cycle of the add-in, but it's important to the events that occur within the Connect class file. When an end user modifies the loaded add-ins through the Add-in Manager, the OnAddInsUpdate method fires in all other add-ins, not the add-in that's being loaded or unloaded. This means if your add-in is dependent on another add-in, you can trap the unloading or loading of a specific add-in.

For example, assume that you have two add-ins: a FileTransfer add-in and a FileMonitor add-in. The FileMonitor add-in might monitor the bytes being sent during a FileTransfer method. If the FileTransfer add-in is unloaded, there's no reason to continue to watch bytes with the FileMonitor add-in. In that case, the FileMonitor add-in would trap the OnAddInsUpdate event for the unloading of the FileTransfer add-in and either stop watching for bytes or unload itself. Conversely, you could automatically load the FileTransfer add-in whenever the FileMonitor add-in is loaded.

Understanding the OnBeginShutdown Method

When the host application that your add-in is running in begins to close, the OnBeginShutdown event occurs. This would occur if the user selects the File, Exit menu item or ends the process of the IDE. When this event fires, your add-in is still loaded, but the host is closing. This occurs all the time with add-ins. Because they're loaded most of the time, an end user never goes through the process of closing all the add-ins and then closing the application. The OnBeginShutdown event can occur more than once. For example, if the host (the IDE instance running the add-in) has a cancel event for a shut down, such as a prompt of “Are you sure you want to close?”, the event fires each time the host attempts to shut down.

Understanding the OnDisconnection Method

The OnDisconnection method handles the unloading of the add-in. This is the final event to occur in the lifetime of the add-in. It can occur via the Add-in Manager dialog when a user unchecks the Loaded option, when the IDE host is shutting down, or when the Connect property of the add-in is set to false. You can trap how the add-in is being disconnected from the host via the ext_dm constant. This constant is a parameter passed to the method from the IDE when disconnection occurs. Table 17.1 lists the values that you can trap when this event fires.

Table 17.1. Values of the ext_dm Constant
ConstantValueDescription
ext_dm_HostShutdown0The add-in was unloaded when the application was closed.
ext_dm_UserClosed1The add-in was unloaded when the user cleared its check box in the Add-In Manager dialog box, or when the Connect property of the corresponding AddIn object was set to false.
ext_dm_UISetupComplete2The add-in was unloaded after the environment setup completed and after the OnConnection method returned.
ext_dm_SolutionClosed3The add-in was unloaded when the solution was closed; received by solution add-ins.

In Listing 17.2, you can see the usage of the OnDisconnection event fired from the Extensibility.IDTExtensibility2.OnDisconnection event. The code listing demonstrates the VB.NET code; if you're writing in C#, the enumeration for the DisconnectMode is identical.

Listing 17.2. Using the DisconnectMode Enumeration to Determine How the Add-in Is Being Closed
Public Sub OnDisconnection(ByVal RemoveMode As _
      Extensibility.ext_DisconnectMode, _
      ByRef custom As System.Array) _
      Implements Extensibility.IDTExtensibility2.OnDisconnection

      Select Case RemoveMode
         Case ext_DisconnectMode.ext_dm_HostShutdown

         Case ext_DisconnectMode.ext_dm_SolutionClosed

         Case ext_DisconnectMode.ext_dm_UISetupComplete

         Case ext_DisconnectMode.ext_dm_UserClosed

         Case Else

      End Select
   End Sub

In this method, you can perform add-in–specific cleanup code, if necessary.

Understanding the Exec and QueryStatus Methods

The final two events that occur in the Connect class of your add-in don't have to do with the connection, startup, or shutdown of the add-in. They deal with events that fire while the add-in is running. The Exec method is the actual code you write to make up your add-in. In Listing 17.3, the commented code tells where your implementation would go.

Listing 17.3. The Exec Method in the Connect Class Where You Implement the Core Add-in Functionality
Public Sub Exec(ByVal cmdName As String, _
  ByVal executeOption As vsCommandExecOption, _
  ByRef varIn As Object, ByRef varOut As Object, _
  ByRef handled As Boolean) _
  Implements IDTCommandTarget.Exec

   handled = False
   If (executeOption = _
    vsCommandExecOption.vsCommandExecOptionDoDefault) Then
      If cmdName = "FirstAddIn_vb.Connect.FirstAddIn_vb" Then

         ' *******
         ' This is where your code goes that makes up your Add-in
         ' *******

         handled = True
         Exit Sub
      End If
   End If
End Sub
						

public void Exec(string commandName,
   EnvDTE.vsCommandExecOption executeOption,
   ref object varIn, ref object varOut, ref bool handled)
{
   handled = false;
   if(executeOption ==
        EnvDTE.vsCommandExecOption.vsCommandExecOptionDoDefault)
   {
      if(commandName == "firstAddin_cs.Connect.firstAddin_cs")
      {
         /*
          *  Your Code Goes Here!
          */
         handled = true;
         return;
      }
   }

The parameters that are passed to the Exec method determine how your code can react to the request to execute your Add-in code.

Based on the vsCommandExecuteOption parameter passed to the Exec method, your code can react to the Exec method in a number of ways. Table 17.2 lists the Constants and their descriptions of how the command should execute and how you would respond.

Table 17.2. VsCommandExecuteOption Constant Values
ConstantValueDescription
vsCommandExecOptionDoDefault0Performs the default behavior, whether prompting the user for input or not.
vsCommandExecOptionPromptUser1Executes the command after obtaining user input.
vsCommandExecOptionDoPromptUser2Executes the command without prompting the user. For example, choosing the Print button on the toolbar causes a document to print immediately, without user input.
vsCommandExecOptionShowHelp3Shows help for the corresponding command, if it exists, but doesn't execute the command.

Because your add-in can have a number of menu items or toolbar items, each command may be handled in any number of ways. Listing 17.4 demonstrates how you might handle different parameters being passed to the Exec method. As before, if you're writing in C#, the constants are the same.

Listing 17.4. Using the VSCommandExecOption Constants in the Exec Method
Public Sub Exec(ByVal cmdName As String, _
     ByVal executeOption As vsCommandExecOption, _
     ByRef varIn As Object, ByRef varOut As Object, _
     ByRef handled As Boolean) _
     Implements IDTCommandTarget.Exec

      handled = False
      If (executeOption = _
       vsCommandExecOption.vsCommandExecOptionDoDefault) Then
         If cmdName = "FirstAddIn_vb.Connect.FirstAddIn_vb" Then

            Select Case executeOption
               Case vsCommandExecOption.vsCommandExecOptionDoDefault

               Case vsCommandExecOption.vsCommandExecOptionDoPromptUser

               Case vsCommandExecOption.vsCommandExecOptionPromptUser

               Case vsCommandExecOption.vsCommandExecOptionShowHelp

            End Select

            ' *******
            ' This is where your code goes that makes up your Add-in
            ' *******

            handled = True
            Exit Sub
         End If
      End If
   End Sub

The Boolean value Handled indicates whether the code in your Exec method was successfully handled. The QueryStatus method can check the status of your add-in. In this method, you can see whether the various command items of your add-in are available, visible, enabled or disabled, or hidden.

Now that you have an idea of the different methods and events that can occur in an add-in, you must write some code that takes advantage of the extensibility features in Visual Studio .NET.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.14.144.216