Jeff Webb, Mike McKelvy, Ronald Martinsen, Taylor Maxwell, Michael Regelski September 1995 Special Edition Using Visual Basic 4 - Chapter 24 1-56529-998-1 Computer Programming computer programming Visual Basic OLE database applications ODBC VB VBA API This book is an all-in-one reference that provides extensive coverage of every topic and technique for creating optimized and customized applications with Visual Basic.

Chapter 24

Creating Add-Ins


Visual Basic 4.0 introduces add-ins. Add-ins are to the Visual Basic programming environment what custom controls are to your applications: pieces of compiled functionality that you can simply plug in and use. Unlike custom controls, however, add-ins can be created by the programmer right in Visual Basic. This chapter explains how to create add-ins in Visual Basic.

In this chapter, you learn about the following:

Overview

The Visual Basic Add-In Manager loads add-in files into the Visual Basic programming environment. Once loaded, an add-in can extend Visual Basic in three ways: by providing new items on the Add-Ins menu; by providing custom templates for new forms; or by adding special features, such as automatic source control.

You can create add-ins using Visual Basic Professional or Enterprise Editions. Add-ins created in Visual Basic include special methods in a public, creatable class. When you load an add-in, Visual Basic starts the add-in's .EXE file and connects it to specific Visual Basic events. Classes in the add-in's executable can respond to these events and interact with Visual Basic through objects that the Visual Basic programming environment provides.

The add-in's executable file stays loaded in memory until Visual Basic quits or the user unloads the add-in from the Add-In Manager. During this time, the add-in can respond to certain user events as they occur in Visual Basic. These events include saving files, adding new forms, and clicking on certain menu items. Figure 24.1 shows how an add-in interacts with Visual Basic.

Fig. 24.1

Once loaded, add-ins can use Visual Basic add-in objects and respond to events in the programming environment.

Loading an Existing Add-In

To load an add-in in Visual Basic, follow these steps:

  1. Choose Add-Ins, Add-In Manager. Visual Basic displays the Add-In Manager dialog box (see fig. 24.2).

Fig. 24.2

The Add-In Manager displays the available add-ins.

  1. Select the check box next to the name of the add-in to load. Then click OK. Visual Basic loads the selected add-in.

After you select add-ins in the Add-In Manager, they load each time that you start Visual Basic. The [add-ins16] or [add-ins32] section of the VB.INI file stores these selections, as follows:

[add-ins16]
RegEditAddin.Application=1

The name of the add-in (RegEditAddin.Application) shown in the preceding example is the programmatic ID of the add-in as it is registered in the system registration database. The setting for each add-in (0 or 1) indicates whether the add-in is loaded.

An add-in might be registered in the system registration database, but if the add-in is not listed in the add-ins section of VB.INI, it does not appear in the Add-In Manager.

Objects That Add-Ins Use

Visual Basic provides an object library, VBEXT.OLB, that you can use to extend the Visual Basic programming environment. Figure 24.3 shows the hierarchy of the objects that Visual Basic provides. Table 24.1 describes the tasks that you perform with each of the objects.

Fig. 24.3

The Visual Basic add-in object hierarchy.

Table 24.1 VBEXT Objects by Task

TaskObjectDescription
Load an add-in AddInManager Triggers ConnectAddin and DisconnectAddin methods in your add-in. You use these events to activate and deactivate your add-in when loaded in Visual Basic.
Application Provides access to all the subordinate objects. You get a reference to the Application object from the ConnectAddin method in your add-in.
Add items to the Add-Ins menu Menu Controls the display of items in the Add-Ins menu.
MenuItems Adds or removes menu items or menus from the Add-Ins menu.
MenuLine Controls the text displayed for a menu item in the Add-Ins menu. Also defines the action taken when the user clicks the menu item.
Control the active project ProjectTemplate Controls the active project by providing access to all the subordinate objects.
SelectedComponents Provides access to all the form, class, and code modules in the active project.
Component Controls a form, class, or code module.
FormTemplate Provides access to the controls on a form.
ControlTemplates Adds new controls to a form; returns ControlTemplate objects for each of the controls on a form.
ControlTemplate Provides access to the Properties collection of a control on a form.
Properties Provides access to each of a control's property settings.
Property Sets or returns the setting of a control's property.
Respond to File menu events FileControl Triggers methods in your add-in when the user chooses any of the items from the Visual Basic File menu.

