4

我知道我可以在 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);
    }
4

3 回答 3

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);
}

可以通过 .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#?

于 2009-11-24T20:30:04.593 回答
3

要在 VB6 中发送,您需要使用 API 调用(SendMessage或 PostMessage)。要在 VB6 中接收,您需要使用子类化(复杂 - 这是我知道的最好方法)。

您是否考虑过改用COM 互操作?与 Windows 消息相比,这是一种在 VB6 和 C# 之间进行通信的更简单的方法。

于 2009-10-29T17:46:57.920 回答
1

使用 PostMessage Windows API 函数。

在课程开始时:

[DllImport("User32.dll", EntryPoint="PostMessage")]
private static extern int PostMessage(int hWnd, int Msg, int wParam, int lParam);

const int WM_USER = 0x0400;
const int CM_MARK = WM_USER + 1;

然后通过覆盖类的 WndProc 来处理消息。

protected override void WndProc(ref Message m)
{
  if (m.Msg == CM_MARK) {
    if (this.ActiveControl is TextBox) {
      ((TextBox)this.ActiveControl).SelectAll();
    }
  }
  base.WndProc(ref m);
} // end sub.

然后在您的 Enter 事件中:

private void txtMedia_Enter(object sender, EventArgs e)
{
  PostMessage(Handle.ToInt32(), CM_MARK, 0, 0);
} // end sub.

这是因为您强制您的自定义处理在 Windows 对 Enter 事件进行默认处理并且它与鼠标处理相关联之后发生。您将请求放在消息队列中,然后在 WndProc 事件中依次处理它。调用事件时,请确保当前窗口是一个文本框,如果是则选择它。

于 2011-05-06T13:31:57.683 回答