Как мне отправлять / получать сообщения Windows между VB6 и 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);
}
Решение
Прежде чем я начну, я хотел бы сказать, что я согласен с Марком Джеем.COM-взаимодействие значительно облегчит вашу жизнь и не потребует от вас выполнения такого большого объема работы.
SendMessage - это предпочтительный способ вызова той или иной стороны через обработчики сообщений Windows.postMessage сложно использовать со сложными типами, поскольку время жизни данных, связанных с сообщением Windows, в обоих случаях ограничено .NET и VB6 сложно управлять, пока сообщение находится в очереди, и завершение сообщения неизвестно, если вы не реализуете какую-либо форму механизма обратного вызова.
В любом случае, отправка сообщений Windows из любого места в окно C # просто требует, чтобы вы знали HWND окна C #, которое должно получить сообщение.Ваш фрагмент выглядит корректным как обработчик, за исключением того, что оператор 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.
Элемент управления.Обрабатывать свойство @ 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 # аналогична.Чтобы получить HWND окна в VB6, используйте .Свойство hWnd окна в VB6, которое должно получить сообщение.
Поскольку кажется, что вы используете свой собственный набор идентификаторов сообщений, существуют дополнительные шаги для обработки пользовательских идентификаторов сообщений в VB6.Большинство людей справляются с этим, создавая подкласс окна формы и используя процедуру подкласса для фильтрации этих сообщений.Я включил пример кода для демонстрации C # в VB6, поскольку обработка пользовательских сообщений в VB6 сложнее.
Вот исходный код для пары тестовых программ, библиотеки C # и проекта VB6 Forms.Библиотека C # должна быть настроена с помощью 'Register for COM Interop' и 'Make Assembly COM-Visible' в настройках проекта.
Во-первых, библиотека C #.Эта библиотека содержит единственный COM-компонент, который будет виден VB6 как тип 'CSMessageLibrary.TestSenderSimple'.Обратите внимание, что вам необходимо включить подпись P / Invoke (например, метод VB6) для 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);
}
}
Теперь, на стороне VB6, вам нужно будет добавить поддержку для подкласса window.Помимо улучшенных решений, которые можно применять для каждого окна, мы просто рассмотрим кое-что, что показывает, как настроить одно окно.
Во-первых, чтобы запустить этот пример, убедитесь, что вы создали приложение на C # и правильно зарегистрировали его в COM.Затем добавьте ссылку из VB6 в файл .tlb, который находится рядом с выводом C #.Вы найдете это в каталоге bin /Debug или bin / Release в проекте C #.
Следующий код должен быть помещен в модуль.В моем тестовом проекте я использовал модуль под названием "Module1".В этом Модуле следует отметить следующие определения.
WM_APP - используется в качестве пользовательского идентификатора сообщения, который не будет создавать помех.
GWL_WNDPROC - константа, которая используется для SetWindowLong для запроса изменения обработчика окна.
SetWindowLong - функция Win32, которая может изменять специальные атрибуты в Windows.
CallWindowProc - функция Win32, которая может передавать сообщения Windows назначенному обработчику окон (функции).
SubclassWindow - функция модуля для настройки подклассов для определенного окна.
UnsubclassWindow - функция модуля для удаления подклассов для указанного окна.
SubWndProc - функция модуля, которая будет вставлена через подкласс, чтобы позволить нам перехватывать пользовательские сообщения Windows.
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
В тестовой форме я подключил экземпляр объекта test C # в качестве члена формы.Форма включает в себя кнопку с идентификатором '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 .Однако отправка сложных типов и строк намного сложнее.Я вижу, что вы создали для этого отдельный вопрос, поэтому я приведу ответы на него там.
ССЫЛКА:Как мне отправить структуру из C # в VB6, а из VB6 - в C #?
Другие советы
Для отправки в VB6 вам необходимо использовать вызов API ( SendMessage или PostMessage). Чтобы получить в VB6 вам нужно использовать подклассы (сложный - вот лучший способ, которым я знаю ). Р>
Рассматривали ли вы возможность использования COM-взаимодействия вместо? Это намного более простой способ связи между 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. Когда вызывается ваше событие, убедитесь, что текущее окно является текстовым полем, и выберите его, если оно есть.