Add-In Object Events

Three add-in objects expose events that you can detect:

Table 24.2 lists the add-in object events and describes when each occurs.

Table 24.2 Add-In Object Events

ObjectEventTrigger
AddInManager ConnectAddIn Visual Basic loads the add-in.
DisConnectAddIn Visual Basic unloads the add-in or Visual Basic quits.
MenuLine AfterClick The user clicks on the menu item.
FileControl AfterAddFile The user adds a form, code, class, resource, or .VBX file to a project.
AfterChangeFileName Visual Basic saves a file, renames a file, or creates an .EXE file.
AfterCloseFile The project file closes. This includes a project file closing because Visual Basic is quitting.
AfterRemoveFile The user removes a form, code, class, resource, or .VBX file from a project.
AfterWriteFile A project, form, code, or class file is written to disk.
BeforeLoadFile Visual Basic loads a project form, code, class, resource, .FRX, or .VBX file.
DoGetAddFileName The user chooses File, Add.
DoGetNewFileName The user chooses File, Save File or File, Save Project for a new file; or the user chooses the File menu's Save File As, Save Project As, or Make EXE File for an existing file.
DoOpenProjectName The user chooses File, Open Project.
RequestChangeFileName Visual Basic prepares to rename a project, form, class, or code module file to disk.
RequestWriteFile Visual Basic prepares to save a project, form, class, or code module file to disk.

Connecting to an Object's Event

The AddInManager object automatically associates two, special method names with its events: ConnectAddIn and DisconnectAddIn. If you provide methods with these names in a public, creatable class, the Add-In Manager creates an instance of that object and invokes the ConnectAddIn method when the add-in is loaded. The ConnectAddIn method has the following form:

Public Sub ConnectAddIn(Application As Object)
' Your initialization code goes here.
End Sub

The Application argument is a passed-in reference to the current instance of the Visual Basic Application object. This reference is your one opportunity to access the Application object, so you must use this reference to derive all subsequent object references in your add-in.

The Add-In Manager invokes the DisconnectAddIn method just before it unloads the add-in. The add-in might unload if a user deselects the add-in in the Add-In Manager or if Visual Basic quits. The DisconnectAddIn method has the following form:

Public Sub DisconnectAddIn(Mode As Integer)
' Your deinitialization code goes here.
End Sub

The Mode argument indicates why the add-in is unloading: 0 indicates that Visual Basic is quitting, and 1 indicates that the user deselected the add-in in the Add-In Manager. Use the DisconnectAddIn method to unload any forms that your add-in uses and to save any settings that you want to preserve.

To detect the events for MenuLine or FileControl objects, you must create an association between the event and the object that you've created to respond to the event. The add-in object's ConnectEvents method creates this association. The following code shows how to use the ConnectAddIn event to add a line to the Add-Ins menu. The code associates the new line with the AfterClick method in this object.

' Initialize the add-in.
Public Sub ConnectAddIn(Application As Object)
' Create a new MenuLine object.
Set mnuitCreateDirectory = Application. _
AddInMenu.MenuItems.Add("Create Directory")
' Associate the MenuLine AfterClick event
' with this object.
mnuitCreateDirectory.ConnectEvents Me
End Sub
' The AfterClick event procedure for the
' Create Directory menu item.
Public Sub AfterClick()
' Display the Create Directory form.
frmCreateDirectory.Show vbModal
End Sub

The object that you associate with an event must exist before you call ConnectEvents. The following code shows how to create an instance of an object before associating the object with a FileControl event:

' Application class -- APLICAT.CLS
'
' Creatable = Tru
' Public = True
' Initialization method.
Public Sub ConnectAddIn(Application As Object)
' Create a new instance of an object
' to receive file control events.
Dim objFileControl As New clsFileControl
' Connect the VB FileControlEvents to the object.
Application.FileControl.ConnectEvents objFileControl
End Sub

Listing 24.1 shows the class definition for the clsFileControl object. This object simply displays a message box indicating when a file event occurs in Visual Basic. You can use this class as a template for your own file control object.

Listing 24.1 The Class Definition for the clsFileControl Object

