我知道我可以使用c#中的代码接收消息,如何发送到vb6,并在vb6中接收,并从vb6发送?
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")] protected override void WndProc(ref Message m) { int _iWParam = (int)m.WParam; int _iLParam = (int)m.LParam; switch ((ECGCardioCard.APIMessage)m.WParam) { // handling code goes here } base.WndProc(ref m); }
meklarian.. 6
在开始之前,我想说我同意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); }
从C#Form,Window或Control检索窗口句柄可以通过.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中有一些额外的步骤来处理自定义消息标识符.大多数人通过子类化表单窗口并使用子类过程来过滤这些消息来处理这个问题.我已经包含了示例代码来演示C#到VB6,因为在VB6中处理自定义消息比较棘手.
这是测试程序对的源代码,C#库和VB6 Forms项目.应在项目设置中为C#库配置'Register for COM Interop'和'Make Assembly COM-Visible'.
首先是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函数,可以修改Windows上的特殊属性.
CallWindowProc - Win32函数,可以将Windows消息中继到指定的窗口处理程序(函数).
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#?
在开始之前,我想说我同意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); }
从C#Form,Window或Control检索窗口句柄可以通过.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中有一些额外的步骤来处理自定义消息标识符.大多数人通过子类化表单窗口并使用子类过程来过滤这些消息来处理这个问题.我已经包含了示例代码来演示C#到VB6,因为在VB6中处理自定义消息比较棘手.
这是测试程序对的源代码,C#库和VB6 Forms项目.应在项目设置中为C#库配置'Register for COM Interop'和'Make Assembly COM-Visible'.
首先是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函数,可以修改Windows上的特殊属性.
CallWindowProc - Win32函数,可以将Windows消息中继到指定的窗口处理程序(函数).
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#?