Lesson 1: Visio COM Add-in Creation in Visual Basic .NET [Visio 2003 SDK Documentation]

The Microsoft Visual Basic® .NET project files that accompany this lesson can be found in the SDK install directory at \Samples\Tutorial\VBNet\Lesson1.

Table of contents

Scenario

About COM Interop and the Visio PIA

Implementation

Creating the TutorialAddin Project

Adding a Reference to the Visio PIA

Specifying a Namespace for the Assembly

Implementing the IDTExtensibility2 Interface

Implementing AssemblyInfo.vb

Deploying the Sample Application

Running the Sample Application

Summary: COM Interop

Scenario

This lesson describes the basic steps for building a Microsoft® Visio® Component Object Model (COM) add-in using Microsoft Visual Basic® .NET. The resulting add-in serves as the basis for the Microsoft Visio Managed Code Interop Tutorial sample application, which is developed in the subsequent lessons. This lesson also summarizes the key information learned from implementing a COM add-in in Visual Basic .NET versus implementing with Visual Basic 6.0.

To create the COM add-in, we will do the following:
  • Create a COM add-in project called TutorialAddin.
  • Add a reference to the Visio Primary Interop Assembly (PIA).
  • Specify a namespace for the assembly.
  • Implement the IDTExtensibility2 interface.
  • Modify the information in AssemblyInfo.vb.
  • Modify the setup project to register the add-in.

Although there is only a small amount of code in the sample application for the first lesson, there is a considerable amount of activity generated by the code. When Visio is opened, a message box appears alerting you the COM add-in was actually invoked. The message box displays properties of the Visio Application object.

For more information about building COM add-ins for Visio, see the article "About Microsoft Office Visio Add-ons and COM Add-ins" in the MSDN Library.

About COM Interop and the Visio PIA

Our goal is to implement our COM add-in as a Microsoft Visual Basic .NET class. However, the .NET implementation is different from the COM implementation; components, interfaces, and globally unique identifiers (GUIDs) give way to assemblies, types, and strong names.

So, how do we navigate between COM and .NET? The answer is COM Interop, which allows unmanaged code to call managed code. Prior to the Microsoft .NET Framework, Microsoft Windows® programming made use of unmanaged code. Managed code is code that the common language runtime executes. The common language runtime provides services that help with memory management, cross-language integration, code access security, and automatic lifetime control of objects.

Visio is written is unmanaged code. When Visio loads a COM add-in, it uses standard COM mechanisms, such as performing a registry lookup, creating a class factory to instantiate the COM add-in, and querying for a well-known interface (IDTExtensibility2) to establish communication between Visio and the COM add-in.

COM Interop also allows managed code to call unmanaged code. Such calls happen when a COM add-in that is implemented in Visual Basic .NET makes a Visio Automation call. The add-in does not directly call the Visio type library, but rather calls a Visio PIA. The Visio PIAs allow seamless integration with code written in the .NET Framework and the Visio Automation interface.

For Visio 2003, there are three PIAs:

  • The Visio 2003 type library PIA
  • The Visio 2003 Save as Web type library PIA
  • The Visio 2003 ActiveX drawing control PIA

These PIAs are automatically installed as part of the Visio application installation if Visual Studio .NET (version 7.1 or higher) is installed on the computer. In this tutorial, only the Visio 2003 type library PIA is utilized.

To the Visual Basic .NET programmer, the Visio PIA appears as a typical .NET assembly. The PIA contains the Microsoft description of Visio types and is signed and authorized by Microsoft. Using the Visio PIA prevents type incompatibilities that might be caused by developers using different interop assemblies (such as those generated by using the TlbImp utility).

Implementation

To see the implementation of the sample application for this lesson, open the solution using Microsoft Visual Studio® .NET. You can find the solution, named Lesson1.sln, in the Lesson1 folder. This lesson walks you through the steps used to create the solution.