' clsFileControl class -- FILEVENTS.CLS
'
' Creatable = False
' Public = True
Option Explicit
Public Sub AfterAddFile(FileName As String)
MsgBox "AfterAddFile"
End Sub
Public Sub AfterChangeFileName(FileType As String, _
NewName As String, OldName As String)
MsgBox "AfterChangeFileName"
End Sub
Public Sub AfterFileClose(FileNames() As String)
MsgBox "AfterChangeFileName"
End Sub
Public Sub AfterRemoveFile(FileNames() As String)
MsgBox "AfterRemoveFile"
End Sub
Public Sub AfterWriteFile(FileName As String, Success As Integer)
MsgBox "AfterWriteFile"
End Sub
Public Sub BeforeLoadFile(FileNames() As String)
MsgBox "BeforeLoadFile"
End Sub
Public Sub DoGetAddFileName(FileNames() As String, _
Cancel As Boolean)
MsgBox "DoGetAddFileName"
End Sub
Public Sub DoGetNewFileName(FileType As Integer, _
NewName As String, OldName As String, Cancel As Boolean)
MsgBox "DoGetNewFileName"
End Sub
Public Sub DoGetOpenProjectName(ProjectName As String, _
NewName As String, OldName As String, Cancel As Boolean)
MsgBox "DoGetOpenProjectName"
End Sub
Public Sub DoRequestChangeFileName(FileType As Integer, _
NewName As String, OldName As String, Cancel As Boolean)
MsgBox "DoRequestChangeFileName"
End Sub
Public Sub RequestChangeFileName(FileType As Integer, _
NewName As String, OldName As String, Cancel As Boolean)
MsgBox "RequestChangeFileName"
End Sub
Public Sub RequestWriteFile(FileName As String, Cancel As Boolean)
MsgBox "RequestWriteFile"
End Sub

Navigating the Add-In Object Hierarchy

The add-in objects don't closely follow the OLE naming conventions for collections. Therefore, you sometimes must use nonobvious method and property names when navigating down the add-in object hierarchy. Table 24.3 shows the properties and methods that you use to navigate down each level in the add-in object hierarchy.

Table 24.3 Properties and Methods Used to Navigate down the Add-In Object Hierarchy

From Object To Object Use This Property/Method
AddInManager Application None; an Application object is passed in as the argument to the ConnectAddin method

Application Menu AddInMenu property
Menu MenuItems MenuItems method
MenuItems MenuLine Item method (default method)
MenuItems Menu Item method (default method)

Application ProjectTemplate ActiveProject property
ProjectTemplate SelectedComponents SelectedComponents property
SelectedComponents Component ActiveForm property or
Item method (default method)
Component ControlTemplates ControlTemplates method orSelectedControlTemplates property
ControlTemplates ControlTemplate Item method (default method)
ControlTemplate Properties Properties method
Properties Property Item method (default method)

Application FileControl FileControl property

To navigate up the add-in object hierarchy, use the Parent or Application properties. All add-in objects provide these properties.

Creating an Add-In

To create an add-in, write code to perform the following actions:

  1. Add the add-in's programmatic ID to the VB.INI file so that it is available from the Add-In Manager.
  2. Initialize the add-in when Visual Basic loads it.
  3. Respond to menu and file events in Visual Basic.
  4. Deinitialize the add-in when it is unloaded or when Visual Basic quits.

The following sections explain each of these steps in greater detail.

Installing the Add-In

Add-ins must have at least one class with Creatable and Public properties set to True. Typically this class is called Application and provides the programmatic ID that you install in the VB.INI file. Visual Basic doesn't provide a built-in way to add entries to the .INI files, so you must use the Windows API function WritePrivateProfileString to perform this task.

Listing 24.2 shows a Main procedure that installs the add-in in the VB.INI file if you run the add-in .EXE file with the /install switch. This procedure demonstrates a handy way to distribute your add-ins, although it increases the code size slightly. You can use this Main procedure in your own add-ins simply by modifying the module-level constants and making it the startup procedure for your add-in.

Listing 24.2 Registering an Add-In with Visual Basic.

