2009-10-29 182 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事件中依次處理。當你的事件被調用時,你要確保當前窗口是一個文本框,如果是的話就選擇它。