Creating the TutorialAddin Project using the Visual Studio .Net Extensibility Project Template

Creating your own Visual Basic .NET COM add-in for Visio is as simple as modifying the TutorialAddin project. While the project contains all the basics you need to start coding, it is instructive to create the project from scratch.

To create the TutorialAddin project
  1. In Visual Studio .NET, on the File menu, point to New, and then click Project.
  2. In the New Project dialog box, expand Other Projects under Project Types, select Extensibility Projects, and then select the Shared Add-in template.
  3. For Name, type TutorialAddin.
  4. The Location box displays the folder that the TutorialAddin project is located in. You can change the folder location directly from the Location box, or click the Browse button to select another location.
  5. Click the More button to view the Create directory for Solution check box, if it is not currently displayed.
  6. Select the Create directory for Solution check box, and then for New Solution Name, type Lesson1. Click OK.

In the Extensibility Wizard that opens, follow these steps:

  1. Click Next to start the wizard, select Create an Add-in using Visual Basic, and then click Next.

  2. Deselect all of the application host options except Microsoft Visio, and then click Next.

  3. Provide a name and description for the add-in, and then click Next. (Name: Visio Managed Code Interop Tutorial, Description: Sample COM add-in that uses the Visio Primary Interop Assembly.)

    Note  The name of the add-in will appear in the COM Add-in dialog box in Visio.

  4. On the Choose Add-in Options page, select the option to load the add-in when the host application loads and then click Next. Do not check the option to make the add-in available to all users.

  5. Click Finish to complete the wizard.

The wizard creates two projects: TutorialAddin and Lesson1Setup. TutorialAddin is the COM add-in implementation. The wizard creates two files within the TutorialAddin project: Connect.vb and AssemblyInfo.vb.

Adding a Reference to the Visio PIA

We need to add a reference to the Visio PIA in order to examine the Visio Application objects metadata. Although Lesson 1 does not actually make calls to the PIA itself, adding this reference to the Visio PIA will complete a basic implementation that you can use for Visual Basic .NET COM add-ins you create later.

To use the Visio PIA
  1. In the Solution Explorer pane, right-click the TutorialAddin project and choose Add Reference.
  2. Click the COM tab. Locate the Microsoft Visio 11.0 Type Library in the component list, and then click Select. Click OK.

In the Properties window, note that the Path property lists Microsoft.Office.Interop.Visio in the c:\WINDOWS\assembly\GAC\ path. This indicates that the Visio PIA is being used instead of forcing Visual Studio .NET to create its own COM interop assembly in your project's location. With a typical Visio installation, if the .Net Framework version 1.1 is installed on your computer, the Visio PIA is installed in the global assembly cache (GAC). If you installed the .Net Framework after you installed Visio, you will need to run the Visio setup again to install the PIA.

Now that we have a reference to the Visio PIA, we can import its namespace in Connect.vb by adding the following code before the Connect class:

Imports Microsoft.Office.Interop.Visio

Note  The wizard-generated references to the Office, System.Data, and System.XML assemblies are not needed by the COM add-in. You can remove these references by right-clicking the assembly in the Solution Explorer pane, and then clicking Remove. To avoid compile errors, you will also need to update the project imports in the project properties. Select the Project menu, and then select TutorialAddin Properties. In the Project Properties dialog box, select Common Properties and then Imports. Remove System.Data from the project imports list.

Create the Project Using the Visio 2003 SDK Add-in or Add-on Project Template

Rather than use the Extensiblity wizard, with the Visio 2003 SDK you could use the Visio Add-in and Add-on project wizard to create the COM Add-in project. This wizard simplifies project creation by creating a Visio-only add-in and adding the reference to the Visio PIA. Follow the steps below to create a Visual Basic .Net add-in using the wizard.

  1. In Visual Studio .NET, on the File menu, point to New, and then click Project.
  2. In the New Project dialog box, select Visual Basic, and then select Visio Add-in or Add-on project type.
  3. For Name, type TutorialAddin.
  4. The Location box shows the default folder location of the TutorialAddin project. You can change the folder location directly from the Location box, or click the Browse button.
  5. Select the Create directory for Solution check box, and then for New Solution Name, type Lesson1. Click OK. (Click the More button to view the Create directory for Solution check box, if it is not currently displayed. )

