Introduction
There are quite a few articles on inter-process communication for VB6, and quite a few articles for inter-process communication in .NET. But it is an interesting mix using managed and unmanaged environments for inter-process communication. I have finally got it working after some issues with VB6. :-)
Background
My company is currently developing new applications in .NET, but we still have to support our (main company app) legacy VB6 applications. We had started to use COM-interop from VB6 to VB.NET components, but struck some bugs trying to automate applications from each other. Having a look around, I figured that Window Messaging was independent of VB and .NET environments. So then began my search of code samples and explanations of doing this. Some explained VB6 messaging, some explained .NET messaging, but couldn't find any that did both.
Using the code
For an explanation on the VB.NET sub-classing code, refer to this CodeProject article. I have taken the code straight from here and see no point in explaining it again.
VB6: Form interface:
Private Sub Form_Load()
....
modMessaging.InitWindowMessaging
End Sub
Private Sub Form_Unload(Cancel As Integer)
modMessaging.StopWindowMessaging
End Sub
VB6: Messaging
Public Function InitWindowMessaging()
hWindowHandle = CreateWindowEx(0, "STATIC", VB6_WINDOWTITLE_SERVER, _
0, 0, 0, 0, 0, 0, 0, App.hInstance, ByVal 0&)
hOldProc = SetWindowLongApi(hWindowHandle,_
GWL_WNDPROC, AddressOf ProcessWindowMessages)
WindowMessagingInitialised = True
End Function
Public Function StopWindowMessaging()
Call SetWindowLongApi(hWindowHandle, GWL_WNDPROC, hOldProc)
End Function
Public Function SendMessageToVBNET()
Dim hwndTarget As Long
Dim MessageId As Long
If WindowMessagingInitialised = False Then
InitWindowMessaging
End If
hwndTarget = VBNET_WindowHandle
MessageId = VB6_TO_DOTNET_MessageId
If
hwndTarget <> 0 Then
Call PostMessage(hwndTarget, MessageId, 0, 0)
End If
End Function
Function to process messages. If not one of our custom messages,
Private Function ProcessWindowMessages(ByVal hwnd As Long, _
ByVal wMsg As Long, _
ByVal wParam As Long, _
ByVal lParam As Integer) As Long
If wMsg = DOTNET_TO_VB6_MessageId Then
MsgBox "window message received from DOTNET environment"
Else
ProcessWindowMessages = CallWindowProc(hOldProc, _
hwnd, wMsg, wParam, lParam)
End If
End Function
Points of Interest
One thing which I think is quite nice about this implementation is that it creates its own windows with user defined names. So you have global constants defined in VB6 and VB.NET for the window names. You can then rename your application, and the communication won't break.
The RegisterWindowMessage
API call provides a system-wide MessageId
. Subsequent calls to RegisterWindowMessage
will return the same MessageId
.
VB6: One thing to note that is vitally important is that you need to disconnect your custom Message Handler before the application closes, otherwise it starts to handle messages in the VB IDE. It was quite hard to track this problem down because it would work nicely when I ran the executable, but once inside the IDE, it would crash VB. Once I started writing debug.print
statements in my Message Handler function, all became clear. Somehow, I was not handing message handling responsibilities to the MainForm
.
VB.NET: Nothing much to note here. So much easier in .NET than VB. :-)
Links used while developing:
Version 3 now has VB6App2 source code. You can now use Window Messaging:
- VB6 -> VB.NET
- VB6 -> C#
- VB6 -> VB6App2
- VB6App2 -> VB6
- VB.NET -> VB6
- VB.NET -> C#
- C# -> VB6
- C# -> VB.NET
History
- Initial release to The Code Project
- Updated source and demos to include C# project.
- Updated source and demos to include VB6 to VB6App2.