Как отправлять/получать сообщения Windows между VB6 и С#?

Я знаю, что могу получать сообщения с кодом ниже в С#, как мне отправить на 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);
    }

person cabgef    schedule 29.10.2009    source источник


Ответы (3)


Прежде чем я начну, я хотел бы сказать, что согласен с MarkJ. COM Interop сделает вашу жизнь намного проще и не потребует от вас столько работы.

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# Form, Window или Control можно с помощью свойства .Handle.

Свойство Control.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# должна быть настроена с помощью «Зарегистрировать для COM-взаимодействия» и «Сделать сборку COM-видимой» в настройках проекта.

Во-первых, библиотека 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, вам нужно будет добавить поддержку подкласса окна. Помимо лучших решений, которые можно применять для каждого окна, мы просто рассмотрим то, что показывает, как настроить отдельное окно.

Во-первых, чтобы запустить этот пример, убедитесь, что вы создали приложение C# и правильно зарегистрировали его в COM. Затем добавьте ссылку из VB6 в файл .tlb, который находится рядом с выходными данными C#. Вы найдете это в каталоге bin/Debug или bin/Release проекта C#.

Следующий код должен быть помещен в модуль. В моем тестовом проекте я использовал модуль под названием «Module1». В этом Модуле следует отметить следующие определения.

WM_APP – используется в качестве пользовательского идентификатора сообщения, не допускающего помех.
GWL_WNDPROC – константа, используемая для SetWindowLong для запроса модификации обработчика окна.
SetWindowLong – функция Win32, которая может изменять специальные атрибуты окон.
br> 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

В тестовой форме я подключил экземпляр тестового объекта 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#?

person meklarian    schedule 24.11.2009
comment
+1. Существует более объективный способ создания подклассов VB6, хотя visualstudiomagazine .com/articles/2009/07/16/ - person MarkJ; 25.11.2009
comment
Я пошел с этим методом только для того, чтобы сделать выборку короче. Однако техника по этой ссылке определенно лучше. - person meklarian; 25.11.2009
comment
Почему я получаю сообщение об ошибке компиляции Недопустимое использование оператора AddressOf? Я использую не модуль, а напрямую Form1. Это разрешено? - person ufo; 12.06.2014
comment
@ufo Прошло много времени с тех пор, как я работал с VB, но я считаю, что вы не можете избежать использования модуля, потому что функция-член в форме не будет иметь надлежащую сигнатуру функции. В таком случае будет неявный дополнительный аргумент (точно так же, как нестатические функции-члены класса имеют неявно объявленное this в C++). - person meklarian; 13.06.2014

Чтобы отправить в VB6, вам нужно использовать вызов API (SendMessage или PostMessage). Для получения в VB6 нужно использовать подклассы (сложно — вот лучший способ, который я знаю).

Рассматривали ли вы вместо этого использование COM Interop? Это гораздо более простой способ общения между VB6 и C#, чем сообщения Windows.

person MarkJ    schedule 29.10.2009

Используйте функцию Windows API PostMessage.

В начале вашего класса:

[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. Когда ваше событие вызывается, вы убедитесь, что текущее окно является текстовым полем, и выберите его, если это так.

person Bob Thompson    schedule 06.05.2011