In the Visio Add-in and Add-on Wizard that opens, follow these steps:

  1. Click Next to start the wizard. Select Create a Visio COM Add-in, and then click Next.
  2. Provide a name and description for the add-in, and then click Next. (Name: Visio Managed Code Interop Tutorial, Description: Sample COM add-in that uses the Visio Primary Interop Assembly.)
  3. On the Add-in Options page, select the option to load with Visio load and then click Next. Do not check the option to make the add-in available to all users.
  4. Click Finish.

Like the extensibility wizard, the Visio Add-in and Add-on wizard creates two projects. The first, TutorialAddin, is the COM add-in implementation. The wizard creates two files within the TutorialAddin project: Connect.vb and AssemblyInfo.vb. The second project is the setup project.

Specifying a Namespace for the Assembly

It is a good practice to uniquely identify assemblies by using a namespace. For the assembly that houses our COM add-in, we will define a root namespace called Microsoft.Visio.Samples.VBNet.TutorialAddin. We will also name the resulting assembly Microsoft.Visio.Samples.VBNet.TutorialAddin.dll.

To specify a namespace and name the assembly
  1. In the Solution Explorer pane, right-click the TutorialAddin project and choose Properties.
  2. Under the Common Properties folder, click General.
  3. Type Microsoft.Visio.Samples.VBNet.TutorialAddin for both Assembly name and Root namespace.
  4. Click OK.

Implementing the IDTExtensibility2 Interface

Connect.vb is the most interesting file in this lesson. It provides a default Visual Basic .NET implementation of the IDTExtensibility2 interface, which is the standard mechanism for an application to communicate with a COM add-in.

The IDTExtensibility2 interface contains five event procedures: OnBeginShutdown, OnAddInsUpdate, OnStartupComplete, OnDisconnection, and OnConnection. Visio gets a reference to the IDTExtensibility2 interface to make calls to the COM add-in. The Connect class in connect.vb implements this interfaces using the following code:

Public Class Connect
     Implements Extensibility.IDTExtensibility2

To see the implementation of the IDTExtensibility2 interface, refer to the Connect.vb file provided in Lesson1.sln. In addition to the interface implementation, you will notice minor differences between this file and the wizard-generated Connect.vb, such as renamed variables.

Using Namespaces in the Connect Class

The wizard-generated code explicitly qualifies the namespace of the IDTExtensibility2 interface. This is a good coding practice in general for Visual Basic .NET, as it removes any ambiguity regarding type names in situations where its possible that two assemblies can contain the same type name (for example, Shape could refer to a Visio Shape or to a Microsoft Office Word Shape). The wizard-generated code adds a reference to the Extensibility public assembly and imports its namespace with the following declaration:

Imports Extensibility

The wizard also adds an Imports statement to another namespace well need:

Imports System.Runtime.InteropServices

The System.Runtime.InteropServices namespace provides classes for managed definitions of COM types. We do not need to import the Microsoft.Office.Core namespace, so you can remove this line. The TutorialAddin sample application uses the following attributes, that will be applied to the Connect class:

<GuidAttribute("593A7087-5C74-43A8-A1B6-53D943630583"), _
     ProgIdAttribute("TutorialAddin.Connect")> _
Public Class Connect

When Visio loads the COM add-in, it uses COM conventions to look up the location of the add-in. Defining a GUID and a ProgID are required for COM component registration. Note that the GUID will differ from the preceding example when you run the wizard.

Implementing the OnConnection Method

