在我开始之前,我想说我赞同MarkJ。 COM Interop将让你的生活变得更加轻松,并且不会要求你做很多工作。
SendMessage是通过Windows消息处理程序调用一方或另一方的首选方法。 PostMessage很难与复杂类型一起使用,因为在消息排队时,与.NET和VB6中的Windows消息相关联的数据的生命周期难以管理,并且消息的完成未知,除非您实现某种形式的回调机制。
无论如何,从任何位置将窗口消息发送到C#窗口只需要知道接收消息的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);
}
检索从C#表单窗口句柄,窗口,或控制可通过.Handle属性来完成。
Control.Handle Property @ 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中的自定义消息标识符。大多数人通过子窗体窗口来处理这个问题,并使用子类过程来过滤这些消息。因为在VB6中处理自定义消息比较棘手,所以我已经包含了演示C#到VB6的示例代码。
这是一对测试程序,C#库和VB6 Forms项目的源代码。 C#库应该在项目设置中配置'Register for COM Interop'和'Make Assembly COM-Visible'。
首先是C#库。该库包含一个单独的COM组件,该组件将作为类型“CSMessageLibrary.TestSenderSimple”在VB6中可见。请注意,您需要为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: How do I send a struct from C# to VB6, and from VB6 to C#?
+1。通过http://visualstudiomagazine.com/articles/2009/07/16/subclassing-the-xp-way进行VB6子类化有一个更“对象”的方法。aspx – MarkJ
我采用这种方法只是为了缩短样本。尽管如此,该环节的技术绝对优越。 – meklarian
真是太棒了! +1 – used2could