简体   繁体   English

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

[英]How do I send/receive windows messages between VB6 and c#?

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

Before I start, I'd like to say that I concur with MarkJ. 在开始之前,我想说我同意MarkJ。 COM Interop will make your life much easier and will not require you to do as much work. COM Interop将使您的生活更轻松,不需要您做太多的工作。

SendMessage is the preferred way to call one side or the other via Windows Message handlers. SendMessage是通过Windows消息处理程序调用一方或另一方的首选方法。 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. PostMessage很难用于复杂类型,因为在消息排队时很难管理与.NET和VB6中的Windows消息相关的数据的生命周期,并且除非您实现某种形式的回调机制,否则消息的完成是未知的。

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. 无论如何,从任何地方向C#窗口发送Windows消息只需要知道要接收消息的C#窗口的HWND。 Your snippet looks to be correct as a handler, except that the switch statement should check against the Msg parameter first. 您的代码段看起来是正确的处理程序,除了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);
}

Retrieving a window handle from a C# Form, Window, or Control can be done via the .Handle property. 从C#Form,Window或Control检索窗口句柄可以通过.Handle属性完成。

Control.Handle Property @ MSDN Control.Handle Property @ MSDN

We'll assume here that you have some way of transferring the window handle from C# to VB6. 我们假设你有一些方法可以将窗口句柄从C#转移到VB6。

From VB6, the signature of the SendMessage window is the following: 从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

To call it, you would do something like the following. 要调用它,您可以执行以下操作。 For brevity, uMsg is WM_APP (32768), wParam/lParam are 0: 为简洁起见,uMsg为WM_APP(32768),wParam / lParam为0:

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

Likewise, sending a message from C# is similar. 同样,从C#发送消息也是类似的。 To get the HWND of a window in VB6, use the .hWnd property of the window in VB6 that should receive the message. 要获取VB6中窗口的HWND,请使用VB6中应接收消息的窗口的.hWnd属性。

As it appears that you are using your own set of message identifiers, there are extra steps to handle custom message identifiers in VB6. 由于您似乎正在使用自己的一组消息标识符,因此在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. 我已经包含了示例代码来演示C#到VB6,因为在VB6中处理自定义消息比较棘手。

Here is the source code for the pair of test programs, a C# library and a VB6 Forms Project. 这是测试程序对的源代码,C#库和VB6 Forms项目。 The C# library should be configured with 'Register for COM Interop' and 'Make Assembly COM-Visible' in the Project settings. 应在项目设置中为C#库配置'Register for COM Interop'和'Make Assembly COM-Visible'。

First, the C# library. 首先是C#库。 This library contains a single COM component that will be visible to VB6 as the type 'CSMessageLibrary.TestSenderSimple'. 该库包含一个COM组件,VB6将其视为“CSMessageLibrary.TestSenderSimple”类型。 Note that you need to include a P/Invoke signature (like the VB6 method) for SendMessage. 请注意,您需要为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);
    }
}

Now, on the VB6 side, you will need to add support to subclass a window. 现在,在VB6方面,您需要添加对子窗口的支持。 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. 首先,要运行此示例,请确保已构建C#应用程序并使用COM正确注册它。 Then add a reference from VB6 to the .tlb file that is alongside the C# output. 然后将VB6中的引用添加到C#输出旁边的.tlb文件中。 You'll find this in the bin/Debug or bin/Release directory under the C# project. 您可以在C#项目下的bin / Debug或bin / Release目录中找到它。

The following code should be placed into a Module. 以下代码应放入模块中。 In my test project I used a module called 'Module1'. 在我的测试项目中,我使用了一个名为“Module1”的模块。 The following definitions should be noted in this Module. 本模块中应注明以下定义。

WM_APP - Used as a custom message identifier that will be interference-free. WM_APP - 用作无干扰的自定义消息标识符。
GWL_WNDPROC - Constant that is used for SetWindowLong to request modification to the window handler. GWL_WNDPROC - 用于请求修改窗口处理程序的SetWindowLong的常量。
SetWindowLong - Win32 function that can modify special attributes on windows. SetWindowLong - Win32函数,可以修改Windows上的特殊属性。
CallWindowProc - Win32 function that can relay windows messages to a designated window handler (function). CallWindowProc - Win32函数,可以将Windows消息中继到指定的窗口处理程序(函数)。
SubclassWindow - Module function to setup subclassing for a designated window. SubclassWindow - 用于为指定窗口设置子类的模块函数。
UnsubclassWindow - Module function to tear down subclassing for a designated window. UnsubclassWindow - 用于拆除指定窗口的子类的模块函数。
SubWndProc - Module function that will be inserted via subclassing, to allow us to intercept custom windows messages. 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

In the test form, I've wired up an instance of the test C# object as a member of the form. 在测试表单中,我已将测试C#对象的实例连接为表单的成员。 The form includes a button whose id is 'Command1'. 表单包含一个id为'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. 发送适合4个字节的数字参数是微不足道的,可以是wParam或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#? REF:如何将结构从C#发送到VB6,从VB6发送到C#?

To send in VB6 you need to use an API call ( SendMessage or PostMessage). 要发送VB6,您需要使用API​​调用( SendMessage或PostMessage)。 To receive in VB6 you need to use subclassing (complicated - here's the best way I know ). 要在VB6中接收,您需要使用子类化(复杂 - 这是我所知道最佳方式 )。

Have you considered using COM Interop instead? 您是否考虑过使用COM Interop It's a much easier way to communicate between VB6 and C# than windows messages. 在VB6和C#之间进行通信比在Windows消息中更容易。

Use the PostMessage Windows API function. 使用PostMessage Windows API函数。

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. 然后通过覆盖类的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: 然后在您的Enter事件中:

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. 这是有效的,因为在Windows执行Enter事件的默认处理及其相关的鼠标处理之后,您强制进行自定义处理。 You put your request on the message queue and it is handled in turn, in the WndProc event. 您将请求放在消息队列中,然后在WndProc事件中依次处理它。 When your event is called, you make sure the current window is a text box and select it if it is. 调用事件时,确保当前窗口是文本框,如果是,则选择它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM