Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Handling .NET forms and their events in VB6 main application

0.00/5 (No votes)
7 Oct 2005 1  
How to handle .NET forms (and their events ) used as objects in legacy VB6 application.

I always wanted to post an article in CodeProject. I was always "taking" not "giving" anything back to this community and that made me feel guilty. I think its time to "give" back something useful to this community.

Introduction

Throughout my humble experience with COM and .NET interoperability (let's narrow it to VB6, and further to COM client and .NET objects!), I have only found too few resources on how to handle forms between the two technologies�rather than methods and properties. Issues like accessing the database through different forms�causing .NET forms to be closed upon closing the main form in VB6 (.NET forms won't close upon closing the main form in VB6 in the ordinary state!)� and handling the events raised in .NET forms with methods in VB6� all these consumed considerable time and effort with some irritating errors!

In this article, I will try to explain how to handle the last two issues and leave the first one for the readers (maybe only suggest making the .NET form have its own DB connection rather passing the data from VB6 to .NET form).

First of all, we need to understand how exactly we can hook VB6 objects (forms here) to events raised in .NET forms, and then we can solve the issue of closing .NET forms when we close the VB6 main form.

Hooking VB6 forms to events raised in .NET forms

The main idea

Note: The credit for handling events this way goes to Adam Nathan's book ".NET and COM the Complete Interoperability Guide". We will not be using the connection point VB6 provides for automatic event handling. To handle events this way we need to declare the .NET forms in VB6 code with the "WithEvents" keyword. As you know, this keyword can�t be used within methods; only in the declaration part, and many of you, I bet, will need to declare and create .NET forms within methods at runtime whenever you need one, rather than have fixed number of .NET forms in the declaration part of VB6.

The main idea is to create in each .NET form a Hashtable. This Hashtable will hold references to every VB6 object (specifically Form) you wish to handle events for. Now when the .NET event is raised a method within the .NET form (an event handler) will loop through each item in the Hashtable (which represents the reference to the VB6 form) and call the proper method (the VB6 handler) through the reference.

An interface containing event handlers

Of course, by now you will conclude that each COM object (VB6 form in our case) in the Hashtable should have some methods and these methods should have the same name and same signature as each of the objects in the Hashtable. OK, now we can make an interface containing handlers (some methods) for the .NET Form�s events, which each COM object wishes to handle.

//Interface with "event handlers" for COM objects to implement

public interface IDotNetEventsHandler
{
    void NewFormCreated( ref DotNetForm sender );
}

As the name of the event handler implies, the VB6 form (the COM client, the main form) wants to be informed about each DotNetForm newly created (of course, we can make it a "Form" rather than "DotNetForm", but then we need to reference the .NET Framework main DLL in the VB6 application. For the sake of simplicity, we will stick to DotNetForm). The parameter is a reference to the newly created DotNetForm object.

Hooking the VB6 form to the event handlers

Now, to hook the VB6 form to the .NET form events, we define an interface for the .NET form to implement. The interface will contain two methods: Add and Remove.

public interface IDotNetEventsHookup
{
    int Add(IDotNetEventsHandler comObject);
    void Remove(int ComObjectIndex);
}

The Add method will take an object that implements the interface IDotNetEventHandler, in our case it will be the VB6 form. In this method, a reference of the VB6 form will be added to the Hashtable, to be notified of any new DotNetForm created in this .NET form. The reference will be assigned to a key to distinguish it. The Remove method simply removes the reference from the Hashtable.

This code in the VB6 application shows how these methods are used:

    Dim netForm As NetEvents.DotNetForm
    Set netForm = New NetEvents.DotNetForm
    
    Dim Ihookup As NetEvents.IDotNetEventsHookup
    Set Ihookup = netForm
    Dim cookie As Long
    cookie = Ihookup.Add(Me)

We now have the VB6 form hooked to the DotNetForm events (it's being referenced in the Hashtable).

This code will be executed when the event is raised in .NET; its main goal is to traverse all references of VB6 forms in the Hashtable to call the proper methods for this event (the event handler):

private void OnNewFormCreated()
{
    for(int counter=1; counter<=this.comObjects.Count; counter++)
         {
        IDotNetEventsHandler obj = 
            (IDotNetEventsHandler)comObjects[counter];
        DotNetForm form = new DotNetForm();
        form.Show();
        obj.NewFormCreated( ref form );
    }
}

Note: Of course, the new DotNetForms should be created outside of the event handler and then passed to the event handler, but if we do this the VB6 event handler will confuse the reader so, we will keep it like this and leave the alteration process to the reader.

Closing .NET forms by closing the main VB6 form

Now after learning (hopefully) how the events are handled, the rest is really simple. All we need is a dynamic array in VB6 form to hold DotNetForm references passed to the event handlers (that are newly created), and then hook this object to the passed DotNetForms.

Private Sub IDotNetEventsHandler_NewFormCreated(_
                                sender As NetEvents.DotNetForm)

  FormsCounter = FormsCounter + 1 'FormsCounter is declared 

                                 'previously in the declaration 

                                 'part

  ReDim Preserve DotNetForms(FormsCounter)
  Set DotNetForms(FormsCounter) = sender
 
  Dim Ihookup As NetEvents.IDotNetEventsHookup
  Set Ihookup = sender
 
  Dim cookie As Long
  cookie = Ihookup.Add(Me)
 
  MsgBox ("Created " + sender.Text)
End Sub

And then traverse these references to close them as needed (usually in the QueryUnload method in VB6 form):

 Dim tempForm As NetEvents.DotNetForm
    While FormsCounter >= 1
      Set tempForm = DotNetForms(FormsCounter)
      If Not tempForm Is Nothing Then
        tempForm.Close
      End If
      FormsCounter = FormsCounter - 1
    Wend

Conclusion

In each .NET form we hold references of all the COM objects that want to handle a particular event raised by the .NET form. Then, when the event is raised in a private event handler (not exposed) we traverse the COM object's references and call the proper methods. As you can see, we don't expose the .NET events to the COM object; it only uses those events to call the proper methods.

Using this we can inform the parent VB6 form of all the newly created .NET forms and store references of those in a dynamic array, so when we close the parent VB6 form we traverse all the .NET forms created that can be closed using the Close method.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here