2009-10-29 4 views
3

Я знаю, что могу получать сообщения с кодом ниже в C#, как мне отправить на vb6 и получить в vb6 и отправить с vb6?Как отправлять/получать сообщения Windows между VB6 и C#?

[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

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

SendMessage - предпочтительный способ вызова одной или другой с помощью обработчиков Windows Message. 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.

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

REF: How do I send a struct from C# to VB6, and from VB6 to C#?

+1

+1. Существует более «объективный» способ сделать подкласс VB6, хотя http://visualstudiomagazine.com/articles/2009/07/16/subclassing-the-xp-way.aspx – MarkJ

+0

Я пошел с этим методом, чтобы сохранить образец короче. Тем не менее, техника на этой линии определенно выше. – meklarian

+0

Отличная статья! +1 – used2could

3

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

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

+0

+1 COM и VB похожи на горох и морковь. – kenny

1

Использование функции API PostMessage для Windows.

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

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

Смежные вопросы