[英]How do I send/receive windows messages between VB6 and c#?
我知道我可以使用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);
}
在開始之前,我想說我同意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);
}
從C#Form,Window或Control檢索窗口句柄可以通過.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中有一些額外的步驟來處理自定義消息標識符。 大多數人通過子類化表單窗口並使用子類過程來過濾這些消息來處理這個問題。 我已經包含了示例代碼來演示C#到VB6,因為在VB6中處理自定義消息比較棘手。
這是測試程序對的源代碼,C#庫和VB6 Forms項目。 應在項目設置中為C#庫配置'Register for COM Interop'和'Make Assembly COM-Visible'。
首先是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。 但是,發送復雜的類型和字符串要困難得多。 我看到你已經為此創建了一個單獨的問題,所以我會在那里提供答案。
要發送VB6,您需要使用API調用( SendMessage或PostMessage)。 要在VB6中接收,您需要使用子類化(復雜 - 這是我所知道的最佳方式 )。
您是否考慮過使用COM Interop ? 在VB6和C#之間進行通信比在Windows消息中更容易。
使用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事件中依次處理它。 調用事件時,確保當前窗口是文本框,如果是,則選擇它。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.