' Adds an entry to VB.INI that will add an add-in to
' VB's Add-Ins dialog box when VB is started.
Option Explicit
' Win API declaration used to write to VB.INI
#If Win16 Then
Declare Function WritePrivateProfileString Lib "Kernel" _
(ByVal lpApplicationName As String, _
ByVal lpKeyName As String, _
ByVal lpString As Any, _
ByVal lplFileName As String) _
As Integer
#ElseIf Win32 Then
Declare Function WritePrivateProfileString Lib "Kernel32" _
(ByVal lpApplicationName As String, _
ByVal lpKeyName As String, _
ByVal lpString As Any, _
ByVal lplFileName As String) _
As Integer
#End If
' Change to ProgID of your add-in's Application class.
Const AddIn = "RegEditAddin.Application"
' Change to "add-ins32" for 32-bit systems.
Const INISection = "add-ins16"
Const INIFile = "vb.ini"
Sub Main()
Dim strAddIn As String, strSetting As String
' If the add-in was launched with the \install
' switch, install the add-in in the VB.INI file.
If InStr(LCase(Command$), "install") Then
' Name of add-in = applicationname.classname
' 1 loads add-in, 0 does not load
strSetting = "1"
' Add entry to VB.INI.
If WritePrivateProfileString(INISection, AddIn, _
strSetting, INIFile) Then
' Success.
Else
' Notify user that add-in couldn't be registered.
MsgBox "Addin could not be registered in VB.INI. " & _
"Add these lines to VB.INI and " & _
restart Visual Basic: " & __
Chr(13) & _
INISection & Chr(13) & _
"[" & AddIn & "]" & "=" & strSetting, _
vbExclamation, "Install Addin"
End If
End If
End Sub

Initializing the Add-In

The Application class that provides the programmatic ID that you install in the VB.INI file must contain the ConnectAddIn and DisconnectAddIn methods. These methods initialize the add-in when it is loaded, and deinitialize it when it is unloaded or when Visual Basic quits.

The ConnectAddIn has one argument, Application, which passes in a reference to the instance of Visual Basic that is loading the add-in. You use this object reference in all subsequent access to Visual Basic objects, so you must preserve its value before the ConnectAddIn procedure ends and the argument loses scope. The best way to save the value of Application is as the Parent property of the add-in's Application object. This is consistent with the OLE standard for object navigation and enables you to get the Visual Basic Application object from all the other objects in your add-in.

Listing 24.3 shows the ConnectAddIn method for the Application class of the REGADDIN.VBP sample, a simple add-in that adds the Registration Info Editor to the Visual Basic Add-Ins menu.

Listing 24.3 The ConnectAddIn Method for REGADDIN.VBP's Application Class

' Application class -- APLICAT.CLS
' Creatable = True
' Public = True
Option Explicit
' Declaration for the menu line object to add.
Private mnuitRegedit As Object
' ConnectAddIn method
' Initializes the add-in
Public Sub ConnectAddIn(Application As Object)
' Set the Parent of this add-in
Set Me.Parent = Application
' Add a menu item to the Add-Ins menu
Set mnuitRegedit = Application.AddInMenu.MenuItems _
.Add("Registration Info Editor")
' Connect the menu item to this object.
mnuitRegedit.ConnectEvents Me
End Sub

The Parent property of the Application class is a write-once, read-always property. After initializing Parent, the ConnectAddIn method is effectively read-only. Listing 24.4 shows the definition for the Parent property of the Application class in the REGADDIN.VBP sample.

Listing 24.4 The Parent Property for REGADDIN.VBP's Application Class

' This variable declaration must appear at the beginning
' of the module (before executable code). Stores a private
' copy of the parent object.
Private mParent As Object
' Parent property (read always/write once).
Public Property Get Parent() As Object
' Return the parent object.
Set Parent = mParent
End Property
Public Property Set Parent(objSetting As Object)
If TypeName(mParent) = "Nothing" Then
Set mParent = objSetting
Else
' Can't reset.
Err.Raise 383, "Application object", _
"Parent property is read-only."
End If
End Property

Responding to Menu and File Events

After you use the ConnectEvents method to connect an object to a Visual Basic menu or file control events, Visual Basic runs the appropriate method in that object when an event occurs. The method name and parameter count and type must match the definition that Visual Basic expects for the particular method.

Listing 24.5 shows the AfterClick method included in the REGADDIN.VBP sample. This method resides in the Application class, which also contains the ConnectAddIn method that creates the association between the menu item and the current object.