For this lesson we will implement the OnConnection method of the IDTExtensibility2 interface. Visio calls this method after it loads the COM add-in. The OnConnection method has the following signature:

Public Sub OnConnection( _
     ByVal Application As Object, _
     ByVal connectMode As Extensibility.ext_ConnectMode, _
     ByVal addInInstance As Object, _
     ByRef custom As System.Array) _
     Implements Extensibility.IDTExtensibility2.OnConnection

The sample application makes use of the first parameter only, which is a reference to the application in which the COM add-in is currently running, in this case Visio. Once the add-in obtains this reference, it can use the Visio API and create a new document.

The implementation of OnConnection makes use of the Application object that it receives. If you generated the Add-in using Visual Studio's extensibility wizard, the generated Connect class will have a private member called ApplicationObject, of type Object. If you generated the Add-in using the Visio Add-in and Add-on wizard, then the Connect class will have a private member, vsoApplication, which is strongly typed as a Visio Application object. The reason for this difference is that the Extensibility wizard generates add-ins for multiple different applications, whereas the Visio wizard only generates add-ins for Visio. Since the tutorial add-in only works with Visio, it should have a strongly typed application member like the one generated by the Visio wizard. Unlike the Visio wizard, the lesson code uses visioApplication, rather than vsoApplication.

Private visioApplication As Microsoft.Office.Interop.Visio.Application

The first step in our implementation of OnConnection is to set this member to the application object passed to the OnConnection method, and then display a dialog box containing properties of the visioApplication object. Both wizards generate code for this method. The code below will differ somewhat from both of them, so first delete the generated implementation of this method and replace it with:

