none
How to correctly create class events in C# to be consumed by COM / Visual BASIC 6? RRS feed

  • Question

  • We still have to maintain a Visual BASIC 6 program here at my customers', and we are trying to slowly migrating it to .NET.

    So I have created a .NET 4.0 class library to be consumed by Visual BASIC 6.

    In this C# class library, I have added an event to the class. But I don't seem to be able to get this event correctly set-up to be consumable by Visual BASIC 6.

    My question is: What is necessary to set up a C# class event so that this event can be consumed by Visual BASIC 6?

     

    Here's the code I've written in C#:

    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.AutoDual)]
    public class FileBase
    {
    	public event Action CheckOutStateChanged;
    
    	...
    }

    Instead of generating a COM compatible event, REGASM creates two stub functions, like this:

     

    Here's part of the interface that has been created by REGASM for COM:

    FileBase:
    	Sub add_CheckOutStateChanged(value As Unknown)
    	Sub remove_CheckOutStateChanged(value As Unknown)
    	...

     

    But this is not what's expected to happen. Neither Visual BASIC 6 nor I do know how to handle these two functions.

     

    Can someone please enlighten me on what's necessary to create a class event that can be consumed by Visual BASIC 6, or COM resp.?

     


    Vote here for a Microsoft Connect feedback channel on Windows - and win a better Windows!

    • Edited by BetterToday Monday, November 14, 2011 7:09 PM
    Monday, November 14, 2011 6:46 PM

