2009-10-29 104 views
3

我知道我可以用c#下面的代码接收消息,如何发送到vb6,并在vb6中接收,并从vb6发送?如何在VB6和c#之间发送/接收Windows消息?

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

回答

5

在我开始之前,我想说我赞同MarkJ。 COM Interop将让你的生活变得更加轻松,并且不会要求你做很多工作。

SendMessage是通过Windows消息处理程序调用一方或另一方的首选方法。 PostMessage很难与复杂类型一起使用,因为在消息排队时,与.NET和VB6中的Windows消息相关联的数据的生命周期难以管理,并且消息的完成未知,除非您实现某种形式的回调机制。

无论如何,从任何位置将窗口消息发送到C#窗口只需要知道接收消息的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#表单窗口句柄,窗口,或控制可通过.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中的自定义消息标识符。大多数人通过子窗体窗口来处理这个问题,并使用子类过程来过滤这些消息。因为在VB6中处理自定义消息比较棘手,所以我已经包含了演示C#到VB6的示例代码。

这是一对测试程序,C#库和VB6 Forms项目的源代码。 C#库应该在项目设置中配置'Register for COM Interop'和'Make Assembly COM-Visible'。

首先是C#库。该库包含一个单独的COM组件,该组件将作为类型“CSMessageLibrary.TestSenderSimple”在VB6中可见。请注意,您需要为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: How do I send a struct from C# to VB6, and from VB6 to C#?

+1

+1。通过http://visualstudiomagazine.com/articles/2009/07/16/subclassing-the-xp-way进行VB6子类化有一个更“对象”的方法。aspx – MarkJ

+0

我采用这种方法只是为了缩短样本。尽管如此,该环节的技术绝对优越。 – meklarian

+0

真是太棒了! +1 – used2could

3

要在VB6送你需要使用一个API调用(SendMessage或PostMessage的)。要在VB6中接收,你需要使用子类化(复杂的 - 这里是best way I know)。

您是否考虑过使用COM Interop代替?与Windows消息相比,VB6和C#之间的通信更容易。

+0

+1 COM和VB就像豌豆和胡萝卜。 – kenny

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. 

然后在您输入的事件:

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

这工作,因为你强迫你的自定义处理Windows不会Enter事件的它的默认处理后,会出现与它相关的鼠标操作。您将请求放在消息队列中,并在WndProc事件中依次处理。当你的事件被调用时,你要确保当前窗口是一个文本框,如果是的话就选择它。