Listing 24.5 The AfterClick Method for REGADDIN.VBP's Application Class

' This line must occur before executable code.
' Retains the handle used to shut down the application on exit.
Private mAppHandle As Integer
Public Sub AfterClick()
' Run the Registration Info Editor.
mAppHandle = Shell ("REGEDIT /V", 1)
End Sub

The AfterClick method is a very simple application of an add-in. It merely shells another application�in this case, one that you use quite often when working with OLE: the Registration Info Editor.

Listings 24.6 and 24.7 are the definitions for the menu and file control events in Visual Basic. You can change the name of the arguments that these definitions use, but not their number, order, or type.

Listing 24.6 Add-In Event Definitions for the MenuLine Object

Public Sub AfterClick()
End Sub

Listing 24.7 Add-In Event Definitions for the FileControl Object

Public Sub AfterAddFile(FileName As String)
End Sub
Public Sub AfterChangeFileName(FileType As String, _
NewName As String, OldName As String)
End Sub
Public Sub AfterFileClose(FileNames() As String)
End Sub
Public Sub AfterRemoveFile(FileNames() As String)
End Sub
Public Sub AfterWriteFile(FileName As String, Success As Integer)
End Sub
Public Sub BeforeLoadFile(FileNames() As String)
End Sub
Public Sub DoGetAddFileName(FileNames() As String, _
Cancel As Boolean)
End Sub
Public Sub DoGetNewFileName(FileType As Integer, _
NewName As String, OldName As String, Cancel As Boolean)
End Sub
Public Sub DoGetOpenProjectName(ProjectName As String, _
NewName As String, OldName As String, Cancel As Boolean)
End Sub
Public Sub DoRequestChangeFileName(FileType As Integer, _
NewName As String, OldName As String, Cancel As Boolean)
End Sub
Public Sub RequestChangeFileName(FileType As Integer, _
NewName As String, OldName As String, Cancel As Boolean)
End Sub
Public Sub RequestWriteFile(FileName As String, Cancel As Boolean)
End Sub

Use the DisconnectEvents method to remove the association between a menu or FileControl object and an object in your application. The DisconnectEvents method suspends event trapping for the specified object.

Deinitializing the Add-In

The DisconnectAddIn method in the add-in's Application class undoes the actions taken in the ConnectAddIn method. Before Visual Basic unloads the add-in, you want your add-in to remove any items that it added to the Add-Ins menu and unload any forms that it displayed.

Listing 24.8 is the DisconnectAddIn method in the REGADDIN.VBP sample's Application class. The method closes the Registration Info Editor before the add-in unloads and removes the Add-Ins menu item if the user deselected the add-in in the Add-In Manager.

Listing 24.8 The DisconnectAddIn Method in the REGADDIN.VBP Sample's Application Class

#Const Win32 = 1
' Declarations for functions used by DisconnectAddIn
#If Win16 Then
Private Declare Function PostAppMessage Lib "User" _
(ByVal htask As Integer, _
ByVal wMsg As Integer, _
ByVal wParam As Integer, _
lParam As Any) _
As Integer
Private Declare Function IsTask Lib "Kernel" _
(ByVal htask As Integer) _
As Integer
#If Win32 Then
Private Declare Function PostAppMessage Lib "User32" _
(ByVal htask As Long, _
ByVal wMsg As Long, _
ByVal wParam As Long, _
lParam As Any) _
As Long
Private Declare Function IsTask Lib "Kernel32" _
(ByVal htask As Long) _
As Long
#End If
Const WM_QUIT = &h12
' Deinitialize the add-in
Public Sub DisconnectAddIn(iConnect As Integer)
Dim iWorked As Integer
' If the handle to RegEdit is still valid.
If IsTask(mAppHandle) Then
' Tell RegEdit to close
iWorked = PostAppMessage(mAppHandle, WM_QUIT, 0, 0)
End If
Select Case iConnect
' Add-in disconnected because VB closed.
Case 0
' No extra work.
' Add-in disconnected because user deselected its
' check box in the Add-in Manager.
Case 1
' Remove menu item.
mnuitRegedit.Parent.Remove mnuitRegedit
End Select
' End AddIn
End
End Sub

Adding a Menu Item

Use the MenuItems collection's Add method to add an item to the Add-Ins menu. Visual Basic enables you to add items only to the Add-Ins menu. Other menus are off-limits for now.

