在开始之前,我想说我同意 MarkJ。COM Interop 将使您的生活更轻松,并且不需要您做太多的工作。
SendMessage 是通过 Windows 消息处理程序调用一侧或另一侧的首选方式。PostMessage 很难用于复杂类型,因为在 .NET 和 VB6 中与 Windows 消息关联的数据的生命周期在消息排队时很难管理,并且除非您实现某种形式的回调机制,否则消息的完成是未知的.
无论如何,从任何地方向 C# 窗口发送 windows 消息只需要您知道要接收消息的 C# 窗口的 HWND。作为处理程序,您的代码段看起来是正确的,除了 switch 语句应该首先检查Msg参数。
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);
}
可以通过 .Handle 属性从 C# 窗体、窗口或控件中检索窗口句柄。
Control.Handle 属性@MSDN
我们在这里假设您有某种方法可以将窗口句柄从 C# 传输到 VB6。
从 VB6 开始,SendMessage 窗口的签名如下:
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
要调用它,您可以执行以下操作。为简洁起见,uMsg 为 WM_APP (32768),wParam/lParam 为 0:
Dim retval As Long
retval = SendMessage(hWnd, 32768, 0, 0)
同样,从 C# 发送消息也是类似的。要在 VB6 中获取窗口的 HWND,请使用 VB6 中应接收消息的窗口的 .hWnd 属性。
由于您使用的是自己的一组消息标识符,因此在 VB6 中处理自定义消息标识符有额外的步骤。大多数人通过子类化表单窗口并使用子类过程来过滤这些消息来处理这个问题。我已经包含了示例代码来演示 C# 到 VB6,因为在 VB6 中处理自定义消息比较棘手。
这是这对测试程序、一个 C# 库和一个 VB6 Forms 项目的源代码。C# 库应在项目设置中使用“注册 COM 互操作”和“使程序集 COM 可见”进行配置。
首先,C# 库。这个库包含一个单一的 COM 组件,它对 VB6 可见,类型为“CSMessageLibrary.TestSenderSimple”。请注意,您需要为 SendMessage 包含一个 P/Invoke 签名(如 VB6 方法)。
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);
}
}
现在,在 VB6 方面,您需要添加对子类窗口的支持。除了可以在每个窗口中应用的更好的解决方案之外,我们还将介绍如何设置单个窗口的内容。
首先,要运行此示例,请确保您已构建 C# 应用程序并已将其正确注册到 COM。然后将 VB6 中的引用添加到 C# 输出旁边的 .tlb 文件。您可以在 C# 项目下的 bin/Debug 或 bin/Release 目录中找到它。
以下代码应放入模块中。在我的测试项目中,我使用了一个名为“Module1”的模块。本模块中应注意以下定义。
WM_APP - 用作不受干扰的自定义消息标识符。
GWL_WNDPROC - SetWindowLong 用于请求修改窗口处理程序的常量。
SetWindowLong - Win32 函数,可以修改窗口的特殊属性。
CallWindowProc - Win32 函数,可以将窗口消息中继到指定的窗口处理程序(函数)。
SubclassWindow - 为指定窗口设置子类的模块函数。
UnsubclassWindow - 用于拆除指定窗口的子类化的模块函数。
SubWndProc - 将通过子类化插入的模块函数,以允许我们拦截自定义窗口消息。
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
在测试表单中,我将测试 C# 对象的一个实例连接为表单的成员。该表单包含一个 ID 为“Command1”的按钮。子类在表单加载时设置,然后在表单关闭时删除。
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
发送适合 4 个字节的数字参数是微不足道的,无论是作为 wParam 还是 lParam。但是,发送复杂的类型和字符串要困难得多。我看到您为此创建了一个单独的问题,所以我将在那里提供答案。
REF:如何将结构从 C# 发送到 VB6,以及从 VB6 发送到 C#?