Using .NET Class Libraries from VB6 Clients

Introduction

It often happens that someone needs to use a .NET class library from within a Visual Basic 6.0 project. Fortunately, .NET has some interoperability features that allow us to expose our classes to clients as if they were COM objects. And, COM objects can be used by VB6 applications. So, what do you have to do if you want to expose a complete COM interface from your class library and use it from within your VB6 app?

Of course, there is some documentation within the MSDN. Look at the "Interoperating with Unmanaged Code" section, and you will find some very useful hints. There is also an article here on CodeGuru, contributed by Steve Green, showing how to expose properties and methods: "Consuming .NET Components from COM-Aware Clients."

From these sources, the bits and pieces become clearer. But what do you do if you want to expose events as well as properties and methods, all from the same class library? And how can your VB6 client use them? Well, it took some effort to find out.

Implementing the .NET Class Library

Basically, if you want to expose your classes to a COM client, you have to expose an interface. If you want to expose not only methods and properties, but also events, you have to expose even two interfaces: one for the methods and properties, and the other one for the events. I will provide a very simple class library, exposing just one method, one property, and one event, to demonstrate how these interfaces look like. Essentially, this class library consists of two interfaces and one class. Let's start with the interface that exposes the property and the method:

using System;

namespace ComInterop
{
   public interface IComInterop
   {
      void SomeMethod( int nValue );
      int SomeProperty{ get; set; }
   }
}

Pretty straightforward. Let's look at the second interface; it exposes our event:

using System;
using System.Runtime.InteropServices;

namespace ComInterop
{
   [InterfaceTypeAttribute( ComInterfaceType.InterfaceIsIDispatch )]
   public interface IComInteropEvents
   {
      void SomeEvent();
   }
}

The only difference here is that we need to expose this interface as an IDispatch interface. By default, interfaces are exposed as dual interfaces, which means IUnknown as well as IDispatch. Our event interface, however, must be exposed explicitly as an IDispatch interface. We use the InterfaceTypeAttribute to do this. Otherwise, the event interface is similar to the previous interface.

Now that we have defined our two interfaces, we can do the implementation, which in .NET is a class:

using System;
using System.Reflection;
using System.Runtime.InteropServices;

[assembly: AssemblyKeyFile( "ComInterop.snk" )]

namespace ComInterop
{
   public delegate void SomeEventHandler();

   [ComSourceInterfacesAttribute( "ComInterop.IComInteropEvents" )]
   public class CComInterop : IComInterop
   {
      private int m_nSomeProperty = 0;
      public event SomeEventHandler SomeEvent;

      public void SomeMethod( int nValue )
      {
         m_nSomeProperty = nValue;
         SomeEvent();
      }

      public int SomeProperty
      {
         get{ return m_nSomeProperty; }
         set{ m_nSomeProperty = value; }
      }
   }
}

Defining a delegate to raise an event is straightforward, so I will not comment on that. Our event will be raised by the only method included in the sample code, allowing the VB6 client to catch it every time the method is called.

The interesting point here is that our implementation class has to inherit from both interfaces defined before, but in a very different way. While inheritance from IComInterop is defined .NET style, inheritance from IComInteropEvents is defined with the help of another attribute: the ComSourceInterfaceAttribute, which defines those event interfaces that will be exposed as COM connection points.

In our sample, we want to expose the IComInteropEvents interface of the ComInterop namespace as a connection point interface, so we define this interface as our event source interface.

When imitating a COM interface, we need to prepare our class library to be registered. Hence we have to provide a strong name, which in turn requires a key file, and that key file will be created by including the attribute AssemblyKeyFile.

To make things easy, a simple batch file is provided to build the library, as well as registering it. Make sure your paths are correctly set, either by running vcvars32.bat or by adding them manually.

Implementing the VB6 Client

To make calls into our .NET class library and to catch its event, a very simple VB6 application is provided. It consists of one form, containing just one button.

The only difference between this code and a VB6 source that references an actual COM object is this:

Public WithEvents m_oComInterop As ComInterop.CComInterop
Public m_iComInterop As ComInterop.IComInterop

We have to define two object references instead of one. Of course, both references have to be assigned to an instance of our class library, which we do when the form is loaded:

