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

发布于 2024-08-09 13:05:49 字数 487 浏览 5 评论 0原文

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

I know I can receive messages with the code below in c#, how do I send to vb6, and receive in vb6, and send from 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);
    }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

睡美人的小仙女 2024-08-16 13:05:49

在开始之前,我想说我同意 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 函数,可以修改 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。然而,发送复杂的类型和字符串要困难得多。我看到您为此创建了一个单独的问题,因此我将在那里提供答案。

参考:如何将结构从 C# 发送到 VB6,以及从​​ VB6 发送到 C#?

Before I start, I'd like to say that I concur with MarkJ. COM Interop will make your life much easier and will not require you to do as much work.

SendMessage is the preferred way to call one side or the other via Windows Message handlers. PostMessage is tough to use with complex types, as the lifetime of data associated with a windows message in both .NET and VB6 is difficult to manage while the message is queued, and completion of the message is unknown unless you implement some form of callback mechanism.

Anyhow, sending windows messages from anywhere to a C# window merely requires that you know the HWND of the C# window that is to receive the message. Your snippet looks to be correct as a handler, except that the switch statement should check against the Msg parameter first.

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

Retrieving a window handle from a C# Form, Window, or Control can be done via the .Handle property.

Control.Handle Property @ MSDN

We'll assume here that you have some way of transferring the window handle from C# to VB6.

From VB6, the signature of the SendMessage window is the following:

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

To call it, you would do something like the following. For brevity, uMsg is WM_APP (32768), wParam/lParam are 0:

Dim retval As Long
retval = SendMessage(hWnd, 32768, 0, 0)

Likewise, sending a message from C# is similar. To get the HWND of a window in VB6, use the .hWnd property of the window in VB6 that should receive the message.

As it appears that you are using your own set of message identifiers, there are extra steps to handle custom message identifiers in VB6. Most people handle this by subclassing a form window, and using the subclass procedure to filter those message. I've included sample code to demonstrate C# to VB6 since handling custom messages is trickier in VB6.

Here is the source code for the pair of test programs, a C# library and a VB6 Forms Project. The C# library should be configured with 'Register for COM Interop' and 'Make Assembly COM-Visible' in the Project settings.

First, the C# library. This library contains a single COM component that will be visible to VB6 as the type 'CSMessageLibrary.TestSenderSimple'. Note that you need to include a P/Invoke signature (like the VB6 method) for SendMessage.

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

Now, on the VB6 side, you will need to add support to subclass a window. Aside from better solutions that can be applied per-window, we'll just go with something that shows how to setup a single window.

First, to run this sample, make sure you have built the C# application and properly registered it with COM. Then add a reference from VB6 to the .tlb file that is alongside the C# output. You'll find this in the bin/Debug or bin/Release directory under the C# project.

The following code should be placed into a Module. In my test project I used a module called 'Module1'. The following definitions should be noted in this Module.

WM_APP - Used as a custom message identifier that will be interference-free.
GWL_WNDPROC - Constant that is used for SetWindowLong to request modification to the window handler.
SetWindowLong - Win32 function that can modify special attributes on windows.
CallWindowProc - Win32 function that can relay windows messages to a designated window handler (function).
SubclassWindow - Module function to setup subclassing for a designated window.
UnsubclassWindow - Module function to tear down subclassing for a designated window.
SubWndProc - Module function that will be inserted via subclassing, to allow us to intercept custom windows messages.

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

In the test form, I've wired up an instance of the test C# object as a member of the form. The form includes a button whose id is 'Command1'. The subclass is setup when the form loads, and then removed when the form is closed.

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

Sending numeric arguments that fit in 4 bytes is trivial, either as the wParam or lParam. However, sending complex types and strings is much tougher. I see that you've created a separate question for that, so I will provide answers to that over there.

REF: How do I send a struct from C# to VB6, and from VB6 to C#?

ぇ气 2024-08-16 13:05:49

要在 VB6 中发送,您需要使用 API 调用 (SendMessage< /a> 或 PostMessage)。要在 VB6 中接收,您需要使用子类化(复杂 - 这是

您是否考虑过使用 COM Interop 来代替?这是一种比 Windows 消息更容易在 VB6 和 C# 之间进行通信的方法。

To send in VB6 you need to use an API call (SendMessage or PostMessage). To receive in VB6 you need to use subclassing (complicated - here's the best way I know).

Have you considered using COM Interop instead? It's a much easier way to communicate between VB6 and C# than windows messages.

你没皮卡萌 2024-08-16 13:05:49

使用 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 事件中依次处理。当您的事件被调用时,您确保当前窗口是一个文本框,如果是,则选择它。

Use the PostMessage Windows API function.

At the beginning of your class:

[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;

Then handle the message by overriding the class's 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.

Then in your Enter event:

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

This works because you force your custom processing to occur after Windows does its default processing of the Enter event and it's associated mouse-handling. You put your request on the message queue and it is handled in turn, in the WndProc event. When your event is called, you make sure the current window is a text box and select it if it is.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文