Как я могу отправлять / получать сообщения Windows между VB6 и C #? - PullRequest
4 голосов
/ 29 октября 2009

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

Ответы [ 3 ]

5 голосов
/ 24 ноября 2009

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

SendMessage является предпочтительным способом вызова одной или другой стороны через обработчики сообщений Windows. PostMessage сложно использовать со сложными типами, поскольку временем жизни данных, связанных с сообщением Windows, как в .NET, так и в VB6 трудно управлять, пока сообщение находится в очереди, и завершение сообщения неизвестно, если вы не реализуете какую-либо форму механизма обратного вызова .

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

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 # аналогична. Чтобы получить HWND окна в VB6, используйте свойство .hWnd окна в VB6, которое должно получить сообщение.

Поскольку кажется, что вы используете свой собственный набор идентификаторов сообщений, существуют дополнительные шаги для обработки пользовательских идентификаторов сообщений в VB6. Большинство людей справляются с этим путем создания подкласса окна формы и использования процедуры подкласса для фильтрации этих сообщений. Я включил пример кода для демонстрации C # для VB6, так как обработка пользовательских сообщений сложнее в VB6.

Вот исходный код для пары тестовых программ, библиотеки C # и проекта форм VB6. Библиотека C # должна быть настроена на «Регистрация для взаимодействия 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, которая может изменять специальные атрибуты на окнах.
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 # в качестве члена формы. Форма включает в себя кнопку с идентификатором «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: Как отправить структуру из C # в VB6 и из VB6 в C #?

3 голосов
/ 29 октября 2009

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

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

1 голос
/ 06 мая 2011

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

...