Raising events in VB6 from .NET via COM Interop
So I've been experimenting with VB6 and a .NET DLL compiled with COM interop to see how it behaves. The results are a little surprising.
What I did was I built a test scenario where the "Client" (vb6 app) hangs for 5 seconds while the "Server" (.NET COM object) fires an event once per second.
First, I start the timer. Inside the DLL on each timer tick, it adds 1 to an internal counter ("Count"). When the timer fires, it raises an event and passes the value of Count at that time as a parameter (nTicks).
When the client app (vb6) hangs, internally the count continues to run, but the events get queued up. VB6 gets all the events in a rush when the 5 second sleep is over.
Here's the part where it gets interesting. During that rush, the events come in out-of-order. The order seems random. I've run it several times and the order always varies. I circled the interesting part in red, below.
Aside from the return order issue I think this threading behavior could be interesting, such as launching a complicated calculation or SQL query, and then returning the result when it is completed. As long as the entire operation is "atomic", it won't matter if the results come out of order, or a sequence number could be used to ensure the results are processed in the proper order.
Here is the VB6 code for the test app above...
Dim WithEvents foo As ClassTest1.ComClass3
Private Declare Sub SleepAPI Lib "kernel32" Alias "Sleep" (ByVal dwMilliseconds As Long)
Public Sub WriteLn(ByVal buf$)
List1.AddItem Time$ & ": " & buf$
List1.ListIndex = List1.NewIndex
Private Sub Command1_Click()
foo.CauseClickEvent 2, 3
Private Sub Command2_Click()
Private Sub Command3_Click()
WriteLn "Start Timer"
Private Sub Command4_Click()
WriteLn "Stop timer"
Private Sub Command5_Click()
WriteLn "Sleep 5 seconds..."
Private Sub foo_Click(ByVal x As Long, ByVal y As Long)
MsgBox "foo_click(" & x & ", " & y & ")"
Private Sub foo_Pulse(ByVal nTicks As Long)
WriteLn "nTicks=" & nTicks & ", Count=" & foo.GetCount
Private Sub Form_Load()
Set foo = New ClassTest1.ComClass3
And, here is the VB.NET code for the COM Server DLL...
Option Explicit On
Option Strict On
Public Delegate Sub ClickDelegate(x As Integer, y As Integer)
Public Delegate Sub ResizeDelegate()
Public Delegate Sub PulseDelegate(nTicks As Integer)
Public Interface IComClass3Events
Sub Click(x As Integer, y As Integer)
Sub Pulse(nTicks As Integer)
Public Interface IComClass3
'Subs, Functions, Properties go here
'No subs, functions, or events need to be declared here
'to make our events work properly in COM
<ComClass(ComClass3.ClassId, ComClass3.InterfaceId, ComClass3.EventsId)>
Public Class ComClass3 : Implements IComClass3
#Region "COM GUIDs"
' These GUIDs provide the COM identity for this class
' and its COM interfaces. If you change them, existing
' clients will no longer be able to access the class.
Public Const ClassId As String = "0f3173c9-d101-40db-9317-333e5b1948be"
Public Const InterfaceId As String = "0dd277ac-567d-4899-8b60-f8b67a8dac70"
Public Const EventsId As String = "fff2962c-52a8-486a-b68f-8af64a419d41"
Public Event Click As ClickDelegate
Public Event Resize As ResizeDelegate
Public Event Pulse As PulseDelegate
Private _Enabled As Boolean
Private _Counter As Integer
Dim WithEvents myTimer As New Timers.Timer
' A creatable COM class must have a Public Sub New()
' with no parameters, otherwise, the class will not be
' registered in the COM registry and cannot be created
' via CreateObject.
Public Sub New()
Public Sub CauseClickEvent(x As Integer, y As Integer)
RaiseEvent Click(x, y)
Public Sub CauseResizeEvent()
Public Function GetCount() As Integer
GetCount = _Counter
Public Sub CausePulse()
Public Sub StartTicker()
_Enabled = True
myTimer.Interval = 1000
myTimer.Enabled = True
Public Sub StopTicker()
_Enabled = False
myTimer.Enabled = False
Private Sub myTimer_Elapsed(sender As Object, e As Timers.ElapsedEventArgs) Handles myTimer.Elapsed
_Counter = _Counter + 1