The Add method returns a reference to the MenuLine object that you just added. You can use this reference in subsequent lines to associate the menu item with an object that detects the item's AfterClick event. The following line shows how to add a line displaying the text "Registration Info Editor" to the Add-Ins menu:

Set mnuitRegedit = Application.AddInMenu.MenuItems. _
Add("&Registration Info Editor")

After adding the MenuLine object, you can connect it to an object that contains the AfterClick method to respond to click events on the MenuLine object. The following line connects the current object to the MenuLine object's AfterClick event:

mnuitRegedit.ConnectEvents Me

You can also add submenus to the Add-Ins menu. A submenu is a menu that displays other menu items, which cascade downward as shown in figure 24.4.

Fig. 24.4

You can add cascading submenus to the Add-Ins menu.

Use the MenuItems collection's AddMenu method to add a submenu to the Add-Ins menu. The AddMenu method returns a reference to the Menu object that you just added. You can use this reference in subsequent lines to add MenuLines or additional submenus. The following line shows how to add to the Add-Ins menu a submenu that displays the text "Submenu1":

Set mnuSub1 = Application.AddInMenu.MenuItems.AddMenu("&Submenu1")

Submenus can't detect click events the same way that MenuLine objects can. To add menu lines to a submenu, use the Add method on the Menu object's MenuItems collection. The following line shows how to add the line "MenuLine1" to the submenu Submenu1, which the previous line added:

Set mnuitLine1 = mnuSub1.MenuItems.AddMenu("&MenuLine1")

Removing a Menu Item

Use the MenuItems collection's Remove method to delete an item from the Add-Ins menu. Deleting an item automatically removes all its subitems if the removed item is a submenu. The following line shows how to remove the Registration Info Editor line that you added in the previous section:

mnuitRegedit.Parent.MenuItems.Remove mnuitRegedit

If you want to disable a menu line without removing it, set its Enabled property. The following line disables (grays) the Registration Info Editor line:

mnuitRegedit.Enabled = False

To disable a menu line without graying it or to associate a menu item with an AfterClick method in another object, use the DisconnectEvents method. The following line disassociates the Registration Info Editor line from the current object:

mnuitRegedit.DisconnectEvents

After using DisconnectEvents, you can associate the menu line with a new object by using the ConnectEvents method.

Debugging an Add-In

If Visual Basic has trouble locating an add-in or running the ConnectAddIn method, it displays the message shown in figure 24.5 when you try to load the add-in.

Fig. 24.5

This error indicates a problem with the registration or ConnectAddIn event of an add-in.

If the add-in is correctly registered and its programmatic ID matches the one installed in VB.INI, the ConnectAddIn method probably has a problem.

You can debug an add-in by using two instances of Visual Basic. This is similar to the way that you debug any object library cross-process, but it involves a couple extra steps.

To debug an add-in, follow these steps:

  1. Check the Windows Task List and end any running instances of the add-in's application.
  2. Load the add-in application's project (.VBP) in Visual Basic.
  3. Choose Tools, Project Options. Visual Basic displays the Project Options dialog box.
  4. In the Project Options dialog box's StartMode group, select the Object Application option. Click OK.
  5. Start the application.
  6. Edit the VB.INI file to include an entry in the [add-ins16] or [add-ins32] section for the programmatic ID of the add-in's Application class. The following lines show an example of the entry for REGADDIN.VBP:

    [add-ins16]
    RegEditAddIn.Application = 1

  7. Start another instance of Visual Basic. The new instance automatically connects to the add-in application running in the other instance of Visual Basic.
  8. Choose Tools, Environment Options. Visual Basic displays the Environment Options dialog box.
  9. In the Environment Options dialog box, deselect the Break on All Errors check box and select the Break in Object Application check box, then click OK. This enables you to step from the current application into the add-in if an error occurs.

You might want to set a breakpoint on the ConnectAddIn method within the add-in application to trace through initialization. After you set this breakpoint, you can rerun ConnectAddIn by unloading and reloading the add-in in the other instance of Visual Basic.

From Here...

For more information on the following topics, see the indicated chapters:


© 1996, QUE Corporation, an imprint of Macmillan Publishing USA, a Simon and Schuster Company.