Private Sub Form_Load()
   Set m_oComInterop = New ComInterop.CComInterop
   Set m_iComInterop = m_oComInterop

   m_nCounter = 0
End Sub

We simply create one instance, and then assign the resulting reference to the other reference as well. The rest of the code is self-explanatory:

Public m_nCounter As Integer

Private Sub cmdCallMethod_Click()
   m_nCounter = m_nCounter + 1
   m_iComInterop.SomeMethod m_nCounter
End Sub

Private Sub m_oComInterop_SomeEvent()
   MsgBox "Counter = " + CStr(m_iComInterop.SomeProperty)
End Sub

Every time the button "cmdCallMethod" is clicked, the counter will be incremented, and the class library method will be called. This, in turn, raises the class library event, which will be caught by the VB6 app, thereby launching a message box, displaying the current value of the counter, which is being retrieved by calling the class library's property.

When building the VB6 app, don't forget to add a reference to the ComInterop class library!



About the Author

Andreas Wieberneit

I started my IT career during the late 80s, using programming environments such as C, Pascal and dBase. With the advent of the 90s, I discovered C++ and OOP. Since then, I devoted most of my work to build modular systems, composed of DLLs, COM objects, and service applications; to participate in the development of multi-tier business systems; and to communicate with Oracle, SQL-Server and postgreSQL databases, all kinds of hardware devices, as well as smart cards. With the beginning of the new millenium I switched to .NET, where the need to integrate existing components forced me to deal with the secrets of interoperability. During the past years, I shared my working hours between managing software projects and doing hands-on implementation of software systems. Currently, I live and work in Toronto, Canada. You can reach me at awieberneit@yahoo.ca.

Downloads

Comments

  • wheloltabotly PumeSonee Phobereurce 7295524

    Posted by TizefaTaNaday on 06/12/2013 10:56pm

    SlormaSweeS airjordanviiretrodoernbecher.holidaygiving.org rhyroiniatigo jordanretro4sale.holidaygiving.org Atosecets

    Reply
  • System.Reflection.TargetException: Object does not match target type.

    Posted by jellul on 03/15/2005 06:29am

    Hi There,
    
    First of all I think that your article is great and easy to understand.
    
    However I am having a problem with a .NET class library being used in a vb6 application.
    
    The problem is this:
    the class has an event; and this event works fine (for a while); however after a while of running the following error is thrown:
    System.Reflection.TargetException: Object does not match target type
    
    I have no idea what the problem is since it works fine for a few hours than all of a sudden this exception is thrown.
    
    My code is as follows:
    The COM event:
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    public interface OneWireNetworkInterfaceEvents
    {	
      void NoteInserted(DS2408NoteReader ds2408, byte note);
    }
    
    
    The delegate (placed outside the class):
    public delegate void NoteInsertedDelegate(DS2408NoteReader ds2408, byte note);
    
    
    The event inside the class:
    public event DeviceDiscoveredDelegate DeviceDiscovered;	
    
    
    
    Calling the event:
    if (NoteInserted != null) 
    {
      NoteInserted(sender, note);
    }	
    
    
    The VB application code:
    Private Sub owni_NoteInserted(ByVal DS2408 As XSOneWire.DS2408NoteReader, ByVal note As Byte)
      Me.Caption = Now & "::" & DS2408.ID & "::" & note
    End Sub
    
    
    One note i would like to point out is that the class DS2408 does not implement any com interfaces.
    
    Any ideas?  Cause this is really frustrating?!??!

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: September 17, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Another day, another end-of-support deadline. You've heard enough about the hazards of not migrating to Windows Server 2008 or 2012. What you may not know is that there's plenty in it for you and your business, like increased automation and performance, time-saving technical features, and a lower total cost of ownership. Check out this upcoming eSeminar and join Rich Holmes, Pomeroy's practice director of virtualization, as he discusses the …

  • Not long ago, security was viewed as one of the biggest obstacles to widespread adoption of cloud-based deployments for enterprise software solutions. However, the combination of advancing technology and an increasing variety of threats that companies must guard against is rapidly turning the tide. Cloud vendors typically offer a much higher level of data center and virtual system security than most organizations can or will build out on their own. Read this white paper to learn the five ways that cloud …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds

IntelliTXT script section -->