Before I start, I'd like to say that I concur with MarkJ. COM Interop will make your life much easier and will not require you to do as much work.
SendMessage is the preferred way to call one side or the other via Windows Message handlers. PostMessage is tough to use with complex types, as the lifetime of data associated with a windows message in both .NET and VB6 is difficult to manage while the message is queued, and completion of the message is unknown unless you implement some form of callback mechanism.
Anyhow, sending windows messages from anywhere to a C# window merely requires that you know the HWND of the C# window that is to receive the message. Your snippet looks to be correct as a handler, except that the switch statement should check against the Msg parameter first.
protected override void WndProc(ref Message m)
{
int _iWParam = (int)m.WParam;
int _iLParam = (int)m.LParam;
switch ((ECGCardioCard.APIMessage)m.Msg)
{
// handling code goes here
}
base.WndProc(ref m);
}
Retrieving a window handle from a C# Form, Window, or Control can be done via the .Handle property.
Control.Handle Property @ MSDN
We'll assume here that you have some way of transferring the window handle from C# to VB6.
From VB6, the signature of the SendMessage window is the following:
Private Declare Function SendMessage Lib "USER32.DLL" _
(ByVal hWnd As Long, ByVal uMsg As Long, _
ByVal wParam As Long, ByVal lParam As Long) As Long
To call it, you would do something like the following. For brevity, uMsg is WM_APP (32768), wParam/lParam are 0:
Dim retval As Long
retval = SendMessage(hWnd, 32768, 0, 0)
Likewise, sending a message from C# is similar. To get the HWND of a window in VB6, use the .hWnd property of the window in VB6 that should receive the message.
As it appears that you are using your own set of message identifiers, there are extra steps to handle custom message identifiers in VB6. Most people handle this by subclassing a form window, and using the subclass procedure to filter those message. I've included sample code to demonstrate C# to VB6 since handling custom messages is trickier in VB6.
Here is the source code for the pair of test programs, a C# library and a VB6 Forms Project. The C# library should be configured with 'Register for COM Interop' and 'Make Assembly COM-Visible' in the Project settings.
First, the C# library. This library contains a single COM component that will be visible to VB6 as the type 'CSMessageLibrary.TestSenderSimple'. Note that you need to include a P/Invoke signature (like the VB6 method) for SendMessage.
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace CSMessageLibrary
{
[ComVisible(true)]
public interface ITestSenderSimple
{
// NOTE: Can't use IntPtr because it isn't VB6-compatible
int hostwindow { get; set;}
void DoTest(int number);
}
[ComVisible(true)]
public class TestSenderSimple : ITestSenderSimple
{
public TestSenderSimple()
{
m_HostWindow = IntPtr.Zero;
m_count = 0;
}
IntPtr m_HostWindow;
int m_count;
#region ITestSenderSimple Members
public int hostwindow
{
get { return (int)m_HostWindow; }
set { m_HostWindow = (IntPtr)value; }
}
public void DoTest(int number)
{
m_count++;
// WM_APP is 0x8000 (32768 decimal)
IntPtr retval = SendMessage(
m_HostWindow, 0x8000, (IntPtr)m_count, (IntPtr)number);
}
#endregion
[DllImport("user32.dll", CharSet = CharSet.Auto)]
extern public static IntPtr SendMessage(
IntPtr hwnd, uint msg, IntPtr wparam, IntPtr lparam);
}
}
Now, on the VB6 side, you will need to add support to subclass a window. Aside from better solutions that can be applied per-window, we'll just go with something that shows how to setup a single window.
First, to run this sample, make sure you have built the C# application and properly registered it with COM. Then add a reference from VB6 to the .tlb file that is alongside the C# output. You'll find this in the bin/Debug or bin/Release directory under the C# project.
The following code should be placed into a Module. In my test project I used a module called 'Module1'. The following definitions should be noted in this Module.
WM_APP - Used as a custom message identifier that will be interference-free.
GWL_WNDPROC - Constant that is used for SetWindowLong to request modification to the window handler.
SetWindowLong - Win32 function that can modify special attributes on windows.
CallWindowProc - Win32 function that can relay windows messages to a designated window handler (function).
SubclassWindow - Module function to setup subclassing for a designated window.
UnsubclassWindow - Module function to tear down subclassing for a designated window.
SubWndProc - Module function that will be inserted via subclassing, to allow us to intercept custom windows messages.
Public Const WM_APP As Long = 32768
Private Const GWL_WNDPROC = (-4)
Private procOld As Long
Private Declare Function CallWindowProc Lib "USER32.DLL" Alias "CallWindowProcA" _
(ByVal lpPrevWndFunc As Long, ByVal hWnd As Long, ByVal uMsg As Long, _
ByVal wParam As Long, ByVal lParam As Long) As Long
Private Declare Function SetWindowLong Lib "USER32.DLL" Alias "SetWindowLongA" _
(ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Public Sub SubclassWindow(ByVal hWnd As Long)
procOld = SetWindowLong(hWnd, GWL_WNDPROC, AddressOf SubWndProc)
End Sub
Public Sub UnsubclassWindow(ByVal hWnd As Long)
procOld = SetWindowLong(hWnd, GWL_WNDPROC, procOld)
End Sub
Private Function SubWndProc( _
ByVal hWnd As Long, _
ByVal iMsg As Long, _
ByVal wParam As Long, _
ByVal lParam As Long) As Long
If hWnd = Form1.hWnd Then
If iMsg = WM_APP Then
Dim strInfo As String
strInfo = "wParam: " & CStr(wParam) & vbCrLf & "lParam: " & CStr(lParam)
Call MsgBox(strInfo, vbOKOnly, "WM_APP Received!")
SubWndProc = True
Exit Function
End If
End If
SubWndProc = CallWindowProc(procOld, hWnd, iMsg, wParam, lParam)
End Function
In the test form, I've wired up an instance of the test C# object as a member of the form. The form includes a button whose id is 'Command1'. The subclass is setup when the form loads, and then removed when the form is closed.
Dim CSharpClient As New CSMessageLibrary.TestSenderSimple
Private Sub Command1_Click()
CSharpClient.DoTest (42)
End Sub
Private Sub Form_Load()
CSharpClient.hostwindow = Form1.hWnd
Module1.SubclassWindow (Form1.hWnd)
End Sub
Private Sub Form_Unload(Cancel As Integer)
CSharpClient.hostwindow = 0
Module1.UnsubclassWindow (Form1.hWnd)
End Sub
Sending numeric arguments that fit in 4 bytes is trivial, either as the wParam or lParam. However, sending complex types and strings is much tougher. I see that you've created a separate question for that, so I will provide answers to that over there.
REF: How do I send a struct from C# to VB6, and from VB6 to C#?