如何发送VB6与C#/接收窗口消息? [英] How do I send/receive windows messages between VB6 and c#?

查看:513
本文介绍了如何发送VB6与C#/接收窗口消息?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我知道我可以用下面的代码在C#中,我怎么送VB6接收邮件,并在VB6接收,并从VB6派?



  [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand,NAME =将FullTrust)] 
保护覆盖无效的WndProc(参考消息M)
{

INT _iWParam =(INT)m.WParam;
INT _iLParam =(INT)m.LParam;
开关((ECGCardioCard.APIMessage)m.WParam)
{
//处理代码到这里
}
base.WndProc(REF米);
}


解决方案

在我开始之前,我ð喜欢说我同意MarkJ。 COM互操作会让你的生活变得更轻松,也不会要求你做尽可能多的工作。



SendMessage函数是调用一方或通过Windows消息处理其他的首选方式。 PostMessage的是艰难与复杂类型使用,与Windows消息在相关的数据的生命周期.NET和VB6是难以管理,而该消息在排队,除非你实现某种形式的回调机制消息的完成是未知



总之,从任何地方到一个C#窗口发送消息窗口只要求你知道这是接收消息的C#窗口的HWND。您的片段看起来是正确的,因为一个处理程序,除了switch语句应该检查对消息参数第一。

 保护覆盖无效的WndProc(参考消息M)
{

INT _iWParam =(INT)m.WParam;
INT _iLParam =(INT)m.LParam;
开关((ECGCardioCard.APIMessage)m.Msg)
{
//处理代码到这里
}
base.WndProc(REF米);
}



从C#的形式,窗口或控制可以做到的检索窗口句柄通过.Handle属性。



Control.Handle物业@ MSDN



我们将这里假设你的窗口句柄从C#转移到VB6的一些方法。



从VB6的SendMessage函数窗口的签名如下:

 私人声明函数库SendMessage函数USER32.DLL_ 
(BYVAL的hWnd长,BYVAL uMsg长,_
BYVAL的wParam长,BYVAL lParam的长)只要

要调用它,你会做一些像下面这样。为了简便起见,uMsg是WM_APP(32768),wParam参数/ lParam的是0:

 昏暗RETVAL只要
RETVAL = SendMessage消息(HWND,32768,0,0)

同样,发送从C#的消息是相似的。要获得VB6一个窗口的HWND,使用窗口中VB6的.hWnd属性,应收到该邮件。



随着看来,你使用的是自己消息标识符的设置,还有额外的步骤来处理VB6自定义消息标识符。多数人通过继承表单窗口,以及使用该子类过程来筛选那些消息处理这个问题。我已经包括示例代码来演示C#来VB6因为处理自定义消息是在VB6棘手。



下面是一对测试程序,一个C#库的源代码和VB6窗体项目。 C#的图书馆应该有'注册为COM Interop和配置让大会COM可见在项目设置。



首先,C#库。这个库包含了一个COM组件,这将是可见的VB6作为类型CSMessageLibrary.TestSenderSimple。需要注意的是,你需要包括的P / Invoke的SendMessage函数签名(如VB6法)

 使用系统; 
使用System.Collections.Generic;
使用System.Text;使用System.Runtime.InteropServices
;

命名空间CSMessageLibrary
{
[标记有​​ComVisible特性(真)]
公共接口ITestSenderSimple
{
//注意:不能使用的IntPtr因为它不是VB6兼容
INT hostwindow {获得;设置;}
无效DoTest(INT号);
}

[标记有​​ComVisible特性(真)]
公共类TestSenderSimple:ITestSenderSimple
{
公共TestSenderSimple()
{
m_HostWindow = IntPtr.Zero;
m_count = 0;
}

IntPtr的m_HostWindow;
INT m_count;

#地区ITestSenderSimple会员
公众诠释hostwindow
{
{返回(INT)m_HostWindow; }
集合{m_HostWindow =(IntPtr的)价值; }
}

公共无效DoTest(INT数)
{
m_count ++;

// WM_APP为0x8000(32768十进制)
IntPtr的RETVAL = SendMessage函数(
m_HostWindow,为0x8000,(IntPtr的)m_count,(IntPtr的)号码);
}
#endregion

函数[DllImport(user32.dll中,字符集= CharSet.Auto)]
的extern公共静态的IntPtr的SendMessage(
的IntPtr HWND,UINT味精,IntPtr的WPARAM,LPARAM的IntPtr);
}
}

