如何在VB6和c#之间发送/接收Windows消息?

时间:2009-10-29 17:07:11

标签: c# vb6 wndproc

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

3 个答案:

答案 0 :(得分:5)

在开始之前,我想说我同意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函数,可以将窗口消息中继到指定的窗口处理程序(函数) 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 :(得分:3)

要发送VB6,您需要使用API​​调用(SendMessage或PostMessage)。要在VB6中接收,您需要使用子类(复杂 - 这里是best way I know)。

您是否考虑过使用COM Interop?在VB6和C#之间进行通信比在Windows消息中更容易。

答案 2 :(得分: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事件中依次处理它。调用事件时,确保当前窗口是文本框,如果是,则选择它。