Answers

  • Hello BetterToday,

     

    1. COM Source Interface and the ComSourceInterfacesAttribute.

    1.1 Declare a COM-visible source interface in managed code to indicate to the COM client object the event interface to implement.

    1.2 The FileBase class must be marked with the ComSourceInterfacesAttribute with IFileBaseEvents type as the argument.

    1.3 Section 2 provides a full listing for a COM-visible source interface and an implementation of FileBase marked with the ComSourceInterfacesAttribute. Please read the explanatory comments included in the code.

    1.4 Section 3 provides a sample VB6 client code.

     

    2. Sample C# Code.

    2.1 Listed below is the source interface and an augmented FileBase class code :

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Runtime.InteropServices;
    
    namespace CSBetterTodayClassLib
    {
        // Define a source interface for COM client objects 
        // (e.g. VB6 objects) to implement.
        [ComVisible(true)]
        // The InterfaceTypeAttribute is important for VB6 clients.
        [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
        public interface IFileBaseEvents
        {
            // Declare the methods of the IFileBaseEvents source interface.
            [DispId(1)] void CheckOutStateChanged();
        }
    
        [ComVisible(true)]
        [ClassInterface(ClassInterfaceType.AutoDual)]
        // Indicate that the FileBase class will support
        // the IFileBaseEvents source interface.
        [ComSourceInterfaces(typeof(IFileBaseEvents))]
        public class FileBase
        {
            // Note that the name and signature of each managed event 
            // supported by this class must be the same as that 
            // of the corresponding method of the source interface. 
            //
            // For example, here, the CheckOutStateChanged managed event
            // will correspond to IFileBaseEvents.CheckOutStateChanged().
            // They both have the same name and has the same function
            // return type and parameter types (actually none).
            public event Action CheckOutStateChanged;
    
            // I have defined a method to test trigger the event.
            public void TriggerEvent()
            {
                CheckOutStateChanged();
            }
        }
    }
    
    

    2.2 Note that I have defined a method named TriggerEvent() for the client code to call in order to test trigger the event.

      

    3. Sample VB6 Client Code.

    3.1 Once the source interface IFileBaseEvents has been defined and the class FileBase declared to support it (via the ComSourceInterfacesAttribute), the FileBase class will be shown to support the CheckOutStateChanged() event. This will be seen in the VB6 Object Browser.

    3.2 Listed below is a sample VB6 client code that calls TriggerEvent() and receives the CheckOutStateChanged() event :

    Dim WithEvents objFileBase As FileBase
    
    Private Sub Command_Trigger_Click()
      objFileBase.TriggerEvent
    End Sub
    
    Private Sub Form_Load()
      Set objFileBase = New FileBase
    End Sub
    
    Private Sub objFileBase_CheckOutStateChanged()
      MsgBox "CheckOutStateChanged"
    End Sub
    
    

     

    - Bio.

     


    Please visit my blog : http://limbioliong.wordpress.com/
    • Proposed as answer by Sudhakar Kottapalli Tuesday, November 15, 2011 7:43 AM
    • Marked as answer by BetterToday Tuesday, November 15, 2011 4:32 PM
    • Unmarked as answer by BetterToday Tuesday, November 15, 2011 6:32 PM
    • Marked as answer by BetterToday Wednesday, November 16, 2011 3:48 PM
    Tuesday, November 15, 2011 4:44 AM

All replies

  • Hi,

    There is a very nice article about this topic on codeproject: http://www.codeproject.com/KB/cs/CreateActiveXDotNet.aspx. It explains in detail howto expose a .NET component as ActiveX control.

    Hope that helps,

    Stefan

    Monday, November 14, 2011 8:26 PM
  • Hello BetterToday,

     

    1. COM Source Interface and the ComSourceInterfacesAttribute.

    1.1 Declare a COM-visible source interface in managed code to indicate to the COM client object the event interface to implement.

    1.2 The FileBase class must be marked with the ComSourceInterfacesAttribute with IFileBaseEvents type as the argument.

    1.3 Section 2 provides a full listing for a COM-visible source interface and an implementation of FileBase marked with the ComSourceInterfacesAttribute. Please read the explanatory comments included in the code.

    1.4 Section 3 provides a sample VB6 client code.

     

    2. Sample C# Code.

    2.1 Listed below is the source interface and an augmented FileBase class code :

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Runtime.InteropServices;
    
    namespace CSBetterTodayClassLib
    {
        // Define a source interface for COM client objects 
        // (e.g. VB6 objects) to implement.
        [ComVisible(true)]
        // The InterfaceTypeAttribute is important for VB6 clients.
        [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
        public interface IFileBaseEvents
        {
            // Declare the methods of the IFileBaseEvents source interface.
            [DispId(1)] void CheckOutStateChanged();
        }
    
        [ComVisible(true)]
        [ClassInterface(ClassInterfaceType.AutoDual)]
        // Indicate that the FileBase class will support
        // the IFileBaseEvents source interface.
        [ComSourceInterfaces(typeof(IFileBaseEvents))]
        public class FileBase
        {
            // Note that the name and signature of each managed event 
            // supported by this class must be the same as that 
            // of the corresponding method of the source interface. 
            //
            // For example, here, the CheckOutStateChanged managed event
            // will correspond to IFileBaseEvents.CheckOutStateChanged().
            // They both have the same name and has the same function
            // return type and parameter types (actually none).
            public event Action CheckOutStateChanged;
    
            // I have defined a method to test trigger the event.
            public void TriggerEvent()
            {
                CheckOutStateChanged();
            }
        }
    }
    
    

    2.2 Note that I have defined a method named TriggerEvent() for the client code to call in order to test trigger the event.

      

    3. Sample VB6 Client Code.

    3.1 Once the source interface IFileBaseEvents has been defined and the class FileBase declared to support it (via the ComSourceInterfacesAttribute), the FileBase class will be shown to support the CheckOutStateChanged() event. This will be seen in the VB6 Object Browser.

    3.2 Listed below is a sample VB6 client code that calls TriggerEvent() and receives the CheckOutStateChanged() event :

    Dim WithEvents objFileBase As FileBase
    
    Private Sub Command_Trigger_Click()
      objFileBase.TriggerEvent
    End Sub
    
    Private Sub Form_Load()
      Set objFileBase = New FileBase
    End Sub
    
    Private Sub objFileBase_CheckOutStateChanged()
      MsgBox "CheckOutStateChanged"
    End Sub
    
    

     

    - Bio.

     


    Please visit my blog : http://limbioliong.wordpress.com/
    • Proposed as answer by Sudhakar Kottapalli Tuesday, November 15, 2011 7:43 AM
    • Marked as answer by BetterToday Tuesday, November 15, 2011 4:32 PM
    • Unmarked as answer by BetterToday Tuesday, November 15, 2011 6:32 PM
    • Marked as answer by BetterToday Wednesday, November 16, 2011 3:48 PM
    Tuesday, November 15, 2011 4:44 AM
  • Oh wow!! ... Bio, you are great! ... I can't believe it.

    You put so much effort into this working example... thanks a lot!

    I completely forgot about the fact that COM event sinks require a separate interface. And surely I didn't know about the ComSourceInterfacesAttribute and how to handle it.

    I have now updated my project exactly as you said and the new type library looks like this in VB6 Object Catalog right now:


     

    Thanks for spending your time on this and thank you for sharing your knowledge with me!

     

    I have declared the event as private:

    private event Action CheckOutStateChanged;

    This keeps the "add_" and "remove_" method stubs from being added to the TLB. I hope the event will work nonetheless.

    Take care,
    Axel Dahmen



    Vote here for a Microsoft Connect feedback channel on Windows - and win a better Windows!


    • Edited by BetterToday Tuesday, November 15, 2011 3:08 PM
    Tuesday, November 15, 2011 3:07 PM
  • Hmm, this doesn't seem to work: The event delegate collection always is null:


     

    Here's the VB6 code I've been using:

    In some module:

    Public
    gFileAccess As FileBase

    -------------------------------------------------------

    In frmMDIForm:

     
    Private WithEvents
    fileAccess As FileBase

    Private Sub fileAccess_CheckOutStateChanged()
    Print "Success."
    End Sub

    ...

    Private Sub MDIForm_Load()
    Set gFileAccess = New RemoteFile ' RemoteFile derives from FileBase
    Set fileAccess = gFileAccess
    End Sub

     

    What am I doing wrong here? Did you have a chance to test your example?

     


    Vote here for a Microsoft Connect feedback channel on Windows - and win a better Windows!

    • Edited by BetterToday Tuesday, November 15, 2011 7:09 PM
    Tuesday, November 15, 2011 6:42 PM
  • Hello BetterToday,

    1. I Assume that RemoteFile is a Managed Class.

    1.1 I assume that RemoteFile is a managed class, e.g. :

    [ComVisible(true)]
    public class RemoteFile : FileBase
    {
      ...
    } 
    

    1.2 This should present no problems.

    1.3 My test program tested OK with modified code based on the one you posted last. The CheckOutStateChanged() event was fired OK.

     

    2. Use OLEVIEW.EXE.

    2.1 You can use OLEVIEW.EXE to confirm whether IFileBaseEvents is declared as the default source interface for the RemoteFile coclass.

    2.2 With the simple definition for RemoteFile that I used, the following is the coclass listing in the IDL :

        [
          uuid(ECF30124-35E4-31E1-B31F-21057CD49A5E),
          version(1.0),
            custom({0F21F359-AB84-41E8-9A78-36D110E6D2F9}, "CSBetterTodayClassLib.RemoteFile")
        ]
        coclass RemoteFile {
            [default] interface _RemoteFile;
            interface _FileBase;
            interface _Object;
            [default, source] dispinterface IFileBaseEvents;
        };
    
    

    2.3 Perhaps you can show us how the RemoteFile class is defined.

     

    - Bio.

     


    Please visit my blog : http://limbioliong.wordpress.com/
    Wednesday, November 16, 2011 5:18 AM
  • Thanks again for replying, Bio,

    Here's the information you requested:

     

    TypeLib information:

     [
    uuid(EC866A58-25CF-3747-ABE1-3A616C94D934),
    version(1.0),
    custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, "CSBetterTodayClassLib.RemoteFileAccess.Classes.Base.IFileBaseEvents")

    ]
    dispinterface IFileBaseEvents {
    properties:
    methods:
    [id(0x00000001)]
    void CheckOutStateChanged();
    };

    [
    uuid(935DC555-F05D-36AA-886E-78021E717CD7),
    version(1.0),
    custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, "CSBetterTodayClassLib.RemoteFileAccess.Classes.Remote.RemoteFile")
    ]
    coclass RemoteFile {
    [default] interface _RemoteFile;
    interface _FileBase;
    interface _Object;
    [default, source] dispinterface IFileBaseEvents;
    };

     

    RemoteFile souce code:

    using CSBetterTodayClassLib.RemoteFileAccess.Classes.Base;

    namespace CSBetterTodayClassLib.RemoteFileAccess.Classes.Remote
    {
    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.AutoDual)]
    public class RemoteFile : FileBase
    {
    ...
    }
    }

    Your help is very much appreciated.

     


    Vote here for a Microsoft Connect feedback channel on Windows - and win a better Windows!


    • Edited by BetterToday Wednesday, November 16, 2011 11:55 AM
    Wednesday, November 16, 2011 11:51 AM
  • Hi Bio,

    I just found the reason why the event wasn't fired: There are some other classes in my .NET library which create their own objects of the RemoteFile class. That's why the event handler collection was empty. I was accidentally triggering a different, library internal, RemoteFile object!

    I'm very sorry for causing this confusion in the end.

    You've been a great, extraordinary valuable help!!!

    Thanks so much.

    Take care,
    Axel


    Vote here for a Microsoft Connect feedback channel on Windows - and win a better Windows!

    • Edited by BetterToday Wednesday, November 16, 2011 3:53 PM
    Wednesday, November 16, 2011 3:53 PM