现在,在VB6的一面,你需要添加支持的子类的窗口。除了更好的解决方案,可每个窗口得到应用,我们还是要的东西,展示了如何设置一个窗口。



首先,要运行这个样本,确保你已经建立了C#应用程序,并妥善COM注册它。然后从VB6添加引用.tlb文件是沿着C#输出。你会在C#项目下的bin / Debug或Bin / Release目录找到。



下面的代码应该放到一个模块。在我的测试项目中,我使用了一种叫做模块1的模块。 。下面的定义应该在此模块中需要注意



WM_APP - 用作一个自定义的消息标识符,这将是无干扰的结果
GWL_WNDPROC - 恒是用于SetWindowLong函数来请求修改到窗口处理结果,
SetWindowLong函数 - Win32函数,可以在窗口修改特殊属性结果,
CallWindowProc的 - 这可以中继窗口消息给Win32函数指定窗口句柄(功能)结果
调用SubclassWindow - 模块功能设置为子类指定窗口结果
UnsubclassWindow - 模块功能推倒继承了指定窗口结果$。 b $ b SubWndProc - 将通过子类插入模块功能,让我们截取自定义Windows消息。

 公共常量WM_APP只要= 32768 
私人常量GWL_WNDPROC =(-4)
私人procOld作为龙

私人声明函数库CallWindowProc的USER32.DLL别名CallWindowProcA_
(BYVAL lpPrevWndFunc长,BYVAL的hWnd长,BYVAL uMsg长,_
BYVAL wParam中长,BYVAL lParam的长)只要

私人声明函数库调用SetWindowLongUSER32.DLL别名SetWindowLongA_
(BYVAL的hWnd长,BYVAL参数nIndex长,BYVAL dwNewLong长)只要

公用Sub调用SubclassWindow(BYVAL的hWnd长)
procOld = SetWindowLong函数(HWND,GWL_WNDPROC,AddressOf SubWndProc)
端子

公用Sub UnsubclassWindow(BYVAL的hWnd长)
procOld = SetWindowLong函数(HWND,GWL_WNDPROC,procOld)
端子

专用功能SubWndProc(_
BYVAL的hWnd长,_
BYVAL IMSG长,_
BYVAL的wParam长,_
BYVAL lParam的长)只要

。如果的hWnd = Form1.hWnd然后
如果IMSG = WM_APP然后
昏暗strInfo作为字符串
strInfo =的wParam:&放大器; CStr的(wParam中)及vbCrLf&安培; lParam的:&放大器; CStr的(lParam的)

呼叫MSGBOX(strInfo,vbOKOnly,WM_APP收到!)

SubWndProc = TRUE
退出功能
端如果
端如果

SubWndProc = CallWindowProc的(procOld,的hWnd,IMSG来说,wParam,lParam的)
端功能

在测试的形式,我已经连接好测试C#对象的实例作为窗体中的一员。该表格​​包含一个按钮,id为命令1。子类是安装程序时,当窗体关闭的形式加载,然后删除。

 昏暗CSharpClient作为新CSMessageLibrary.TestSenderSimple 

私人小组Command1_Click()
CSharpClient.DoTest(42)
端子

私人小组的Form_Load()
CSharpClient.hostwindow = Form1中.hWnd
Module1.SubclassWindow(Form1.hWnd)
端子

私人小组Form_Unload(取消作为整数)
CSharpClient.hostwindow = 0
模块1 .UnsubclassWindow(Form1.hWnd)
端子

发送,适合在4个字节的数字参数是平凡的,无论是作为的wParam或lParam的。然而,发送复杂类型和字符串是更加强硬。我看到你已经创建为一个单独的问题,所以我会提供答案那边。



REF:如何发送一个结构从C#到VB6,和从VB6到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?

    [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. 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#?

这篇关于如何发送VB6与C#/接收窗口消息?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