Try
     ' Do an explicit cast to the Visio Application object so it is
     ' clear that there is a type change in this statement.      
     visioApplication = CType(application, _
          Microsoft.Office.Interop.Visio.Application)
     ' Show a message box with information about Visio using
     ' properties of the  Visio Application object.
     MsgBox(visioApplication.Name & vbCrLf _
          & "Version = " & visioApplication.Version & vbCrLf _
          , , DIALOG_TITLE)
     Catch err As COMException
     MsgBox("Exception in OnConnection: " & _
          err.Message, , TITLE)
     catch (err) as InvalidCastException
         MsgBox("Exception in OnConnection: " & _
          err.Message, , TITLE
End Try

Because the signature for OnConnection is itself generic across all applications, we get a lowest-common-denominator base-class object. The Application type is specific to Visio. Use the full namespace, so that it is clear that this is the Visio application type.

In the code, we perform this cast nested within a Try/Catch block, because it is possible for the cast to fail. The failure could happen if this addin was registered for use with a different application, and that application loaded the add-in. In this case, the code would throw an InvalidCastException exception. The code also catches a COMException exception, in case the calls into Visio to get the Name and Version fail.

In the message box calls above, the last parameter is TITLE. TITLE is a string constant, whose definition will be in the module Shared.vb. The Shared.vb file is not generated by the wizard. To add a new file called Shared.vb to your project, right click on your add-in project, select Add, then Add New Item. In the dialog box choose module and enter Shared.vb. In Lesson 1, DIALOG_TITLE is the only item in the module, but because the sample application is developed in further lessons, the module will provide all the constants and procedures that are shared between the class modules in the project.

Module _Shared
     Public Const TITLE As String = _
          "Visio Managed-Code Interop Tutorial Sample"
End Module

Implementing AssemblyInfo.vb

AssemblyInfo.vb, generated by the Add-in wizard, contains custom attributes for setting the version resource fields for the TutorialAddin assembly.

For the sample application, we will fill in the following attributes. Since the tutorial code that ships with the Visio 2003 SDK was developed by Microsoft, it contains Microsoft copyrights. Add your own company information to the appropriate tags in assemblyinfo.vb.

<Assembly: AssemblyTitle("Visio Managed Code Interop Tutorial")>
<Assembly: AssemblyDescription _
     ("Sample COM add-in that uses the Visio Primary Interop Assembly")>
<Assembly: AssemblyCompany("Microsoft Corporation")>
<Assembly: AssemblyProduct("Microsoft Office Visio 2003 SDK Managed-Code Interop Tutorial ")>
<Assembly: AssemblyCopyright("Copyright (c) 1993-2004 Microsoft Corporation. " _
    & "All rights reserved. ")>
<Assembly: AssemblyTrademark( _
    "ShapeSheet, SmartShapes and Visio are either registered trademarks or " & _
    "trademarks of Microsoft Corporation, in the U.S. and/or other countries.")> 
<Assembly: AssemblyCulture("")>
<Assembly: AssemblyVersion("1.0.0.0")>

You can view this information in Windows Explorer by right-clicking the TutorialAddin.dll file name (located in the bin folder of the TutorialAddin project after the project is built), and choosing Properties, as shown in the following figure:

TutorialAddin.dll Properties dialog box

Figure 1. TutorialAddin.dll Properties dialog box

Deploying the Sample Application

The wizard automatically creates a setup project; its name will include the word setup, but the actual name will vary depending on the wizard used to create the project. Rename this project to Lesson1Setup. For each lesson, rename the setup project to reflect the lesson number, so that you can easily tell which lesson is installed. This project builds a Microsoft Windows Installer setup application, which simplifies deployment of the COM add-in to other users' computer.

Both the Extensibility Wizard and the Visio Add-in or Add-on wizard create setup projects that will register your DLL as a COM server, and register the COM Add-in with Visio. The setup project determines what COM object to register based on the primary output (our tutorial add-in) and it dependencies. The information need to register the COM add-in with Visio is specified in the generated setup project.

Registering the COM Add-in with Visio

The COM add-in project wizard generates registry information to register the COM add-in with Visio. We will examine where this information is defined in the setup project.

  1. In the Solution Explorer pane, right-click the Lesson1Setup project, point to View, and then click Registry. The Registry window opens.
  2. In the Registry window, expand HKEY_CURRENT_USER.
  3. Since we did not select the wizard option The add-in will be available for all users of the computer on which the add-in is installed, the registry settings will be in HKEY_CURRENT_USER key. When the option (The add-in will be available for all users of the computer on which the add-in is installed) is selected, the registry settings will be located in the HKEY_LOCAL_MACHINE key. We chose not to make this add-in available to all users so that it would appear in the COM Add-ins dialog box accessible from the Tools menu.
  4. The contents of the registry subtree are as follows:
[HKEY_CURRENT_USER\SOFTWARE\Microsoft\Visio\Addins\TutorialAddin.Connect]
FriendlyName="Visio Managed Code Interop Tutorial"
Description="Sample COM add-in that uses the Visio
     Primary Interop Assembly"
LoadBehavior=3

The COM add-in is identified by a ProgID (TutorialAddin.Connect). This is the ProgID that we defined previously for the Connect class using the ProgIdAttribute attribute.

The LoadBehavior flag represents two values combined to specify this behavior. The value "2" indicates the COM add-in is to be loaded and connected when Visio starts. This value is combined with another value of "1" to specify that after the COM add-in is registered and loaded, it should be in the connected state. Specifying a LoadBehavior value of "3" causes the OnConnection method to be invoked when Visio loads the COM add-in on startup.

Customizing the Installer

Selecting the Lesson1Setup project in the Solution Explorer pane displays the project settings in the Properties pane. The following properties have been modified for the setup project:

  • Author
  • Manufacturer
  • ProductName
  • Title

These settings determine the name of the folder that the files are installed in and the text in the setup dialog boxes.

If you generated the add-in using the Extensibility wizard, you also need to exclude some of the dependent files from the from the setup project. The only files that should be installed are:

  • Microsoft.Samples.VBNet.TutorialAddin.tlb
  • Extensibility.dll
  • The Primary output from Tutorial Addin (Active)

All other files should be excluded. Visual Studio will not add the Visio PIA file to the dependencies until the setup project is built; you will have to build the project once, and then exclude the Visio PIA (Microsoft.Office.Interop.Visio.dll) from the setup project.

Deploying the COM Add-in

At this point, yauthor="a-nelabb" time="20040118T173509-0800" data="Y"ou can deploy the TutorialAddin sample application.

To deploy the TutorialAddin Sample application
  1. In the Solution Explorer pane, right-click the TutorialAddin project, and then click Build.
  2. In the Solution Explorer pane, right-click the Lesson1Setup project, and then click Build.
  3. Run the setup application. The setup application is named Setup.exe and is located under the Lesson1Setup or TutorialAddinSetup folder of your solution, in either the Debug or Release folder, depending on your build settings.

Running the Sample Application

After you have deployed the sample application, you can run it by opening Visio. You should see the following dialog box.

Sample application dialog box.

Figure 2. Dialog box shown when you open the sample application in Visio

In addition, TutorialAddin now appears in the list of available Visio COM add-ins. To verify this, on the Visio Tools menu, point to Macros, and then click COM Add-Ins. You will see the following dialog box.

COM Add-ins dialog box

Figure 3. List of available COM add-ins in Visio

Visio displays the location of the add-in as mscoree.dll, not TutorialAddin.dll as you might expect. This is because mscoree.dll is the common language runtime, which provides the implementation of COM Interop. The common language runtime is called by COM, and in turn calls the TutorialAddin assembly.

Note  To make changes to the COM add-in and rebuild it, you must first close Visio because it has TutorialAddin.dll loaded, which prevents the assembly from being overwritten.

Summary: COM Interop

From this simple starting point, a number of differences are apparent when a COM add-in is implemented in Visual Basic .NET code as opposed to Visual Basic 6.0. The following table summarizes these differences.

Implementations of a COM add-in
In the Windows .NET Framework In COM
Type standard Binary standard
Assemblies Type libraries
Type safe Type unsafe
Object-based Interface-based
Exceptions HRESULTs
Strong names GUIDs

Note that COM is a binary standard. It specifies how an object will appear in memory, establishing a convention to allow components written in one language to talk to components written in a different language. The Windows .NET Framework provides a type standard, and it does this by providing an implementation (the common language runtime). Code that follows this type standard, known as the Common Language Specification (CLS), can take advantage of functionality that the common language runtime provides for "free".

One of the big advantages of using the common language runtime is that the "glue" between code units (components in COM, assemblies in the Windows .NET Framework) is transparent. Application deployment is often as simple as copying the generated assemblies to a directory on a user's computer. Even though we are using Visual Basic .NET, however, we still need to adhere to COM standards, such as defining a ProgID, to implement our add-in.

But beyond doing some basic tasks such as registration, we can leave a lot of the dirty work to COM Interop. COM Interop abstracts the inconsistencies between the two models such as:

  • Different data types
  • Method signatures
  • Exceptions/HRESULTs

Building the TutorialAddin project automatically creates a type library (TutorialAddin.tlb), which you can examine by using the OLEView tool. For example, it contains the COM signature for the OnConnection method:

void OnConnection(
        [in] IDispatch* Application,
        [in] ext_ConnectMode ConnectMode,
        [in] IDispatch* AddInInstance,
        [in] SAFEARRAY(VARIANT)* custom);

This is the method called by Visio when it loads the COM add-in. Here is the same method using .NET types:

Public Sub OnConnection( _
    ByVal application As Object, _
    ByVal connectMode As Extensibility.ext_ConnectMode, _
    ByVal addInInstance As Object, _
    ByRef custom As System.Array)

COM Interop marshals the parameters for OnConnection automatically, packing them into a format that can be moved across processes. In the next lesson, we will see how types provided by the Visio PIA are similar to .NET, which makes coding to the PIA in Visual Basic .NET more intuitive.